2. 基础组合逻辑IP

任务目标

实现一个基于边沿检测的按键消抖模块,消除机械抖动,输出稳定的按键脉冲。按键按下和释放时均会产生约5~20ms的机械抖动,本模块通过20ms稳定计数窗口滤除抖动,仅在按键状态稳定后输出单周期脉冲。

设计原理

采用“采样+计数”方式:当检测到按键电平与稳定状态不一致时,启动计数器;若电平在连续20ms内保持不变,则判定状态翻转并输出脉冲;若中途变化则清零重新计数。该方案无需精确时钟分频,仅需一个计数器即可实现。

模块接口

信号方向位宽说明
clkinput1系统时钟,50MHz
rst_ninput1异步复位,低有效
key_ininput1按键原始输入(低电平表示按下)
key_pulseoutput1消抖后按键脉冲,高有效,持续1个时钟周期
key_stateoutput1消抖后稳定电平(低=按下,高=释放)

关键完整代码

module key_debounce #(
    parameter CLK_FREQ = 50_000_000,  // 时钟频率Hz
    parameter DEBOUNCE_MS = 20        // 消抖时间ms
)(
    input  wire clk,
    input  wire rst_n,
    input  wire key_in,
    output reg  key_pulse,
    output reg  key_state
);

    // 计算计数上限
    localparam integer CNT_MAX = CLK_FREQ / 1000 * DEBOUNCE_MS - 1;

    reg [31:0] cnt;
    reg        key_state_d;  // 上一拍稳定状态

    // 异步复位
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt       <= 32'd0;
            key_state <= 1'b1;   // 默认高电平(释放)
            key_state_d <= 1'b1;
            key_pulse <= 1'b0;
        end else begin
            // 保存上一拍状态
            key_state_d <= key_state;

            // 按键电平与当前稳定状态不一致 → 启动计数
            if (key_in != key_state) begin
                if (cnt == CNT_MAX) begin
                    // 计数满,状态翻转
                    key_state <= key_in;
                    cnt       <= 32'd0;
                end else begin
                    cnt <= cnt + 32'd1;
                end
            end else begin
                // 电平一致,清零计数器
                cnt <= 32'd0;
            end

            // 边沿检测:稳定状态发生变化时产生脉冲
            if (key_state_d != key_state)
                key_pulse <= 1'b1;
            else
                key_pulse <= 1'b0;
        end
    end

endmodule

运行与验证

Testbench要点:

// 在testbench中:
// 1. 初始化key_in=1,运行100us
// 2. 模拟按下抖动:key_in先拉低2us,再拉高1us,再拉低并保持
// 3. 等待约20ms,观察key_pulse是否出现一个高脉冲
// 4. 模拟释放抖动:key_in拉高2us,拉低1us,再拉高并保持
// 5. 等待约20ms,再次观察key_pulse

仿真通过条件:

  • 按下抖动期间(前3us内)key_pulse保持为0,无毛刺输出
  • 按键稳定约20ms后,key_state变为0(按下),同时key_pulse输出1个时钟周期的高脉冲
  • 释放抖动期间无脉冲输出,稳定20ms后key_state回到1,key_pulse再次输出单周期脉冲

综合与上板验证:将key_pulse连接至LED取反逻辑,每按一次按键LED翻转一次。若LED状态稳定翻转且无抖动闪烁,则模块工作正常。

注意事项

  • 参数CLK_FREQ必须与实际时钟匹配,否则消抖时间会偏移
  • 计数器位宽需满足CNT_MAX最大值,50MHz/20ms时计数上限为999999,需20位以上,此处使用32位安全余量
  • 本模块适用于机械按键,若按键为自锁开关(状态保持型),则无需脉冲输出,只需key_state即可