【FPGA】状态机实现按键消抖

1、状态机简介

状态机,FSM(Finite State Machine),也称为同步有限状态机从。指的是在同步电路系统中使用的,跟随同步时钟变化的,状态数量有限的状态机,简称状态机。

状态机分类

根据状态机的输出是否与输入有关可以分为迷你(Mealy)状态机和摩尔(Moore)状态机。迷你状态机的输出结果的判断条件是当前状态&&输入信号,摩尔状态机的输出结果的判断条件只有当前状态。

assign check = state == s_1100_101 && din = 0;//Mealy
assign check - state == s_1100_1010;//Moore

根据状态机的写法分为一段式、二段式、三段式状态机。

  • 一段式状态机是把状态跳转逻辑、状态机状态和状态机结果输出都写到一个always块里。

在信号少的情况容易理解,信号多的话容易出错且复杂。

  • 二段式状态机是把状态跳转逻辑用always组合逻辑,状态机状态用always时序逻辑,状态机结果输出用assign组合逻辑。

二段式状态机结果输出用组合逻辑判断容易产生毛刺。

  • 三段式状态机是把状态跳转逻辑用always组合逻辑,状态机状态用always时序逻辑,状态机结果输出用always时序逻辑。

三段式状态机描述方式使得FSM做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,更有利于时序稳定。

2、按键消抖原理

参考http://t.csdn.cn/gcJrP

这种方法直接用延时来检测,当检测到按键输入的信号电平改变时,就延时20ms,然后再取此时按键输入的信号电平作为检测结果。

3、状态机实现按键消抖

状态转移图

【FPGA】状态机实现按键消抖_第1张图片
module key_filter(

     input      clk       ,
     input      rst_n     ,
     input      key_in    ,
     output    reg key_out


);
    //参数定义
    parameter       T_DELAY = 1_000_000;//20ms
    localparam      IDLE    = 4'b0001,
                    F_DOWN  = 4'b0010,
                    HOLD    = 4'b0100,
                    F_UP    = 4'b1000;

    //信息定义
    reg  [3:0] state_c;//现态
    reg  [3:0] state_n;//次态
    
    reg    key_r0   ;
    reg    key_r1   ;

    wire   nedge    ;
    wire   pedge    ;

    reg   [19:0]     cnt      ;
    wire             add_cnt  ;
    wire             end_cnt  ;

    //状态转移条件
    wire    idle2f_down ;
    wire    f_down2hold ;
    wire    f_down2idle ;
    wire    hold2f_up   ;
    wire    f_up2idle   ;


    //同步
    always@(posedge clk or negedge rst_n)begin

    if(~rst_n)begin
        key_r0 <= 1;  
    end
    else begin
      
      key_r0 <=key_in;

    end

    end
    
    //打拍
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            key_r1 <= 1;
        end

           else begin  
            key_r1 <= key_r0 ;

           end 
    end

    //下降沿检测
    assign      nedge = ~key_r0 && key_r1;
    //上升沿检测
    assign      pedge = key_r0 &&  ~key_r1;

   
        
    


  
    //状态机
    always @(posedge clk or negedge rst_n) begin
        if(~rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c<= state_n;
            
        end
    

    end
   //状态转换

     always @(*) begin
        case(state_c)
        IDLE:begin
            if(idle2f_down)begin
                state_n <= F_DOWN;
            end
            else begin
                state_n <= state_c;
            end
        end

        F_DOWN:begin
               if(f_down2hold)begin
                state_n <= HOLD;
            end
              else if(f_down2idle)begin
                state_n <= IDLE;
              end
            else begin
                state_n <= state_c;
            end
        end

        HOLD:begin
                if(hold2f_up)begin
                state_n <= F_UP;
            end
            else begin
                state_n <= state_c;
            end
        end

        F_UP:begin
              if(f_up2idle)begin
                state_n <= IDLE;
            end
            else begin
                state_n <= state_c;
            end
        end
        default: state_n = IDLE;

        endcase

     end
        assign    idle2f_down = ( state_c ==   IDLE   )&&(nedge );
        assign    f_down2hold = ( state_c ==   F_DOWN )&&( end_cnt && key_r0 == 1'b0);//计数到20ms时没有出现上升沿表示按键桉下保持稳定
        assign    f_down2idle = ( state_c ==   F_DOWN )&&( end_cnt && key_r1 == 1'b1);//计数到20ms且出现上升沿表示按键抖动,返回空闲态
        assign    hold2f_up   = ( state_c ==   HOLD   )&&( pedge);
        assign    f_up2idle   = ( state_c ==   F_UP   )&&( end_cnt);

       //20ms延时
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          cnt <= 1'b0; 
    end
    else if(add_cnt)begin
        if(end_cnt)begin
            cnt <= 1'b0;
        end
        else begin
            cnt <= cnt + 1'b1;
        end
    end
end

assign  add_cnt = (state_c == F_DOWN) || (state_c == F_UP); //按键桉下或上弹时开始计数
assign  end_cnt = add_cnt && (cnt == T_DELAY-1 || pedge);//当计数计到最大值或检测到上升沿计数器清零

//key_out
     always @(posedge clk or negedge rst_n) begin
     if(!rst_n)begin
        key_out <= 1'b0     ;
    end    
    else if(state_c == HOLD && hold2f_up) begin
        key_out <= ~key_r1  ;
    end
    else begin
        key_out <= 1'b0     ;
    end
end



endmodule

按键控制灯的代码在上面的链接中讲了,在此不在赘述。

仿真结果

【FPGA】状态机实现按键消抖_第2张图片

你可能感兴趣的:(vscode)