按键消抖,啥意思?就是按键消抖呗!
好吧,我一直在思考一个问题,就是关于这个系列的博文讲的深度应该是什么样的,就比如说,按键消抖问题,有人知道,有人不知道,我该不该说具体的问题,然后再说如何解决……好吧,我能明白初学者的心,我从头讲,详细的讲吧。
所谓按键消抖,原因是按键会发生抖动,如下图左侧图片:
当你按下按键的时候,我们理想的是从高电平到低电平,没有任何延迟,但是实际是,电流流过去是需要时间,电流稳定是需要时间,当然这些时间都很少,就是大家在图中实际波形看到的抖动波形。如果,我们的单片机进行检测的时候,我们没有消抖措施,那么采取的信号,谁也无法得知是什么,可能是高电平,也可能是低电平。这就是按键消抖的必要性。
好吧,扯了这么多,我们来讲讲关于按键消抖的方法吧,说白了,消抖,就是让单片机别检测那个抖动的波形,去检测稳定的波形,这样就能得到稳定的结果。那怎么能得到那个稳定的波形呢?有人说,用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;
电子匠人 2014-10-22 19:34
用户1724914 2014-10-21 23:32