FPGA之按键消抖检测

2020-02-27 21:06发布

实现功能:按下一个按键控制led翻转,也就是按一下亮,再按一下灭。
         特权同学视频里的讲解是用了三个按键分别来控制三个led灯翻转,为了简化更容易理解我用的是一个按键控制一位led.然而问题简化后还是然我理解了一上午加一下午.才终于可以无误的检测按键.其实有时候就是个自己根本没有注意的问题而导致的问题。
         在特权同学的视频里面用的是边沿检测法进行消抖。总体思路就是进行一个下降沿的检测及证明有按键按下。而在按下按键的前10ms可能会检测到多次下降沿,所以有可能多次触发,当然松开按键的时候也有大约10ms的抖动时间,又会造成误触发。所以既要想办法滤除掉抖动中出现的误触发。

下面来根据程序进行分析:
module KEY(key,led,clk);

input clk,key;
//
输入信号:50mhz时钟,按键输入


output led;
//
输出led



         reg key_to;

always@(posedge clk)


begin


key_to<=key;


end




reg key_tt;


always@(posedge clk)


key_tt<=key_to;
//
数值滞后于key_to一个时钟周期

wire key_flag=(key_tt&&(~key_to));

红 {MOD}部分代码的作用就是检测一个下降沿并保持一个时钟周期,原理如下:


Key_to:1
1
1
1
1
0
0
0
0
0
(这是每个时钟周期key_to检测到得按键值)

~key_to
0
0
0
0
0
1
1
1
1
1



Key_tt:
1
1
1
1
1
0
0
0
0

0(key_tt
滞后于key_to一个时钟周期)

Key_flag

1

         及当按键有一下降沿后key_flag则会置一
         reg [19:0]cnt;

always@(posedge clk)


begin


if(key_flag)cnt<=20'b0;


else cnt<=cnt+1'b1;


end




reg key_20ms;


always@(posedge clk)


begin


if(cnt==20'h7ffff)key_20ms=key;
//
计时大约20mskey_20ms得到此时按键值

end




reg key_20ms2;


always@(posedge clk)


begin


key_20ms2<=key_20ms;


end




reg d1;


wire key_sta=(key_20ms2&&(~key_20ms));

这段红 {MOD}的代码的作用就是消抖了,分析一下看它是如何起到消抖作用的:如果在key_flag不置一正常的情况下计时器计够20ms时间key_20ms就会锁定一次按键值。而在按键按下期间有抖动出现这时候key_flag就会置一,计时也会清零,所以在这抖动期间计时器会不断的清零即计时器不会计到20ms,所以key_20ms就不会锁定抖动时的按键值,而锁定的还是都抖动前的高电平。人按下按键按键时间一般都大于100ms。除去抖动的10ms时间,应该最少剩余90ms的低电平时间,这时候不存在抖动,计时器可以几斗20mskey_20ms锁定了这个低电平,和key_flag的检测方法一样,在key_20ms有一个下降沿时,~key_20ms会和key_20ms2相与得到key_sta置一,根据这个就可以判断按键按下,在下面的代码中就可以进行led的翻转。松开的时候除去抖动key_20ms是从01上升沿变化的key_sta不会置一误触发。

always@(posedge clk)


begin


if(key_sta)d1=~d1;


end




assign led=d1;

endmodule
这个代码我昨天都写出来了,但是测试的时候经常在松开的时候进行误触发……让我不得其解,琢磨了今天一上午又修改了延时等等都不起作用。我就想算了,可能是按键质量不好抖动时间过长……接下来我看看这个代码生成的rtl图吧,多了解了解内部结构。就在这时我发现一个问题,如图中红 {MOD}标示的原来的值

这样根本就记不到20ms。我又看了程序,终于发现了一个细节问题:我是初学者以前没怎么注意过
if(cnt==20'h7ffff)key_20ms=key;这句语句中我把20写成5了,这才明白,这里的数据位宽表示的都是2进制的……啊,一上午啊就这个小问题导致的不稳定……
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。