按键消抖
1.产生原因
根据机械按键的构造和原理,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。所以在按键闭合及断开的瞬间均伴随有一连串的抖动。
2.消抖方法
分为硬件和软件消抖。
由于硬件消抖一般会增加器件,不方便且对电路板的制作有一定影响。这里采取软件消抖的方法。
检测出按键闭合后执行一个延时程序,根据抖动的时间为 5ms~10ms,我们产生一个 20ms 的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。
3.软件消抖
- 设计模块
要计数过滤掉按键抖动的时间,所以计数器是必不可少的,那么需要时钟,还需要复位信号,以及按键信号。
- 时序图模拟
抖动的时间一般小于10ms,当有20ms 的时间内都没有抖动就说明按键已经处于稳定状态了。所以可以设计一个计数器,计数20ms,这期间没有抖动则此时是什么电平就是什么电平。那么我们需要找到一段20ms没有抖动的区间。当系统检测到按键为低电平时 , 计数器就计数,当检测到按键为高电平时计数器就清零。
上图注意到按键按下的时间较长时,是让cnt继续保持计数最大值,这样可以避免产生多个输出脉冲。
- 代码
module key_filter
#(
parameter CNT_MAX = 20'd999999
)
(
input wire sys_clk,
input wire sys_rst_n,
input wire key_in,
output reg key_out
);
reg [19:0] cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 0 || key_in == 1'b1)
cnt <= 20'd0;
else if (cnt == CNT_MAX)
cnt <= CNT_MAX;
else if (key_in == 1'b0)
cnt <= cnt + 1;
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 0)
key_out <= 1'b0;
else if (cnt == CNT_MAX - 1)
key_out <= 1'b1;
else
key_out <= 1'b0;
endmodule
- 仿真代码
`timescale 1ns/1ns
module tb_key_filter();
reg sys_clk, sys_rst_n, key_in;
wire key_out;
reg [7:0] tb_cnt; //利用tb_cnt来模拟按键抖动计数器
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
key_in <= 1'b1;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
//tb_cnt:按键过程计数器,通过该计数器的计数时间来模拟按键的抖动过程
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 0 )
tb_cnt <= 8'd0;
else if (tb_cnt == 8'd249)
tb_cnt <= 8'd0;
else
tb_cnt <= tb_cnt + 1;
//key_in:产生输入随机数,模拟按键的输入情况
always@(posedge sys_clk or negedge sys_rst_n)
if (sys_rst_n == 0 )
key_in <= 1'd1;
else if ((tb_cnt >= 8'd19 && tb_cnt <= 8'd49) ||
(tb_cnt >= 8'd169 && tb_cnt <= 8'd199))
//在该计数区间内产生非负随机数 0、1 来模拟 10ms 的前抖动和 10ms 的后抖动
key_in <= ($random) % 2;
else if (tb_cnt >= 8'd49 && tb_cnt <= 8'd149)
key_in <= 1'b0;
else
key_in <= 1'b1;
key_filter
#(
.CNT_MAX(20'd24)
)
key_filter_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_in (key_in),
.key_out (key_out)
);
endmodule
-
RTL视图
-
仿真时序图