按键消抖模块的设计与验证
1.章节导读
按键是最为常见的电子元器件之一,在电子设计中应用广泛。在 FPGA 的实验工程中,我们可以使用其作为系统复位信号或者控制信号的外部输入;在日常生活中,遥控器、玩具、计算器等等电子产品都使用按键。目前按键种类繁多,常见的有自锁按键、薄膜按键等等。我们开发板上使用的机械按键也是按键的一种,特点是:接触电阻小,手感好,按键按下或弹起时有―滴答‖清脆声;但由于其构造和原理,在按键闭合及断开的瞬间均伴随有一连串的抖动。本章节中,我们要根据机械按键的构造与原理,设计并实现按键消抖模块。来用modelsim来模拟按键抖动。
2.理论学习
如图 1 所示,我们所使用的按键开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而做的措施就是按键消抖,按键抖动原理图如图 2 所示。
图1 机械按键外观图
图2 机械按键抖动原理图
抖动时间的长短由按键的机械特性决定,一般为 5ms~10ms。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起一次按键被误读多次。为确保控制器对按键的一次闭合仅作一次处理,必须去除按键的抖动。在按键闭合稳定时读取按键的状态,并且必须判别到按键释放稳定后再作处理。消抖是为了避免在按键按下或是抬起时电平剧烈抖动带来的影响。按键的消抖,可用硬件或软件两种方法。本文采取软件的方法去消抖。
软件消抖
如果按键个数较多,常用软件方法去抖,即检测出按键闭合后执行一个延时程序,根据抖动的时间为 5ms~10ms,我们产生一个 20ms 的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。
3.实战演练
3.1 实验目标
利用所学知识,设计并实现一个按键消抖模块,将外部输入的单比特按键信号做消抖处理后输出,输出信号正常可被其他模块调用
3.2 模块框图
因为我们要计数过滤掉按键抖动的时间,所以计数器是必不可少的,所以我们设计的模块一定会用到时序电路,所以时钟 s_clk 和复位 s_rst_n 信号一定先加上,而且是输入信号,另外还有一个输入信号就是按键的输入 key_in,我们最终要实现的就是对输入的key_in 信号进行去抖动,输出信号为去抖动后的稳定的按键信号 key_flag。根据上面的分析设计出的 visio 框图如图 3 所示。
3.3 波形图绘制
3.4 代码书写
模块代码:
module Key_filter(
input s_clk ,
input s_rst_n ,
input key_in ,
output reg key_flag
);
//================================================================================|
//****************** Parameter and Internal Signals *********
//================================================================================|
reg [19:0] cnt_20ms ;
parameter CNT_MAX =20'd999_999 ;
//================================================================================|
// ******************** Main Code *****************************
//================================================================================|
//cnt_20ms
always@(posedge s_clk or negedge s_rst_n)begin
if(s_rst_n==1'b0)
cnt_20ms <= 20'd0;
else if(key_in==1'b1)
cnt_20ms <= 20'd0;
else if(cnt_20ms==CNT_MAX&&key_in==1'b0)
cnt_20ms <=cnt_20ms;
else
cnt_20ms <=cnt_20ms+1'b1;
end
//key_flag
always@(posedge s_clk or negedge s_rst_n)begin
if(s_rst_n==1'b0)
key_flag <= 1'b0;
else if(cnt_20ms==CNT_MAX-1'b1)
key_flag <= 1'b1;
else
key_flag <= 1'b0;
end
endmodule
测试代码:
`timescale 1ns/1ps
module tb_Key_filter();
reg s_clk;
reg s_rst_n;
reg key_in ;
wire key_flag;
//输入一个按键计数
reg [21:0] tb_cnt ;
defparam Key_filter_inst.CNT_MAX='d24;
parameter CNT_1MS = 20'd19 ,
CNT_11MS = 21'd69 ,
CNT_41MS = 22'd149 ,
CNT_51MS = 22'd199 ,
CNT_60MS = 22'd249 ;
//初始化输入信号
initial begin
s_clk = 1'b1;
s_rst_n <= 1'b0;
key_in <= 1'b0;
#20
s_rst_n <= 1'b1;
end
always #10 s_clk = ~s_clk;
//tb_cnt
always@(posedge s_clk or negedge s_rst_n)begin
if(s_rst_n==1'b0)
tb_cnt <= 22'd0;
else if(tb_cnt==CNT_60MS)
tb_cnt<=22'd0;
else
tb_cnt<=tb_cnt+1'b1;
end
//key_in
always@(posedge s_clk or negedge s_rst_n)begin
if(s_rst_n==1'b0)
key_in<= 1'b0;
else if(tb_cnt>=CNT_1MS&&tb_cnt<=CNT_11MS||tb_cnt>=CNT_41MS&&tb_cnt<=CNT_51MS)
key_in<={$random} % 2;
else if(tb_cnt>=CNT_11MS&&tb_cnt<=CNT_41MS)
key_in<= 1'b0;
else
key_in<= 1'b1;
end
Key_filter Key_filter_inst(
.s_clk (s_clk ),
.s_rst_n (s_rst_n ),
.key_in (key_in ),
.key_flag (key_flag )
);
endmodule
仿真效果: