独立按键消抖在单片机和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,则证明按键已释放,一次完整的按键过程结束。
modulekeys(ext_clk_25m,ext_rst_n,led,key_left,key_righ,key_upup,key_down,key_entr ); inputext_clk_25m; inputext_rst_n; inputkey_down,key_entr,key_left,key_righ,key_upup; outputreg [7:0]led;
抖动判断逻辑
这里用4位是为了和采样频率和基准时钟一致
//按键抖动判断逻辑wirekey; //所有的按键相与的结果,用于按键触发判断reg[3:0]keyr ; //按键值key的缓冲寄存器assignkey=key_down&key_entr&key_left&key_righ&key_upup; always(posedgeext_clk_25mornegedgeext_rst_n) beginif(!ext_rst_n) keyr<=4'b1111;elsekeyr<={keyr[2:0],key}; endwirekey_neg=~keyr[2] &keyr [3];//有按键被按下wirekey_pos=keyr[2] &~keyr [3];// 有按键被释放
设置一个计数器定时20ms(这里是40ms)
//定时器计数逻辑,用于对按键的消抖的判断reg [19:0] cnt; always(posedgeext_clk_25mornegedgeext_rst_n) beginif(!ext_rst_n) cnt<=20'd0;elseif(key_pos||key_neg) cnt<=20'd0;elseif(cnt<20'd999_999)cnt<=cnt+1'b1;elsecnt<=20'd0;end
采集按键值
//定时采取按键值always(posedgeext_clk_25mornegedgeext_rst_n) beginif(!ext_rst_n) beginkey_value[0] <=5'b11111;key_value[1] <=5'b11111;endelsebeginkey_value[1] <=key_value[0]; if(cnt==20'd999_999)key_value[0] <={key_left,key_righ,key_upup,key_down,key_entr}; else ; endendwire [4:0]key_press=key_value[1] &~key_value[0];
控制LED
//LED切换控制always(posedgeext_clk_25mornegedgeext_rst_n) beginif(!ext_rst_n) led<=8'hff;elseif(key_press[0]) led[0] <=~led[0]; elseif(key_press[1]) led[1] <=~led[1]; elseif(key_press[2]) led[2] <=~led[2]; elseif(key_press[3]) led[3] <=~led[3]; elseif(key_press[4]) led[4] <=~led[4]; else ; endendmodule