独立按键消抖在单片机和FPGA中都是个不可避免的问题,首先,解释一下什么叫做按键抖动,如图,按键在按下和松开的那个瞬间存在大概20ms的机械抖动:
下面就是本篇的第一个重点 —— 什么时候需要按键消抖设计?如果是像复位按键这样,短时间内可以多次触发,就完全不需要设计消抖,但是如果是要设计按下按键使LED状态翻转,或者按下按键计数一次的话,就必须要设计消抖模块,否则就会带来不可预知的错误,因为在按下按键的那个时刻,可能已经触发了少则几次,多则几十次,可见按键消抖的必要性;
那么,既然按键消抖如此重要,如何来进行消抖呢?
1、硬件消抖 —— 0.1uF电容滤波
这个104的电容就是起高频滤波的作用,在按键不是很多的情况下,可以使用这种设计,但是如果要做项目,会增加大量成本,所以接下来我们讲述如何进行软件消抖;
2、软件消抖 —— delay
if(key_in == 0)
{
deley(2000);
if(key_in == 0)
{
//按键按下,执行相应操作
}
}
在单片机中用C语言,可以这样设计按键消抖,同样的思路,在FPGA中,我们依然可以采用这种思想,将按键的这20ms抖动“屏蔽”,但是FPGA没有delay(2000),该如何设计呢?
FPGA中控制延时可以采用计数器,因为工作时钟是已知的50M,所以要延时20_000_000ns(20ms),只需要对计数1_000_000个clk就可以,这样延时问题就解决了,按照之前的思路,设计如下:只需要在检测到key_in变为0,启动定时器,定时器时间到,再次检测key_in是否为0,若为0,表明按键按下稳定,关闭计数器并清零,然后等待按键释放,也就是key_in出现上升沿,再次启动定时器,时间到后检测key_in,若为1,则证明按键已释放,一次完整的按键过程结束。
module keys(ext_clk_25m,ext_rst_n,led,key_left,key_righ,key_upup,key_down,key_entr
);
input ext_clk_25m;
input ext_rst_n;
input key_down,key_entr,key_left,key_righ,key_upup;
output reg [7:0]led;
抖动判断逻辑
这里用4位是为了和采样频率和基准时钟一致
//按键抖动判断逻辑
wire key; //所有的按键相与的结果,用于按键触发判断
reg[3:0]keyr ; //按键值key的缓冲寄存器
assign key =key_down & key_entr&key_left&key_righ&key_upup;
always@(posedge ext_clk_25m or negedge ext_rst_n)
begin
if(!ext_rst_n)
keyr <=4'b1111;
else
keyr <={keyr[2:0],key};
end
wire key_neg =~keyr[2] &keyr [3];//有按键被按下
wire key_pos =keyr[2] &~keyr [3];// 有按键被释放
设置一个计数器定时20ms(这里是40ms)
//定时器计数逻辑,用于对按键的消抖的判断
reg [19:0] cnt;
always@(posedge ext_clk_25m or negedge ext_rst_n)
begin
if(!ext_rst_n)
cnt <=20'd0;
else if(key_pos||key_neg)
cnt <=20'd0;
else if(cnt <20'd999_999)
cnt <= cnt +1'b1;
else
cnt <=20'd0;
end
采集按键值
//定时采取按键值
always@(posedge ext_clk_25m or negedge ext_rst_n)
begin
if(!ext_rst_n)
begin
key_value[0] <=5'b11111;
key_value[1] <=5'b11111;
end
else begin
key_value[1] <=key_value[0];
if(cnt ==20'd999_999)
key_value[0] <={key_left,key_righ,key_upup,key_down,key_entr};
else ;
end
end
wire [4:0]key_press =key_value[1] & ~key_value[0];
控制LED
//LED切换控制
always@(posedge ext_clk_25m or negedge ext_rst_n)
begin
if(!ext_rst_n)
led <=8'hff;
else if(key_press[0])
led[0] <=~led[0];
else if(key_press[1])
led[1] <=~led[1];
else if(key_press[2])
led[2] <=~led[2];
else if(key_press[3])
led[3] <=~led[3];
else if(key_press[4])
led[4] <=~led[4];
else ;
end
endmodule