verilog三段式状态机学习笔记--按键消抖

什么是按键消抖:通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。 抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。一般不会超过20mS,这是一个很重要的时间参数,在很多场合都要用到。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引起一次按键被误读多次,所以一般在使用到按键的嵌入式设备中都需要进行按键消抖。
verilog三段式状态机学习笔记--按键消抖_第1张图片
使用verilog进行软件消抖大致可以分为4个状态:
1,没有按下按键,处于空闲状态;
2,按下按键,进入抖动滤除状态;
3,按键按下,正常状态;
4,按键释放,进入释放抖动滤除状态;
下图是状态转移图以及转移条件。
verilog三段式状态机学习笔记--按键消抖_第2张图片
以下是代码实现:

module key_filter_top
 (
  input    clk_50MHZ   ,//系统时钟50M
  input       rst_n    ,//系统复位
  input    key_in    ,//按键
  output reg [3:0] led ,     //4位LED灯
  output reg key_flag,
  output led_flag
   ); 
   
   
 reg       key_temp0  ;//用于按键上一次状态存储
 reg       key_temp1  ;//用于按键当前状态存储
 wire      key_posedge  ;//按键上升沿
 wire      key_negedge  ;//按键下降沿
 //wire       led_flag;
 reg  [20:0]cnt      ;//20位2进制计数器
 reg        cnt_en   ;//下降沿计数使能
 reg        cnt_full  ;//上升沿计数使能
 reg       key_flag_temp0  ;//用于按键上标志一次状态存储
 reg       key_flag_temp1  ;//用于按键标志当前状态存储
//===============================================================================
  //
  //边沿检测,采用寄存器方式。
  //
  //===============================================================================
  always@(posedge clk_50MHZ or negedge rst_n)
  begin
   if(!rst_n)
    begin
     key_temp0<=1'b0;
     key_temp1<=1'b0;
    end
   else
    begin
     key_temp1<=key_in;
     key_temp0<=key_temp1;
    end
  end
  
  assign key_posedge=(!key_temp0)&key_temp1;
  assign key_negedge=(!key_temp1)&key_temp0;
//===============================================================================
  //
  //检测到计时器使能开始计数器计数20mS
  //
  //=============================================================================== 
 always@(posedge clk_50MHZ or negedge rst_n)
  begin
   if(!rst_n)
    cnt<=1'b0;
   else if(cnt_en==1)
    begin 
     if(cnt==20'd9_999_999)
      cnt<=1'b0;
     else
      cnt<=cnt+1'b1;
    end
   else
    cnt<=1'b0;
  end
 
 always@(posedge clk_50MHZ or negedge rst_n)
  begin
   if(!rst_n)
    cnt_full<=1'b0;
   else if(cnt==20'd9_999_999)
    cnt_full<=1'b1;
   else
    cnt_full<=1'b0;
  end
  //==============================================================================
  //状态跳转条件:
  //
  //
  //==============================================================================
   localparam IDLE =4'b0001 ;//按键没有按下,空闲状态
   localparam S1  =4'b0010  ;//按键按下,滤除抖动状态
   localparam S2  =4'b0100  ;//按键按下,稳定状态
   localparam S3  =4'b1000  ;//按键释放,滤除抖动状态
   
   reg [3:0] Current_state  ;//初态
   reg [3:0] Next_state    ;//次态
//==============================================================================
  //三段式状态机第一段,时许逻辑电路,描述初始状态
  //
  //
  //==============================================================================
   always@(posedge clk_50MHZ or negedge rst_n)
    begin
     if(!rst_n)
      Current_state <= IDLE;
     else 
      Current_state <= Next_state;
    end
    //==============================================================================
  //三段式状态机第二段,组合逻辑电路,描述状态跳转
  //
  //
  //============================================================================== 
   always @(*)
    begin
      case(Current_state)
IDLE:
       begin
        if(key_negedge)
       begin
        cnt_en<=1'b1;
        Next_state<= S1;
       end
        else 
      begin
       cnt_en<=1'b0;
       Next_state<= IDLE;
      end
       end
S1:
       begin
        if(cnt_full)
      begin
       cnt_en<=1'b0;
       Next_state<= S2;
      end
        else 
      begin
       if(key_posedge)
        begin
         cnt_en<=1'b0;
         Next_state<= IDLE;
        end
       else
        begin
         cnt_en<=1'b1;
         Next_state<= S1;
        end
      end
       end
S2:
       begin
        if(key_posedge)
         begin
       cnt_en<=1'b1;
       Next_state<= S3;
      end
        else
         begin
       cnt_en<=1'b0;
       Next_state<= S2;
      end
       end
S3:
       begin
        if(cnt_full)
      begin
       cnt_en<=1'b0;
       Next_state<= IDLE;
      end
        else 
      begin
       if(key_negedge)
        begin
         cnt_en<=1'b0;
         Next_state<= S2;
        end
       else
        begin
         cnt_en<=1'b1;
         Next_state<= S3;
        end
      end
       end
      default: ;
      endcase
    end
     
  //==============================================================================
  //三段式状态机第三段,组合逻辑电路。
  //
  //
  //==============================================================================
   always @(Current_state or rst_n)
     begin
      if(!rst_n)
     key_flag<=1'b1;
      else 
    begin  
      case(Current_state)
     IDLE:
     	 key_flag<=1'b1;
     S1:
      	key_flag<=1'b1;
     S2: 
    	  key_flag<=1'b0;
     S3:
      key_flag<=1'b0;
     default :key_flag<=1'b1;
     endcase
    end
  end
//===============================================================================
  //
  //边沿检测,采用寄存器方式。
  //
  //===============================================================================
  always@(posedge clk_50MHZ or negedge rst_n)
  begin
   if(!rst_n)
   begin
     key_flag_temp0<=1'b0;
     key_flag_temp1<=1'b0;
    end
   else
    begin
     key_flag_temp0<=key_flag;
     key_flag_temp1<=key_flag_temp0;
    end
    end
  assign led_flag=key_flag_temp1&(!key_flag_temp0);
  
 always @(posedge led_flag or negedge rst_n)
  begin
  if(!rst_n)
    led<=4'b0001;
   else if(led==4'b1000)
    led<=4'b0001;
   else
    led<=(led<<1);
  end

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200224154039465.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNDc3NDY1MA==,size_16,color_FFFFFF,t_70)

你可能感兴趣的:(verilog三段式状态机学习笔记--按键消抖)