• 按键消抖-----verilog


    实际系统中常用的按键大部分都是轻触式按键,如下图所示。该按键内部由一个弹簧片和两个固定触点组成,当弹簧片被按下,则两个固定触点接通,按键闭合。弹簧片松开,两个触点断开,按键也就断开了。根据这种按键的机械特性,在按键按下时,会先有一段时间的不稳定期,在这期间,两个触点时而接通,时而断开,我们称之为抖动,当按键大约按下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
    View Code

     测试代码如下:     

    /********************************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;                   
      end
    endtask
        
    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
    View Code

     仿真结果:

     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
    View Code

    仿真:

  • 相关阅读:
    windows下 python2 和python3 共存
    利用xshell远程连接centos安装oracle11g时在图形界面登录
    本地导入/导出远程oracle数据库
    centos7安装桌面环境
    普通用户修改.bash_profile 权限问题
    centos7 安装oracle11g
    Unable to fetch some archives ,maybe run apt-get update or try with --fix-missing?
    spring mvc 使用ehcache
    leaflet创建简单地图
    【BZOJ 3958】 3958: [WF2011]Mummy Madness (二分+扫描线、线段树)
  • 原文地址:https://www.cnblogs.com/fhyfhy/p/4363457.html
Copyright © 2020-2023  润新知