按键消抖

按键消抖原因

使用机械弹性开关,当机械触点闭合/断开时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定的接通,在断开时也不会马上断开。而是会在闭合/断开的瞬间伴随一连串的抖动,为避免这种现象带来的问题,需要进行按键消抖。

硬件消抖

按键个数较少时可以使用硬件方法消除抖动。下图所示为使用RS触发器进行硬件消抖,当按键未按下时,输出为0;当按键按下时,输出为1。此时,即使按键因为弹性抖动而产生瞬时断开(抖动从B跳开),只要按键不回到原始状态A,双稳态触发器的状态也不会改变(保持为0),从而不产生抖动的波形。

软件消抖

原理

按键数量较多的情况下,通常采用软件方法进行消抖。在检测到按键闭合后开始执行一个延时程序,根据抖动的时间通常为5~10ms,去产生一个20ms的延时,让前沿抖动消失后再一次检测按键的状态。如果仍保持闭合状态电平,则认定为真正有键按下。

消抖过程中的问题

1、消除机械抖动导致的毛刺影响

由于机械抖动关系,在按键按下和松开过程中会出现按键抖动现象,从而导致计数器技术时间难以确定。

 

解决方法:计数器的开始由最后一次低电平(按键信号)决定。若按键信号key_in在到达产生flag之前置高,则将前一次脉冲认为是抖动,计数器清零,在下一次key_in置低后计数器再开始计数。换言之就是:系统检测到key_in为低电平的时候开始计数,检测到高电平时计数器清零。

2、按键时间过久导致出现多个flag信号的现象

假设使用50MHz的晶振进行计算,计数器计数20ms需要计数999_999个周期,当计数器计满后习惯清零,从而导致flag信号到达999_999后产生1个时钟周期宽度的flag信号,同时计数器归零。当按键时间过久,可能导致计数器存在多个0→999_999→0状态,同时,每次到达999_999后会产生一个按键被按下的flag,从而导致出现多个flag的脉冲。

解决方法:计数器的归零由按键信号决定,当按键信号为高电平(表示按键被松开)时,计数器才从999_999归零。

同时为确保flag信号仅保持一个周期,当计数器为999_998时,产生flag信号。否则flag会一直保持高电平直到计数器归零。

 

按键消抖代码

 1 module key_fileter #(parameter CNT_MAX=20'd999_999)(
 2     input        sys_clk        ,    // 系统时钟50MHz
 3     input        sys_rst_n    ,    // 全局复位
 4     input        key_in        ,    // 按键输入信号
 5     output    reg    key_flag        // 为1时表示消抖后检测到按键被按下,为0表示没有检测到被按下
 6 );
 7     reg    [19:0]    cnt_20ms;
 8     // cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,开始计数
 9     always @(posedge sys_clk or negedge sys_rst_n)
10         if(!sys_rst_n)
11             cnt_20ms <= 20'b0;
12         else if(key_in)
13             cnt_20ms <= 20'b0;
14         else if(cnt_20ms==CNT_MAX && key_in)
15             cnt_20ms <= cnt_20ms;
16         else
17             cnt_20ms <= cnt_20ms + 1'b1;
18     
19     // key_flag:当计数满20ms后产生按键有效标志位,且key_flag在999_999时拉高,维持一个周期的高电平
20     always @(posedge sys_clk or negedge sys_rst_n)
21         if(!sys_rst_n)
22             key_flag <= 1'b0;
23         else if(cnt_20ms== CNT_MAX-1'b1)
24             key_flag <= 1'b1;
25         else
26             key_flag <= 1'b0;
27             
28 endmodule
key_filter

按键消抖testbench

 1 `timescale 1ns/1ns
 2 
 3 module tb_key_filter();
 4     // I/O port
 5     reg            sys_clk;
 6     reg            sys_rst_n;
 7     reg            key_in;
 8     wire        key_flag;
 9     
10     reg    [21:0]    tb_cnt;
11     
12     // 为缩短仿真时间,将参数化的时间值改小,但位宽定参数名的宽度
13     parameter    CNT_1MS = 20'd19;
14     parameter    CNT_11MS = 21'd69;
15     parameter    CNT_41MS = 22'd149;
16     parameter    CNT_51MS = 22'd199;
17     parameter    CNT_60MS = 22'd249;
18                     
19     // 初始化系统时钟、复位信号和输入信号
20     initial
21         begin
22             sys_clk     <= 1'b1;
23             sys_rst_n     <= 1'b0;
24             key_in         <= 1'b0;
25             #20
26             sys_rst_n    <= 1'b1;
27         end
28     
29     // 定义系统时钟,50MHz
30     always #10 sys_clk = ~sys_clk;
31     
32     // tb_cnt: 按键过程计数器,用于模拟按键抖动过程
33     always @(posedge sys_clk or negedge sys_rst_n)
34         if(!sys_rst_n)
35             tb_cnt <= 22'b0;
36         else if(tb_cnt==CNT_60MS)    // 计数器计到60MS是完成一次按键从按下到释放的过程
37             tb_cnt <= 22'b0;
38         else
39             tb_cnt <= tb_cnt + 1'b1;
40             
41     // key_in:产生输入随机数,模拟按键情况
42     always @(posedge sys_clk or negedge sys_rst_n)
43         if(!sys_rst_n)
44             key_in <= 1'b1;            // 按键未按下时的状态为高电平
45         else if((tb_cnt>=CNT_1MS && tb_cnt<=CNT_11MS) || (tb_cnt>=CNT_41MS && tb_cnt<=CNT_51MS))
46             key_in <= {$random} % 2;
47         else if(tb_cnt>=CNT_11MS && tb_cnt<=CNT_41MS)
48             key_in <= 1'b0;
49         else
50             key_in <= 1'b1;
51             
52     //--------------------key_filter_inst------------------------------
53     key_fileter #(
54     .CNT_MAX        (20'd24            )
55     )
56     key_fileter_inst(
57         .sys_clk    (sys_clk        ),
58         .sys_rst_n    (sys_rst_n        ),
59         .key_in        (key_in            ),
60         .key_flag    (key_flag        )
61     );
62     
63 endmodule
tb_key_filter

波形图

时钟频率为50MHz,当key_in保持24个周期的低电平时,产生一个周期的key_flag脉冲。波形图如下图所示。

原文地址:https://www.cnblogs.com/lizhiqing/p/12974922.html