FPGA学习笔记--按键消抖模块(小梅哥2024版)

状态转移图

alt text

State值 状态
0 IDLE
1 P_FILTER
2 WAIT_R
3 R_FILTER

模块图

alt text

为了消除亚稳态,前面加两个D触发器,使得信号同步,再与边沿检测器相连 alt text 上升沿:前一拍为0,后一拍为1 下降沿:前一拍为1,后一拍为0

代码实现

module key_filter(
    clk,
    rst_n,
    key,
    key_p_flag,
    key_r_flag
);

    input clk;
    input rst_n;
    input key;

    output reg key_p_flag;//press按下
    output reg key_r_flag;//release松开

    reg sync_d0_key;
    reg sync_d1_key;
    reg r_key;

    wire pedge_key;//下降沿
    wire nedge_key;//上升沿

    wire time_20ms_reached;

    reg [1:0]state;
    localparam IDLE=0;
    localparam P_FILTER=1;
    localparam WAIT_R=2;
    localparam R_FILTER=3;

    parameter MCNT = 1_000_000-1;
    reg [29:0]cnt;

    always @(posedge clk) begin
        sync_d0_key<=key;
    end

    always @(posedge clk) begin
        sync_d1_key<=sync_d0_key;
    end

    always @(posedge clk) begin
        r_key<=sync_d1_key;
    end

    assign nedge_key=(sync_d1_key==0)&&(r_key==1);
    assign pedge_key=(sync_d1_key==1)&&(r_key==0);

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)begin
          state<=IDLE;
          key_p_flag<=0;
          key_r_flag<=0;
        end
        else begin
          case (state)
              IDLE:begin
                key_r_flag<=0;
                if(nedge_key)
                    state<=P_FILTER;//检测到下降沿,进入按下消抖状态
              end

              P_FILTER:begin
                if(time_20ms_reached)begin
                    state<=WAIT_R;//按下消抖完成,进入等待释放状态
                    key_p_flag<=1;
                end
                else if(pedge_key)
                    state<=IDLE;//20ms内检测到上升沿,是抖动,回到空闲
                else
                    state<=state;//维持
              end

              WAIT_R:begin
                key_p_flag<=0;
                if(pedge_key)
                    state<=R_FILTER;//检测到上升沿,进入释放消抖状态
              end

              R_FILTER: begin
                if(time_20ms_reached)begin
                    state<=IDLE;//20ms后没有出现下降沿,消抖完成
                    key_r_flag<=1;
                end
                else if(nedge_key)
                    state<=WAIT_R;//发生抖动,回到WAIT_R,等待下一次上升沿
                else 
                    state<=state;//维持
              end
          
          endcase
        end
    end


    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cnt<=0;
        else if((state==R_FILTER)||(state==P_FILTER))
            cnt<=cnt+1'd1;
        else 
            cnt<=0;
    end

    assign time_20ms_reached=(cnt==MCNT);
    



endmodule

仿真验证

`timescale 1ns/1ns

module key_filter_tb;
    reg clk;
    reg rst_n;
    reg key;
    wire key_p_flag;
    wire key_r_flag;

    key_filter k1(
        .clk(clk),
        .rst_n(rst_n),
        .key(key),
        .key_p_flag(key_p_flag),
        .key_r_flag(key_r_flag)
    );

    initial clk=1;
    always #10 clk=~clk;

    initial begin
        rst_n=0;
        key=1;
        #201;
        rst_n=1;

        key=1;#100000000;
        //按下,开始抖动
        key=0;#18000000;
        key=1;#2000000;
        key=0;#1000000;
        key=1;#200000;
        key=0;#20000000;
        //稳定
        key=0;#50000000;
        //释放,开始抖动
        key=1;#2000000;
        key=0;#1000000;
        key=1;#20000000;
        //稳定
        key=1;#50000000;
        $stop;
    end
endmodule

仿真波形图

alt text 通过波形可以观察到,key信号有抖动,key_p_flag和key_r_flag依然正确指示按键是否按下,达到了消抖效果


本文章使用limfx的vscode插件快速发布