目录
RS触发器
在按键个数较少时可用硬件方法消除键抖动。如图 所示的 RS 触发器为常用的硬件去抖。
图中两个与非门构成一个 RS 触发器。当按键未按下时,输出为 0;
当键按下时,输出为 1。此时即使用按键的机械性能,使按键因弹性抖动而产生瞬时断开(抖动跳开 B),只要按键不返回原始状态 A,双稳态电路的状态不改变,输出保持为 0,不会产生抖动的波形。也就是说,即使 B 点的电压波形是抖动的,但经双稳态电路之后,其输出为正规的矩形波。这一点通过分析RS 触发器的工作过程很容易得到验证
软件消抖
如果按键个数较多,常用软件方法去抖,即检测出按键闭合后执行一个延时程序,根据抖动的时间为 5ms~10ms,我们产生一个 20ms 的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。
抖动时间的长短由按键的机械特性决定,一般为 5ms~10ms。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。
我们添加一个名为 cnt_20ms 用于计数 20ms 时间的计数器,每当系统检测到按键输入信号为低电平时 cnt_20ms 计数器就开始计数,在 cnt_20ms计数器计数期间内,如果再次检测到按键为高电平则说明上次检测到的低电平一定是个抖动,那么我们就将这个计数器清零,这样就最大程度保证延时的20ms时间不会太短. 因为我们不能保证20ms对每一次按键都适用。
另外,为了避免检测按键的结果出现长电平,而不是脉冲如下图
按键会因为低电平的时间太久,会存在多个 20ms 的时间(随便按下按键都是几百ms了,就是图里的 稳定态 其实是几百ms的),cnt_20ms 计数器计数满清零多次,这样就会有多个计数值为 999_999 的情况,从而导致 key_flag 信号产生多次脉冲,这显然是我们不想要的结果。
其根本原因是 cnt_20ms 计数器计数到 999_999 后保持在999_999 的时间太久导致的。 (999999是20ms的clk计数值)
我让这个20ms的计数器计满20ms后不清0,并且停止计数,并且看999_998作为标记位(999_998只出现一次),就可以产生一个周期的有效脉冲了
又发现 cnt_20ms 计数器计数到 999_998 的次数只有一个,而且最接近 999_999,在既保证去抖动时间的前提下使 key_flag 信号只产生一个脉冲信号。最终的波形结果如图 所示。
附上Verilog
module key_filter
#(
parameter MAX_CNT = 20'd99_9999
)
(
//50M系统时钟
input sys_clk,
input sys_rst_n,
input key_in, //按键输入
output reg key_flag //键值判断位
);
reg [19:0] clk_cnt_20ms; //20ms的CLK计数器存储在这里
//每到高电平就清0,低电平就自加
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
clk_cnt_20ms <= 20'b0;
else if(key_in == 1'b1)
clk_cnt_20ms <= 20'b0;
else if((clk_cnt_20ms == MAX_CNT ) && (key_in == 1'b0))//按下的时间持续到20ms,停止自加计数
clk_cnt_20ms <= clk_cnt_20ms;
else
clk_cnt_20ms <= clk_cnt_20ms + 1'b1;
//低电平20ms之后,按键还是低电平,说明真的有按键按下
//MAX_CNT - 1'b1 是为了产生一个时钟周期的有效脉冲
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
key_flag <= 1'b0;
else if(clk_cnt_20ms == (MAX_CNT - 1'b1) )
key_flag <= 1'b1;
else
key_flag <= 1'b0;
endmodule
方法来自于火哥,感谢