按键消抖
按键消抖主要针对的是机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了保证系统能正确识别按键的开关,就必须对按键的抖动进行处理,这就是按键消抖。
开始时的回弹时间和结束时的回弹时间一般为5~10ms,稳定时间一般为20ms,这里采用一个计数器,计数最大时间的为20ms,系统的晶振为50Mhz(换算成时间为20ns),20ms/20ns=1000000,所以计数器计数最大值为999999(从0开始计数)。当是系统检测到按键为低电平时,计数器开始计数,当系统检测到高电平时,对计数器清零。当计数到最大值时,让计数器保持值不变
然后再定义一个标志信号对按键消抖的稳定状态进行采集。当是计数器计数到最大值时,说明按键已经处于稳定状态。
RTL代码
module key_shake
#(
parameter CNT_MAX = 20'd999_999//计数最大值,20ms
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_in ,
output reg key_flag
);
reg [19:0] cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 20'd0;
else if(key_in == 1'b1)
cnt <= 20'd0;
else if(cnt == CNT_MAX)
cnt <= CNT_MAX;
else
cnt <= cnt + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
key_flag <= 1'b0;
else if(cnt == CNT_MAX)
key_flag <= 1'b1;
else
key_flag <= 1'b0;
endmodule
testbench
`timescale 1ns/1ns
module tb_key_shake();
reg sys_clk;
reg sys_rst_n;
reg key_in;
reg [7:0] tb_cnt;
wire key_flag;
initial
begin
sys_clk = 1'b0;
sys_rst_n <= 1'b0;
key_in <= 1'b1;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
//把按键消抖过程压缩至250个系统周期
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tb_cnt <= 8'd0;
else if(tb_cnt == 8'd249)
tb_cnt <= 8'd0;
else
tb_cnt <= tb_cnt + 1'b1;
//模拟按键抖动
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tb_cnt <= 8'd0;
else if(tb_cnt <= 8'd24 || tb_cnt >= 8'd224)
key_in <= 1'b1;
else if((tb_cnt <= 8'd74 && tb_cnt > 8'd24)||(tb_cnt <= 8'd224 && tb_cnt >= 8'd174))
key_in <= {$random}%2;
else
key_in <= 1'b0;
key_shake
#(
.CNT_MAX(70)
)
key_shake_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_in (key_in),
.key_flag (key_flag)
);
endmodule
仿真结果把按键按下时长缩短到250个系统时钟周期。定义一个计数器tb_cnt,计数最大值为249,然后模拟按键被按下的过程。过程如下图所示
modelsim仿真结果
由仿真结果得,每一次模拟按键的稳定状态都被标志信号key_flag采集到。
ps:(部分图片和学习教程由野火提供)