原创 【博客大赛】小小菜鸟学FPGA——(5)按键消抖

2013-9-21 10:04 2327 10 12 分类: FPGA/CPLD 文集: FPGA

按键消抖,啥意思?就是按键消抖呗!

好吧,我一直在思考一个问题,就是关于这个系列的博文讲的深度应该是什么样的,就比如说,按键消抖问题,有人知道,有人不知道,我该不该说具体的问题,然后再说如何解决……好吧,我能明白初学者的心,我从头讲,详细的讲吧。

所谓按键消抖,原因是按键会发生抖动,如下图左侧图片:

按键消抖.jpg

当你按下按键的时候,我们理想的是从高电平到低电平,没有任何延迟,但是实际是,电流流过去是需要时间,电流稳定是需要时间,当然这些时间都很少,就是大家在图中实际波形看到的抖动波形。如果,我们的单片机进行检测的时候,我们没有消抖措施,那么采取的信号,谁也无法得知是什么,可能是高电平,也可能是低电平。这就是按键消抖的必要性。

好吧,扯了这么多,我们来讲讲关于按键消抖的方法吧,说白了,消抖,就是让单片机别检测那个抖动的波形,去检测稳定的波形,这样就能得到稳定的结果。那怎么能得到那个稳定的波形呢?有人说,用T触发器,对滴,如图片中右侧部分,去触发器确实可以消抖,但是随之而来的硬件成本也在增加,有人说,当我检测到按下的信号后,延时一段时间,然后再测是不是按下去了,如果是按下去的信号,那么说明确实是按下去了,否则就是没按下去或者发生其他的意外产生的毛刺。这种方法,我想不出个什么词来形容,只能说在某种范围内是可以实现的,而且现在的按键消抖,还真是这么回事。好吧,我们用伪代码实现一下这个逻辑关系:

If(True ==KeyPressed())

{

         Delay();

         If(True ==KeyPressed())

         {

         //按键确实被按下了

         //led翻转

}

else

{

         //按键没有被按下

}

}

else

{

}

以上伪代码就是我们刚才是的按键去抖,但是有个问题啊,Delay()这个函数,延时为多长时间合适呢?一般,我们延时50ms,我是说一般哈,不是决定具体情况具体来定。

好了,我们延时思想讲完了,剩下的无非就是检测按键,如果检测到按键按下,那么在按键抬起的时候,将某个Led灯的状态取反。好好理解前面的那句话,必须是检测到按下按键并且检测到松手后才改变Led的状态,当然,你也可以设计成:检测到按键按下后,将Led灯取反,然后等待按键释放,完成一次按键触发。说白了,就是你按一次键,只能改变一次,不可以发生按键不放手,灯的状态不停地取反!这个是设计的时候最容易犯晕的一点。

那么上面的伪代码就可以改成如下的格式:

If(True ==KeyPressed())

{

         Delay();

         If(True == KeyPressed())

         {

                   //按键确实被按下了

                   while(True == KeyPressed());

                   //Led翻转

}

else

{

         //按键没有被按下

}

}

else

{

}

OK,如果用伪代码来表示,就是上面的过程。有一个问题要注意一下,上面的伪代码,是按照我们的串行程序的思维来写的,读这篇笔记的很多人,都是单片机其他的串行程序编写经验吧,上面的伪代码都能理解,自己也都能写出来,可是,我们现在是在FPGA上,这玩意程序是并行的啊,我们需要把这个伪代码,即我们的思维,移植成并行,这就是这篇笔记的目的。

首先,我要做一个延时的程序块,这个块的作用是,按键按下的时候开始计时,当达到50ms后,再检测一次,如果按键仍然为按下状态,则确定按键确实按下了,然后再做以后的事情,现在的主要工作是延时,程序如下:

reg[22:0] mount_reg;     

always@(posedge clk)

begin

         if(key_reg==1'b1)

                   mount_reg<=mount_reg+1'b1;

         else

                   mount_reg <= 0;

end

如果按键按下,就计时,按键抬起就清零,就这么个功能。

下一步,是检测按键按下后,延时,检测,确实按下了,然后实现led翻转。STOP!!!按下了,不是马上翻转,是要等到按键抬起时候,才发生翻转!!!代码如下:

reg key_reg_o;

always@(posedge clk or posedge rst)

         if(rst)

         begin

                   key_reg_o <= 1'b0;

                   led_reg <= 1'b0;

         end

         else if(mount_reg == 5_000_000)

         begin

                   if(key_reg==1)

                            key_reg_o <= 1;

         end

         else if(key_reg_o & ~key_reg)

         begin

                   led_reg <= ~led_reg;

                   key_reg_o <= 1'h0;

         end

这样,我们的按键去抖程序大体的思维,从串行移植到并行,写的差不多了,把边边角角补充一下,写一个管教约束文件,就搞定了。下面是整个程序的代码和管教约束文件,verilog代码:

`timescale 1ns / 1ps

//////////////////////////////////////////////////////////////////////////////////

// Company:

// Engineer:

//

// Create Date:    22:14:14 09/21/2013

// Design Name: 电子匠人

// Module Name:    getkey

// Project Name: getkey

// Target Devices: Nexys3

// Tool versions:

// Description:

//

// Dependencies:

//

// Revision:

// Revision 0.01 - File Created

// Additional Comments:

//

//////////////////////////////////////////////////////////////////////////////////

module getkey(

         clk,rst,

         key,

         led   

    );

 

input key,rst,clk;

output led;

 

reg key_reg;

reg led_reg;

 

always@(posedge clk or posedge rst)

         if(rst)

         begin

                   key_reg <= 1'h0;

         end

         else

         key_reg <= key;

 

reg[22:0] mount_reg;     

always@(posedge clk)

begin

         if(key_reg==1'b1)

                   mount_reg<=mount_reg+1'b1;

         else

                   mount_reg <= 0;

end

 

reg key_reg_o;

always@(posedge clk or posedge rst)

         if(rst)

         begin

                   key_reg_o <= 1'b0;

                   led_reg <= 1'b0;

         end

         else if(mount_reg == 5_000_000)

         begin

                   if(key_reg==1)

                            key_reg_o <= 1;

         end

         else if(key_reg_o & ~key_reg)

         begin

                   led_reg <= ~led_reg;

                   key_reg_o <= 1'h0;

         end

 

assign led = led_reg;

 

endmodule

 

以下是管脚约束文件:

NET "key" LOC = C4;

NET "rst" LOC = B8;

NET "led" LOC = T11;

NET "clk" LOC = V10;

文章评论2条评论)

登录后参与讨论

电子匠人 2014-10-22 19:34

谢谢 过奖了

用户1724914 2014-10-21 23:32

解释很详细,赞!
相关推荐阅读
电子匠人 2017-07-13 23:40
夏夜随想
今天一个小朋友来找我扯蛋,说工资很低但是自己一定会很努力。看得出他确实很踏实,而且很坚决的说,自己定了个小目标,明年年初调薪工资能上8k/月。我一直看着他,听他讲完后,跟他说:“你的目标不够好,改一改...
电子匠人 2016-03-11 11:21
谁都有被骂成狗的日子
昨天下午,慧子**打电话给我,说她领导又骂了她,电话里的慧子**一肚子委屈——这已经不是第一次了,但凡那位领导好好说几句暖人心的话,慧子**也是绝对会屁颠屁颠的去干活。 我忽然想起刚毕业的...
电子匠人 2014-11-24 17:52
【博客大赛】年末侃大山——说说技术
今天新项目过来个领导,跟我聊了一会,他问我的一个问题是:“你擅长哪方面的技术?”这下把我给问愣了,我说,我哪样也不擅长。 和几个朋友一起准备创业,现在还在筹备阶段,经常会接触到一些人问这样的问...
电子匠人 2014-11-20 21:56
【博客大赛】年末侃大山——说说做事
在我还没有毕业的时候,一个师兄跟我说,第一份工作一定要去大公司,即使钱少,当时我很不理解,问他原因,他说,避免你成为一个山寨工程师。 虽然他告诉了我原因,但是在很长的一段时间内,我还是没理解这句话的...
电子匠人 2014-11-12 23:13
【博客大赛】年末侃大山——说说梦想
     前几天,某电商的移动客户端首页有这么一句话:“梦想是要有的,万一实现了呢?” 读小学的时候我的梦想是当一个超人,读初中的时候我的梦想是没有考试,读高中的时候梦想是隔壁班的姑娘的花裙子...
电子匠人 2014-11-08 23:31
【博客大赛】年末侃大山——前言
一眨眼就到了年末,上半年忙掉了腚,下半年忙掉了脑子,去年还在EDN写了几个专栏,今年却没怎么做贡献,想来真是惭愧。 昨晚看邮箱,看到今年最后一季的主题竟然是随便侃,像我这么有(ai)能(chu...
我要评论
2
10
关闭 站长推荐上一条 /2 下一条