本次案例是按着小梅哥的思路来写的,部分截图和文字来自其教学视频。

1、状态机的设定

基于状态机的按键消抖模块

基于状态机的按键消抖模块

2、模块代码

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:Lclone
// 
// Create Date: 2023/01/14 20:44:54
// Design Name: 
// Module Name: key_filter
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module key_filter(
    input           Clk,        //时钟信号
    input           Rst_n,      //复位信号
    input           Key_in,     //按键输入信号
    output  reg     Key_press,  //按键按下信号
    output  reg     Key_release //按键释放信号
    );
    reg [1:0]   key_reg;
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            key_reg <= 0;
        else
            key_reg <= {key_reg[0],Key_in};//打拍子,为了捕获上升沿和下降沿
    end
    reg key_nedge;
    reg key_pedge;
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            begin
                key_nedge <= 0;
                key_pedge <= 0;
            end
        else if(key_reg == 2'b10 )//下降沿捕获
            key_nedge <= 1'b1;
        else if(key_reg == 2'b01 )//上降沿捕获
            key_pedge <= 1'b1;
        else
            begin
                key_nedge <= 0;
                key_pedge <= 0;
            end
    end
    parameter  CNT_20MS = 1_000_000;
    reg [19:0]  cnt_20ms;
    reg [ 1:0]  state;
    always @(posedge Clk or negedge Rst_n) begin
        if(Rst_n == 0)
            begin
                state <= 0;
                cnt_20ms <= 0;
                Key_press <= 0;
                Key_release <= 0;
            end
        else case(state)
            0:
                begin
                    Key_release <= 0;
                    if(key_nedge == 1)
                        state <= 1;
                    else
                        state <= 0;
                end
            1:
                begin
                    if(cnt_20ms < CNT_20MS & key_pedge == 1)
                        begin
                            state <= 0;
                            cnt_20ms <= 0;
                        end
                    else if(cnt_20ms == CNT_20MS - 1)
                        begin
                            state <= 2;
                            cnt_20ms <= 0;
                            Key_press <= 1;
                        end
                    else
                        cnt_20ms <= cnt_20ms + 1'b1;
                end
            2:
                begin
                    Key_press <= 0;
                    if(key_pedge == 1)
                        state <= 3;
                    else
                        state <= 2;
                end
            3:
                begin
                    if(cnt_20ms < CNT_20MS & key_nedge == 1)
                        begin
                            state <= 2;
                            cnt_20ms <= 0;
                        end
                    else if(cnt_20ms == CNT_20MS - 1)
                        begin
                            state <= 0;
                            cnt_20ms <= 0;
                            Key_release <= 1;
                        end
                    else
                        cnt_20ms <= cnt_20ms + 1'b1;
                end
            default:;
        endcase
    end
endmodule

3、仿真

(1)$random函数的使用

rand = {$random(seed)} % 10_000_000;

(2)仿真代码

`timescale 1ns / 1ps
module key_filter_tb();
reg clk_50m;
initial clk_50m <= 1;
always #10 clk_50m <= ~clk_50m;
reg rst_n;
initial begin
    rst_n <= 0;
    #200
    rst_n <= 1;
end
reg         key_in;
wire        key_press;
wire        key_release;
key_filter  key_filter_inst(
    .Clk                (clk_50m),
    .Rst_n              (rst_n),
    .Key_in             (key_in),
    .Key_press          (key_press),
    .Key_release        (key_release)    
    );
initial begin
    key_in <= 1;
    #400
    press_key(1);
    #20
    press_key(2);
    #20
    press_key(3);
end
reg [31:0]rand;
task press_key;
    input [3:0]seed;
    begin
        key_in = 1;
        #20_000_000;
        repeat(5)begin
            rand = {$random(seed)} % 10_000_000;
            #rand key_in = ~key_in;
        end
        key_in = 0;
        #40_000_000;
        repeat(5)begin
            rand = {$random(seed)} % 10_000_000;
            #rand key_in = ~key_in;
        end
        key_in = 1;
        #40_000_000;
    end
endtask
endmodule

(3)仿真结果

第一次按下
基于状态机的按键消抖模块

第二次按下
基于状态机的按键消抖模块

第三次按下
基于状态机的按键消抖模块

实验结果:每次按下都能准确发出Key_press和Key_release信号,实验初步验证成功。

4、参考文献

[1]【零基础轻松学习FPGA】小梅哥Xilinx FPGA基础入门到项目应用培训教程】 https://www.bilibili.com/video/BV1va411c7Dz/?p=20&share_source=copy_web&vd_source=c6135c3b3a9878c08e2ddc91acdf6853&t=0