• 按键消抖


    按键消抖原因

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

    硬件消抖

    按键个数较少时可以使用硬件方法消除抖动。下图所示为使用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脉冲。波形图如下图所示。

  • 相关阅读:
    《人月神话》阅读笔记3
    团队冲刺0110
    团队冲刺0109
    团队冲刺0108
    团队冲刺0107
    团队冲刺0106
    团队冲刺0105
    团队冲刺010304
    团队冲刺0102
    团队冲刺0101
  • 原文地址:https://www.cnblogs.com/lizhiqing/p/12974922.html
Copyright © 2020-2023  润新知