小编科普几种按键消抖威廉希尔官方网站 的设计方案

电子说

1.3w人已加入

描述

在数字威廉希尔官方网站 中,开关用于用于产生高、低电平,按键用于产生单次脉冲。由于开关和按键为机械部件,每次按下或者释放时,由于簧片的弹性会产生短暂的抖动,然后才能稳定接通或者断开。

抖动现象会导致按键威廉希尔官方网站 的输出产生毛刺,如图1所示,从而可能导致系统产生误动作。

开关和按键的抖动时间一般在20ms以内。为了防止因按键抖动引起的系统误动作,必须对按键威廉希尔官方网站 进行消抖,只在按键闭合或者断开稳定后才允许输出。

数字威廉希尔官方网站

图1 按键抖动现象

按键消抖有软件消抖和硬件消抖两类方法。软件消抖是在嵌入式系统中,检测到按键按下时,应用软件延时20ms后再次检测按键的状态,如果两次状态相同,则确认按键已经按下。

这种处理方式虽然简单,但是会浪费CPU资源。

硬件消抖有多种方法。第一种方法是应用施密特威廉希尔官方网站 的回差特性配合积分威廉希尔官方网站 实现按键消抖,应用威廉希尔官方网站 如图2所示。

数字威廉希尔官方网站

图2 应用积分威廉希尔官方网站 实现按键消抖

第二种方法是应用锁存器的保持功能实现开关消抖,应用威廉希尔官方网站 如图3所示。

数字威廉希尔官方网站

图3 应用锁存器实现开关消抖

除了上述两种按键消抖方法外,在基于FPGA的数字系统设计中,也可以应用状态机设计按键消抖威廉希尔官方网站 ,在FPGA内部实现。

基于状态机设计按键消抖威廉希尔官方网站 时,需要将按键的一次动作分解为:按下前、按下时、稳定期和释放时4个状态,如图1中所示,分别用KEY_IDLE、KEY_PRESSED、KEY_ACTIVE和KEY_RELEASE表示。

设按键输入用key_in表示,低电平有效,设计消抖时间为20ms,则按键消抖状态机的状态转换关系如图4所示。

数字威廉希尔官方网站

图4 按键消抖状态机

根据上述状态转换关系,描述按键消抖模块的Verilog HDL代码参考如下:

 

module KEY_debounce #(parameter DEBOUNCE_TIME = 1000_000 )( 
  // 50MHz时钟时,对应消抖时间为20ms
input clk_50,                         // 50MHz时钟,周期为20ns
      input rst_n,                          // 复位信号
      input key_in,                         // 按键输入
      output reg key_out  // 消抖后输出
      );
      // 内部状态定义,循环编码方式
      localparam KEY_IDLE    = 2'b00,     // 按下前
                   KEY_PRESSED = 2'b01,    // 按下时
                   KEY_ACTIVE  = 2'b11,    // 稳定期
                   KEY_RELEASE = 2'b10;    // 释放时
      // 内部变量定义          
      reg [19:0] debounce_cnt;                // 消抖计数变量
      reg [1:0]  current_state,next_state;  // 现态和次态
      reg [0:1]  keytmp;                        // 同步寄存器
      // 内部线网定义 
      wire cnt_en,cnt_end;                     // 计数允许和停止计数标志
      wire cnt_flag;                            // 消抖计数标志
      wire release_flag;                       // 按键释放标志
      // 允许消抖计数逻辑:按键按下时或者释放时,cnt_en有效。
assign cnt_en = current_state == KEY_PRESSED 
|| current_state == KEY_RELEASE;
      // 停止计数标志:cnt_en有效并且debounce_cnt达到最大值,则cnt_end有效。
       assign cnt_end = cnt_en && ( debounce_cnt == DEBOUNCE_TIME - 1 );
      // 正在计数标志:cnt_en有效并且debounce_cnt未达到最大值,则cnt_flag有效。
      assign cnt_flag = cnt_en && ( debounce_cnt < DEBOUNCE_TIME );
      // 按键已释放标志: cnt_end有效,并且keytmp[1]为高电平,则release_flag有效。
      assign release_flag = cnt_end && keytmp[1];
      // 按键输入两级同步寄存过程,以消除亚稳态。
      always @(posedge clk_50 or negedge rst_n)
         if ( !rst_n ) 
            keytmp <= 2'b00;                         // 清零
         else 
            keytmp[0:1] <= {key_in,keytmp[0]};   // 右移  
      // 时序逻辑过程,描述状态转换
      always @(posedge clk_50 or negedge rst_n) 
         if ( !rst_n )
             current_state <= KEY_IDLE;
         else 
             current_state <= next_state;
      // 组合逻辑过程,定义次态 
      always @(*)  begin
         case ( current_state )
             KEY_IDLE: if ( !keytmp[1] ) // 按键按下时,进入KEY_PRESSED
                            next_state = KEY_PRESSED;
                          else                // 否则,保持KEY_IDLE
                            next_state = current_state; 
             KEY_PRESSED: if ( cnt_end && !keytmp[1] ) 
// 消抖时间到且keytmp[1]为0,确认按下有效
                                 next_state = KEY_ACTIVE;
                             else if ( cnt_flag && keytmp[1] ) 
// 正在计数,但keytmp[1]为1,则为抖动
                                       next_state = KEY_IDLE;
else  // 否则状态保持
                                       next_state = current_state; 
             KEY_ACTIVE: if ( keytmp[1] ) // keytmp[1]跳变为1则进入释放状态
                               next_state = KEY_RELEASE;
                            else 
                               next_state = current_state; // 否则状态保持
             KEY_RELEASE: if ( release_flag ) // 按键已释放,返回
                                next_state = KEY_IDLE;  
                             else if ( cnt_flag && !keytmp[1] )
                                      // 正在计数,但keytmp[1]为0,则为抖动
                                      next_state = KEY_ACTIVE;  
                                   else  // 否则状态保持
                                      next_state = current_state;
                 default:  next_state = KEY_IDLE;
         endcase
      end
      // 时序逻辑过程,消抖计时
      always @( posedge clk_50 or negedge rst_n ) 
         if ( !rst_n )
             debounce_cnt <= 20'b0;
         else if ( cnt_en )       // 计数允许信号有效
                  if ( cnt_end )    // 消抖时间到
                     debounce_cnt <= 20'b0;
                  else             // 消抖时间未到
                     debounce_cnt <= debounce_cnt + 1'b1;
                else                // 计数允许信号无效
                   debounce_cnt <= 20'b0;
// 时序逻辑过程,按键消抖后输出
      always @( posedge clk_50 or negedge rst_n ) 
         if ( !rst_n )
             key_out <= 1'b1;   
         else 
             case ( current_state )
                KEY_IDLE   :     key_out <= 1'b1;
KEY_PRESSED:   key_out <= 1'b1;
KEY_ACTIVE :    key_out <= 1'b0; 
KEY_RELEASE:   key_out <= 1'b0;
default:  key_out <= 1'b1;
             endcase
endmodule

 

对上述代码进行仿真验证时,需要建立testbench文件,应用系统函数$random产生随机数,以模拟不规则的抖动脉冲间隔。

应用系统函数$random产生随机整数的语法格式为

 

num = $random%b
其中b为十进制整数,num为-(b-1) ~ (b-1)范围内的随机整数。 应用系统函数$random产生随机正整数的语法格式为
num ={$random}%b
其中b为十进制整数,num为0 ~ (b-1)范围内的随机整数。 测试按键消抖模块功能的testbench代码参考如下:
`timescale 1ns/1ps
module KEY_debounce_vlg_tst();
     reg  clk;
     reg  rst_n;
     reg  key_in;
     wire key_out;
     // 模块参数重定义,减少计数容量,以缩短仿真时间
     defparam KEY_debounce.DEBOUNCE_TIME = 50000;
// 内部变量定义
     reg [15:0] rand_num;
     // 仿真参数定义
     parameter RESET_TIME = 2, STEP = 5;
     // 按键消抖模块例化 
     KEY_debounce i1
        ( .clk      (clk),
          .rst_n    (rst_n),
          .key_in   (key_in),
          .key_out (key_out));
     // 设置复位信号波形 
   initial begin
        rst_n = 1;
        #1;
        rst_n = 0;
        #(STEP * RESET_TIME);
        rst_n = 1;
     end
     // 设置按键输入 
     initial begin
        #1; key_in = 1;                  //按下前
        #(STEP * 10); press_key;       // 第1次按键过程
        #10_000;       press_key;       // 第2次按键过程
     end
     // 设置时钟信号  
     initial clk_50 = 0;
     always  #(STEP/2) clk_50 = ~clk_50;
     // 监测任务
         initial                             
        $monitor($time,"clk_50=%b rst_n=%b key_in=%b key_out=%b",
clk_50,rst_n,key_in,key_out); 
     // 按键任务定义 
     task press_key;
        begin
          repeat (20) begin // 模拟前沿抖动过程
            rand_num = {$random}%5000;
            #rand_num key_in = ~key_in;
            end
          key_in = 0;
          #300_000;
          repeat (20) begin  // 模拟后沿抖动过程
            rand_num = {$random}%5000;
            #rand_num key_in = ~key_in;
            end
          key_in = 1;
          #300_000;
       end
     endtask
 endmodule
 

 

上述代码中使用了defparam语句用于对KEY_debounce模块中的DEBOUNCE_TIME参数进行重定义,在确保功能验证的前提下缩短消抖时间。启动modelsim进行仿真,结果如图5所示。

数字威廉希尔官方网站

图5 按键消抖模块仿真波形

从波形图中可以看出,消抖威廉希尔官方网站 对按键按下和释放产生的4次抖动都能实现有效消抖,因此验证基于状态机设计的按键消抖模块功能正确。






审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分