打印

FPGA之按键消抖检测

[复制链接]
4286|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wzt19910506|  楼主 | 2012-3-18 14:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
实现功能:按下一个按键控制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));

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


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));

这段红色的代码的作用就是消抖了,分析一下看它是如何起到消抖作用的:如果在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图吧,多了解了解内部结构。就在这时我发现一个问题,如图中红色标示的原来的值

这样根本就记不到20ms。我又看了程序,终于发现了一个细节问题:我是初学者以前没怎么注意过
if(cnt==20'h7ffff)key_20ms=key;这句语句中我把20写成5了,这才明白,这里的数据位宽表示的都是2进制的……啊,一上午啊就这个小问题导致的不稳定……
评分
参与人数 1威望 +2 收起 理由
GoldSunMonkey + 2 我很赞同

相关帖子

沙发
GoldSunMonkey| | 2012-3-18 18:05 | 只看该作者
谢谢分享~

使用特权

评论回复
板凳
viatuzi| | 2012-3-19 10:07 | 只看该作者
大部分的时候,出的问题都是小问题。
但往往小问题是最难debug的。

使用特权

评论回复
地板
GoldSunMonkey| | 2012-3-19 10:56 | 只看该作者
:)是的,这个时候需要耐心

使用特权

评论回复
5
bear0514| | 2012-3-20 18:09 | 只看该作者
很不错,前段时间一直没有明白消振是怎么回事,现在好像明白了

使用特权

评论回复
6
jutyy| | 2012-5-24 20:50 | 只看该作者
key_tt<=key_to;
//数值滞后于key_to一个时钟周期

请问这里为什么滞后一个周期了,always语句是并行运行的吗。。初学者向您指教,谢谢。。

使用特权

评论回复
7
gkb986| | 2012-5-26 22:28 | 只看该作者
这东西用状态机稳定,延迟一个周期做边沿检测的话要看时钟频率是多少啦!

使用特权

评论回复
8
GoldSunMonkey| | 2012-5-28 13:46 | 只看该作者
这东西用状态机稳定,延迟一个周期做边沿检测的话要看时钟频率是多少啦!
gkb986 发表于 2012-5-26 22:28
是的,肯定如此

使用特权

评论回复
9
westmas| | 2012-6-6 15:48 | 只看该作者
process(clk_div100) ---32us
begin
        if rising_edge (clk_div100) then
                r_fin <= dff32(31);
                if(dff32(0)/=fin and dff32(31)=fin) then
                        dff32<=(others=>fin);
                else
                        dff32<=dff32(30 downto 0) & fin;
                end if;
        end if;
end process;
用移位寄存器做滤波,每个时钟将输入信号左移,当发现位寄存器LSB不等于输入信号,且MSB=输入信号(即信号电平改变两次)则认为有毛刺进入滤波器,将寄存器中所有数据都改写为输入值。该模块的功能既是将信号中所有脉宽小于时钟周期*滤波器(移位寄存器)位宽的脉冲滤除。

使用特权

评论回复
10
hackice| | 2012-6-8 13:05 | 只看该作者
感谢楼主分享!拿去研究研究

使用特权

评论回复
11
GoldSunMonkey| | 2012-6-8 17:22 | 只看该作者
:)

使用特权

评论回复
12
davinci_cn| | 2012-6-9 18:12 | 只看该作者
有时候感觉主体精神学到了,容易放松,这时候能多研究下,收货还是蛮多的。

使用特权

评论回复
13
一般首席| | 2013-10-19 20:38 | 只看该作者
学习了:D

使用特权

评论回复
14
GoldSunMonkey| | 2013-10-21 22:34 | 只看该作者
一般首席 发表于 2013-10-19 20:38
学习了

欢迎常来啊

使用特权

评论回复
15
wgj2778| | 2013-10-23 17:32 | 只看该作者
不错不错

使用特权

评论回复
16
nosilence_2007| | 2013-10-24 09:41 | 只看该作者
jutyy 发表于 2012-5-24 20:50
key_tt

同问  为什么会延迟一个时钟

使用特权

评论回复
17
GoldSunMonkey| | 2013-10-24 23:11 | 只看该作者
:lol

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

134

帖子

8

粉丝