这个实验其实很早就做了,但是由于这段时间自己一直在忙一些其他的事所以没有及时更新。今天抽出个空来更新一下。本次实验是关于按键控制LED亮灭。其中涉及到的内容有计数器、按键消抖以及一些简单的逻辑。
1.按键消抖的原理
按键消抖的方法有很多种,目前我知道的有大概两种,一种是使用移位寄存器打拍,但是只有当移位寄存器一直输出0或者一直输出1的时候,才代表完成了消抖;另一种是使用计数器和锁存键值来实现消抖,这里我介绍后者,因为感觉后者更加稳定可靠一些,虽然代码有些许的繁琐。
采用这个方法,想要实现按键消抖,我们首先需要先编写一个计数器(用于定时,大约20ms),这个计数器有一个特点:在抖动的时候不计时,会自动清零,只有在键值稳定的时候才会计数到20ms。
为了让计数器具有这个特点,我们在设计计数器之前,先对前一时钟和后一时钟的键值进行分别进行缓存,最后将这两个键值异或起来赋值给一个标志位key_n,用于在键值不稳定时控制时钟去清0。
然后我们需要一个检测并锁存键值的latch,当每次计满20ms的时候取一次键值,并与先前的键值进行比较。最后由比较后的结果来控制LED亮灭。
具体原理可以再参考这位博主的博客:基于FPGA的按键消抖_luoai_2666的博客-CSDN博客_fpga按键消抖
2.代码实现
这边也可以改成2个或者多个按键控制led,只要修改相应变量的位宽和变量个数即可,读者可以自行修改。
///*----------------2-按键消抖+按键控制LED亮灭(1个按键控制LED,另一个用于复位)-----------------*/
module key(
input wire clk,//时钟主频50M,计数100 0000次达到20ms
input wire rst_n,
input wire sw_1,
output reg [1:0] led
);
reg key_buffer1;
reg key_buffer2;
//-------judge-------//
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_buffer1<=1'b1;
key_buffer2<=1'b1;
end
else begin
key_buffer1<=sw_1;
key_buffer2<=key_buffer1;
end
end
wire key_n;//用于判断前后两个时钟采样得到的键值是否一样
assign key_n=key_buffer1^key_buffer2;//按位异或,当没有按键按下时,key_n=0
//----------counter---------//
/*只有在按键值不变时才会一直计数至20ms。如果按键值两个时钟前后有所改变(此时也就是按键抖动的时候),那么计数就清零。*/
reg [19:0] cnt; //100 0000转换成bin就是需要20位
wire add_cnt;
wire end_cnt;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt<=20'd0;
end
else if(add_cnt)begin
if(end_cnt)
cnt<=0;
else
cnt <= cnt+1'b1;
end
end
assign add_cnt=1;
assign end_cnt=(key_n||(cnt==100_0000-1));//当按键值变化(抖动或者按键状态变化) 或者 计数到20ms 时,end_cnt变为1。
//----------latch---------//
reg latch;
reg latch_fin;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
latch <= 1'b1;
latch_fin <= 1'b1;
end
else begin
latch_fin <= latch;
if(cnt==100_0000-1)begin
latch <= sw_1;
end
end
end
wire led_ctrl=latch_fin &(~latch);//先前的按键状态latch_fin:11;按下后一按键状态latch:01
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led <=2'b11;
end
else if(led_ctrl) led <= 2'b01;
end
endmodule
运行仿真后效果如下图所示: