打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
按键消抖
实际系统中常用的按键大部分都是轻触式按键,如下图所示。该按键内部由一个弹簧片和两个固定触点组成,当弹簧片被按下,则两个固定触点接通,按键闭合。弹簧片松开,两个触点断开,按键也就断开了。根据这种按键的机械特性,在按键按下时,会先有一段时间的不稳定期,在这期间,两个触点时而接通,时而断开,我们称之为抖动,当按键大约按下20ms后,两个触点才能处于稳定的闭合状态,按键松开时和闭合时情况类似。而我们的FPGA工作在很高的频率,按键接通或断开时任何一点小的抖动都能轻易的捕捉到,如果不加区分的将每一次闭合或断开都当做一次按键事件,那么势必一次按键动作会被FPGA识别为很多次按键操作,从而导致系统工作稳定性下降。

 

 

 轻触按键实物图

 

一次按键动作的大致波形如下图所示:

 

 

      因此,我们所需要做的工作,就是滤除按键按下和释放时各存在的20ms的不稳定波形。做法思路是:检测按键按下---》等待20Ms ----》检测此时按键键值,若为按下值则按下有效,否则按下无效(后面可以检测亦可以不检测,据具体情况而定----》检测到按键松开----》延迟20Ms ----》检测此时的键值,若为按下值则松开无效,否则按键松开)
硬件电路:    
        独立按键属于一种输入设备,其与FPGA连接的IO口被接上了10K的上拉电阻,在按键没有按下时,FPGA会检测到高电平;当按键按下后,FPGA的IO口上则将呈现低电平。因此,按键检测的实质就是读取FPGA的IO上的电平。


独立按键典型电路
 verilog 程序如下所示:
  
/********************************Copyright**************************************                           **----------------------------File information--------------------------** File name  :key_shake.v  ** CreateDate :2015.03** Funtions   : 按键的消抖操作:在复位之后的100us内,不响应按键的操作,在之后有按键按下后,有20ms的延迟,之后输出按键输出.** Operate on :M5C06N3L114C7** Copyright  :All rights reserved[F]. ** Version    :V1.0**---------------------------Modify the file information----------------** Modified by   :** Modified data :        ** Modify Content:V1.1:clk-->clk_100M, 常数声明放到一起,便于修改。*******************************************************************************/  module  key_shake (           clk_100M,           rst_n,                       key_in,            key_out             ); input          clk_100M;            //100Mhz input          rst_n;  input          key_in; output         key_out;  //-------------------------------------- //在复位之后的100us内,不响应按键的操作 localparam    t_100us  = 14'd9999; localparam    t1ms = 17'd99999;       //定时1ms  localparam    t_20ms = 5'd20;      reg    [13:0]   cnt;    reg             key_en;         //复位之后允许按键输入标志 always @(posedge clk_100M or negedge rst_n) begin  if(!rst_n)   begin      cnt <= 0;            key_en <=0;    end  else     begin      if(cnt == t_100us)              begin                   key_en <= 1;                 end       else              begin                    key_en <= 0;                    cnt <= cnt + 1;              end    end  end  //-------------------------------------------------- wire         HtoL_flag;         //下降沿标志 wire         LtoH_flag;         //上升沿标志 reg   [2:0]   key_reg; always @(posedge clk_100M or negedge rst_n) begin  if(!rst_n)   begin      key_reg <= 3'b111;            //默认没按下状态为高,按下之后为低.反之则为3'b000;    end  else     begin      key_reg <= {key_reg[1:0],key_in};      end  end     assign HtoL_flag = key_en?(key_reg[2:1] == 2'b10):0;            //下降沿检测,一个时钟的高电平 assign LtoH_flag = key_en?(key_reg[2:1] == 2'b01):0;               //上升沿检测,一个时钟的高电平  //--------------------------------------------- reg          cnt_en;                 //计数使能标志 reg   [16:0]  cnt2;  always @(posedge clk_100M or negedge rst_n) begin  if(!rst_n)   begin      cnt2 <= 17'd0;    end  else if((cnt_en)&&(cnt2 == t1ms))    begin      cnt2 <= 17'd0;    end    else if(cnt_en)    begin      cnt2 <= cnt2 + 17'd1;    end        else           cnt2 <= 17'd0;      end     reg   [4:0]   cnt3;  always @(posedge clk_100M or negedge rst_n) begin  if(!rst_n)   begin       cnt3 <= 5'd0;    end  else if((cnt_en)&&(cnt2 == t1ms))    begin            if(cnt3 == t_20ms )               cnt3 <= t_20ms;            else         cnt3 <= cnt3 + 1;                                  end    else if(!cnt_en)       cnt3 <= 5'd0;          end    //----------------------------------//按键状态机    reg  [2:0]  i;    reg      key_down;        //按键按下标志    reg      key_up;          //按键释放标志      always @(posedge clk_100M or negedge rst_n)     begin      if(!rst_n)       begin                key_down <= 0;                key_up <= 0;                i <= 0;                cnt_en <= 0;        end      else         begin          case(i)                 'd0:                    begin                             key_down <= 0;                       key_up <= 0;                          if(HtoL_flag) i <= 'd1;         //检测到按下                            else if(LtoH_flag) i <= 'd2;    //检测到释放按键                            else  i  <= 'd0;                     end                    'd1:                      begin                            if(cnt3 == t_20ms )                              begin                                    if(!key_in)                  //检测到按键依然被按下                                   begin                                       key_down <= 1;            //按键按下成功                                       i <= 'd3;                                       cnt_en <= 0;                                      end                                    else                                        begin                                           key_down <= 0;                                          i <= 'd0;                                         cnt_en <= 0;                                                 end                                 end                             else                               cnt_en <= 1;                            end                    'd2:                      begin                            if(cnt3 == t_20ms )                              begin                                    if(key_in)                  //检测到按键被释放                                   begin                                       key_up <= 1;             //按键释放成功                                       i <= 'd3;                                       cnt_en <= 0;                                      end                                    else                                        begin                                           key_up <= 0;                                           i <= 'd0;                                         cnt_en <= 0;                                                 end                                 end                             else                               cnt_en <= 1;                            end                    'd3:                      begin                             key_up <= 0;                               key_down <= 0;                                         i <= 'd0;                                                      end                                      default:i <= 'd0;                    endcase                    end      end         assign      key_out = key_down;         //当按键被按下有效时// assign   key_out = key_up;         //当按键被释放后才有效时endmodule

 

 测试代码如下:     

/********************************Copyright**************************************                           **----------------------------File information--------------------------** File name  :key_testbench.v  ** CreateDate :2015.03** Funtions   :按键消抖的测试文件** Operate on :M5C06N3L114C7** Copyright  :All rights reserved. ** Version    :V1.0**---------------------------Modify the file information----------------** Modified by   :** Modified data :        ** Modify Content:*******************************************************************************/  module  key_testbench;      reg          clk;    reg          rst_n;    reg          key_in;    wire         key_out;      key_shake  key_shake_1(                                     .clk,                                     .rst_n,                                                                          .key_in,                                     .key_out                                         );                                         localparam  tck = 24;localparam  t = 1000/tck;always  #(t/2)  clk = ~clk;task key_in_down; begin     #(3*t)       key_in = 1;     #(3*t)       key_in = 0;          #(8*t)       key_in = 1;     #(8*t)    key_in = 0;     #(13*t)   key_in = 1;     #(13*t)     key_in = 0;  end endtask task key_in_up; begin     #(3*t)        key_in = 0;          #(3*t)        key_in = 1;        #(8*t)        key_in = 0;          #(8*t)        key_in = 1;              #(13*t)      key_in = 0;          #(13*t)      key_in = 1;                     endendtask    initial   begin    clk = 0;        rst_n = 0;        key_in = 1;                #(100*t)  rst_n = 1;                #(100*t);      #(10*t)    key_in_down;      #(100*t);      #(10*t)    key_in_up;              #(8000*t);             #(10*t) repeat(2)     key_in_down;          //按下时抖动           #(640000*t);                             //按下时间           #(10*t) repeat(2)  key_in_up;            //释放时抖动          end    endmodule

 仿真结果:

 1、在复位之后100us之前按下按键时,不响应。

 2、抖动(按下后20ms之内释放)。

 
  3、按下之后检测以及释放之后的检测。
  

 

如果将按键按下有效时刻、按键释放有效时刻和按键所处状态全部表现出来,则代码稍作修改即可:

  

/********************************Copyright**************************************                           **----------------------------File information--------------------------** File name  :key_shake.v  ** CreateDate :2015.03** Funtions   : 按键的消抖操作:在复位之后的100us内,不响应按键的操作,在之后有按键按下后,有20ms的延迟,检测,然后松开时也有20ms的检测,之后输出按键输出.** Operate on :M5C06N3L114C7** Copyright  :All rights reserved[F]. ** Version    :V1.0**---------------------------Modify the file information----------------** Modified by   :** Modified data :        ** Modify Content:V1.1:clk-->clk_100M, 常数声明放到一起,便于修改。*******************************************************************************/  module  key_shake (                     clk,                     rst_n,                                key_in,                     key_down_out,                        key_up_out,                     key_in_out             ); input          clk;            //24Mhz input          rst_n;  input          key_in; output         key_down_out;     //按下输出 output         key_up_out;       //释放输出 output         key_in_out;       //跟随输入输出  //-------------------------------------- //在复位之后的100us内,不响应按键的操作   parameter  t_20ms = 5'd20;         `define      CLK_20M//    `define      CLK_24M//    `define      CLK_50M          `ifdef   CLK_20M       parameter  t_100us  = 12'd1999;         parameter  t1ms = 16'd19999;       //定时1ms     `endif   `ifdef   CLK_20M       parameter  t_100us  = 12'd2399;         parameter  t1ms = 16'd23999;       //定时1ms     `endif    `ifdef   CLK_20M       parameter  t_100us  = 13'd4999;         parameter  t1ms = 16'd49999;       //定时1ms     `endif      reg    [12:0]   cnt;    reg             key_en;         //复位之后允许按键输入标志  always @(posedge clk or negedge rst_n)  begin   if(!rst_n)     begin      cnt <= 0;      key_en <=0;     end  else     begin      if(cnt == t_100us)        begin          key_en <= 1;        end      else        begin          key_en <= 0;           cnt <= cnt + 1;        end    end  end  //-------------------------------------------------- wire          HtoL_flag;         //下降沿标志 wire          LtoH_flag;         //上升沿标志 reg   [2:0]   key_reg; always @(posedge clk or negedge rst_n) begin  if(!rst_n)   begin      key_reg <= 3'b111;            //默认没按下状态为高,按下之后为低.反之则为3'b000;    end  else     begin      key_reg <= {key_reg[1:0],key_in};      end  end     assign HtoL_flag = key_en?(key_reg[2:1] == 2'b10):0;            //下降沿检测,一个时钟的高电平 assign LtoH_flag = key_en?(key_reg[2:1] == 2'b01):0;               //上升沿检测,一个时钟的高电平  //--------------------------------------------- reg           cnt_en;                 //计数使能标志 reg   [15:0]  cnt2;  always @(posedge clk or negedge rst_n) begin  if(!rst_n)   begin      cnt2 <= 16'd0;    end  else if((cnt_en)&&(cnt2 == t1ms))    begin      cnt2 <= 16'd0;    end    else if(cnt_en)    begin      cnt2 <= cnt2 + 16'd1;    end        else           cnt2 <= 16'd0;      end     reg   [4:0]   cnt3;  always @(posedge clk or negedge rst_n) begin  if(!rst_n)   begin       cnt3 <= 5'd0;    end  else if((cnt_en)&&(cnt2 == t1ms))    begin       if(cnt3 == t_20ms )          cnt3 <= t_20ms;       else         cnt3 <= cnt3 + 1;                                  end    else if(!cnt_en)       cnt3 <= 5'd0;          end    //----------------------------------//按键状态机    reg  [2:0]  i;    reg      key_down;        //按键按下标志    reg      key_up;          //按键释放标志      always @(posedge clk or negedge rst_n)     begin      if(!rst_n)       begin                key_down <= 0;                key_up <= 0;                i <= 0;                cnt_en <= 0;        end      else         begin          case(i)           'd0:               begin                 key_down <= 0;                 key_up <= 0;                     if(HtoL_flag) i <= 'd1;         //检测到按下                 else if(LtoH_flag) i <= 'd2;    //检测到释放按键                 else  i  <= 'd0;                end               'd1:                 begin                   if(cnt3 == t_20ms )                       begin                        if(!key_in)                  //检测到按键依然被按下                          begin                             key_down <= 1;            //按键按下成功                             i <= 'd3;                             cnt_en <= 0;                           end                          else                             begin                                key_down <= 0;                                 i <= 'd0;                                cnt_en <= 0;                                     end                          end                        else                          cnt_en <= 1;                       end                'd2:                    begin                       if(cnt3 == t_20ms )                         begin                          if(key_in)                  //检测到按键被释放                            begin                                 key_up <= 1;             //按键释放成功                                 i <= 'd3;                                 cnt_en <= 0;                               end                           else                             begin                                 key_up <= 0;                                   i <= 'd0;                                 cnt_en <= 0;                                       end                            end                        else                          cnt_en <= 1;                       end               'd3:                    begin                      key_up <= 0;                        key_down <= 0;                                  i <= 'd0;                                                 end                                 default:i <= 'd0;               endcase                    end      end//--------------------------- reg          key_out; always @(posedge clk or negedge rst_n) begin  if(!rst_n)   begin      key_out <= 1;     end  else     begin      if(key_down)           key_out <= 0;               //按下为低      else  if(key_up)           key_out <= 1;              //释放为高      else           key_out <= key_out;         //否则保持    end  end     assign   key_down_out = key_down;         //当按键被按下有效时 assign   key_up_out = key_up;         //当按键被释放后才有效时 assign   key_in_out = key_out;endmodule

 

 

 

仿真:

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
基于Verilog下按键消抖(特权版)
[博客大赛]按键消抖之终极解决方案
按键消抖的原理和基于fpga的消抖设计_明德扬资料
Verilog HDL按键消抖
【FPGA学习】一文教你轻松实现数模转换的设计
小菜鸟学FPGA——按键消抖
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服