按键消抖key_filter
模块目的
通常按键开关往往为机械弹性开关,当机械触电断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。这样的抖动会对我们的按键操作产生一些干扰,比如:有时候按下了一次按键,但是会发生很多次的功能的变化,这就是因为抖动的存在。
在机械按键的触点闭合和断开时,都会产生抖动,为了保证系统能正确识别按键的开关,就必须对按键的抖动进行处理。单片机如果在触点抖动期间检测按键的通断状态,则可能导致判断出错,即按键一次按下或释放被错误地认为是多次操作,从而引起误处理。因此,为了确保单片机对一次按键动作只作—次响应,就必须考虑如何消除按键抖动的影响。
实验原理
按键稳定闭合时间长短是由操作人员决定的,通常都会在 100ms 以上,刻意快速按的话能达到 40-50ms 左右,很难再低了。抖动时间是由按键的机械特性决定的,一般都会在 10ms以内,为了确保程序对按键的一次闭合或者一次断开只响应一次,必须进行按键的消抖处理。当检测到按键状态变化时,不是立即去响应动作,而是先等待闭合或断开稳定后再进行处理。按键消抖可分为硬件消抖(通过硬件添加电容等元件来减少抖动)和软件消抖。
实验框图与波形图
在输入信号中,用上下抖动来模拟前后抖动大概在10ms以内,稳定状态随意设置。值得注意的是!其中计数器的设置:由于前后抖动加起来最大为20ms,所以只要按键时间大于20ms就可以判断为一定被按下,设置计数器为20ms,实验采用的时钟为50Mhz,一个时钟周期为20ns,1ms=1000000ns,所以计数器最大值M应设置为999_999.
其中cnt_20ms规则为:当key_in为高电平时归零,当key_in为低电平时,每一个时钟周期计数一次,当计数到最大值时一直保持CNT_MAX,直到key_in的高电平到来。
其中对于输出key_flag给出了两种方案
- 当cnt_20ms计数到最大值拉高电平,直到cnt_20ms为0拉低电平。
- 当cnt_20ms计数到最大值-1时拉高电平,不过只拉高一个时钟周期,此时key_flag信号将是一个脉冲信号,更适合后续模块利用。
实验代码
针对实验参数,应该单独给CNT_MAX用参数赋值,方便后续代码调用和仿真
module key_filter
#(
parameter CNT_MAX=20'd999_999
)
(
input wire sys_clk,
input wire sys_rst_n,
input wire key_in,
output reg key_flag
);
reg [19:0] cnt_20ms;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt_20ms<=20'd0;
else if(key_in==1'b1)
cnt_20ms<=20'd0;
else if(cnt_20ms==CNT_MAX)
cnt_20ms<=cnt_20ms;
else
cnt_20ms<=cnt_20ms+20'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
key_flag<=1'd0;
else if(cnt_20ms==CNT_MAX-20'd1)
key_flag<=1'b1;
else key_flag<=1'b0;
endmodule
仿真代码
这里引入新的变量tb_cnt模拟按键,模拟按下开关后抖动的时间即5ms=250ns,即计数250次,8位宽
`timescale 1ns/1ns
module tb_key_filter();
reg sys_clk;
reg sys_rst_n;
reg key_in;
reg [7:0] tb_cnt;//模拟按下开关后抖动的时间即5ms=250ns,即计数250次,8位宽
wire key_flag;
initial
begin
sys_clk=1'b1;
sys_rst_n<=1'b0;
#20
sys_rst_n<=1'b1;
end
always #10 sys_clk=~sys_clk;//50Mhz
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
tb_cnt<=8'b0;
else if(tb_cnt==8'd249)
tb_cnt<=8'b0;
else tb_cnt<=tb_cnt+8'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
key_in<=1'b1;
else if(((tb_cnt>=8'd19)&&(tb_cnt<=8'd49))
||((tb_cnt>=8'd149)&&(tb_cnt<=8'd199)))
key_in<={$random}%2;//模拟抖动
else if((tb_cnt<19)||(tb_cnt>199))
key_in<=1'b1;
else key_in<=1'b0;
key_filter
#(
.CNT_MAX(20'd24)
)
key_filter_inst
(
. sys_clk(sys_clk),
. sys_rst_n(sys_rst_n),
. key_in(key_in),
. key_flag(key_flag)
);
endmodule