2. 消抖原理:硬件消抖(RC滤波)与软件消抖(延时采样)的对比

按键抖动,说白了就是机械触点闭合瞬间的「弹跳」。你按下去,它不会老老实实稳定住,而是会来回弹几下。我刚开始做FPGA时,就被这个现象坑过——明明只按了一次,系统却检测到了三四次触发。

为什么会这样?因为机械按键内部是金属弹片。按下时,弹片会振动,产生一连串的脉冲信号。这个抖动时间通常持续5-20ms。嗯,这里要注意,不同按键的抖动时间差异很大,便宜的按键可能抖得更久。

解决抖动,无非两条路:硬件上做RC滤波,或者软件上做延时采样。我两种方法都用过,今天跟你聊聊各自的优缺点。

2.1 硬件消抖:RC低通滤波

硬件消抖的原理很简单——用电容把高频抖动滤掉。你想想看,抖动信号频率高,电容对高频信号阻抗低,直接把它旁路到地了。剩下的就是稳定的电平变化。

典型的RC电路长这样:

VCC —— 按键 —— R1 —— 输出点 —— 到FPGA引脚
                    |
                    R2
                    |
                   GND
                    |
                   C1
                    |
                   GND

R1和R2构成分压,C1是滤波电容。时间常数τ = (R1//R2) × C1。我一般取τ在10ms左右,这样既能滤掉抖动,又不会让按键响应太迟钝。

参数选择建议:

  • R1 = 10kΩ,R2 = 10kΩ,C1 = 1μF → τ ≈ 5ms
  • R1 = 4.7kΩ,R2 = 4.7kΩ,C1 = 2.2μF → τ ≈ 10ms
  • R1 = 1kΩ,R2 = 1kΩ,C1 = 10μF → τ ≈ 20ms
我的经验: 别把时间常数搞太大。我曾经为了追求「稳定」,用了100μF的电容,结果按键按下去要等半秒才响应,用户体验极差。10ms左右是个甜点值。

硬件消抖的好处是零CPU开销,信号进来就是干净的。但缺点也很明显——需要额外元件,而且一旦焊好,参数就固定了。如果你换了个抖动特性不同的按键,就得重新改电路。

2.2 软件消抖:延时采样

软件消抖,说白了就是「等它抖完再读」。FPGA里没有CPU那种延时函数,但我们有计数器。核心思路是:检测到电平变化后,启动一个计数器,等10-20ms后再采样一次。如果两次采样结果一致,就认为按键稳定了。

我常用的三段式状态机写法:

// 按键消抖模块 - 延时采样法
module debounce (
    input  wire       clk,      // 系统时钟,假设50MHz
    input  wire       rst_n,    // 复位
    input  wire       key_in,   // 按键输入
    output reg        key_out   // 消抖后输出
);

    // 20ms延时需要的计数次数
    // 50MHz → 20ns周期,20ms = 1_000_000个时钟周期
    localparam CNT_MAX = 20'd1_000_000;

    reg [19:0] cnt;
    reg        key_sync;   // 同步后的按键值
    reg        key_last;   // 上一次的按键值

    // 两级同步,消除亚稳态
    reg key_sync_1, key_sync_2;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            key_sync_1 <= 1'b1;
            key_sync_2 <= 1'b1;
        end else begin
            key_sync_1 <= key_in;
            key_sync_2 <= key_sync_1;
        end
    end
    assign key_sync = key_sync_2;

    // 消抖逻辑
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt     <= 20'd0;
            key_last <= 1'b1;
            key_out  <= 1'b1;
        end else begin
            key_last <= key_sync;
            
            // 检测到电平变化,启动计数器
            if (key_sync != key_last) begin
                cnt <= 20'd0;
            end 
            // 计数器未满,继续计数
            else if (cnt < CNT_MAX) begin
                cnt <= cnt + 1'b1;
            end 
            // 计数器满,输出稳定值
            else begin
                key_out <= key_sync;
            end
        end
    end

endmodule
核心要点: 这个代码里我做了两级同步,这是必须的。按键信号来自外部,和FPGA时钟不同步,直接采样可能采到亚稳态。我见过有人省掉同步器,结果系统偶尔会误触发——嗯,那种bug最难查。

软件消抖的好处是零成本,改参数也方便。想调延时?改一下CNT_MAX就行。但缺点也明显——占用FPGA逻辑资源,而且如果系统时钟变了,延时时间也会变。

2.3 两种方案的对比

对比项 硬件消抖(RC滤波) 软件消抖(延时采样)
成本 需要电阻电容,增加BOM 零额外成本
灵活性 参数固定,改起来麻烦 改代码即可,非常灵活
资源占用 不占FPGA资源 需要计数器、状态机
可靠性 抗干扰能力强 受时钟抖动影响
响应速度 取决于RC时间常数 取决于采样延时
适用场景 工业环境、对可靠性要求高 消费电子、快速原型验证
避坑指南: 我曾经在一个项目里同时用了硬件和软件消抖——RC滤波后再做延时采样。结果按键响应慢得像蜗牛,按下去要等300ms才有反应。后来才意识到,两种方法叠加,延时是相加的。所以,选一种就好,别画蛇添足。

2.4 我的选择建议

说实话,没有的好坏。我个人的习惯是:

  • 做开发板、原型验证 → 用软件消抖。改起来快,不用动烙铁。
  • 做产品、尤其是工业产品 → 用硬件消抖。RC滤波抗干扰能力强,电源噪声、电磁干扰都不容易误触发。
  • 如果按键数量多 → 用软件消抖。一个模块可以复用,省PCB面积。

你想想看,一个产品上如果有8个按键,每个按键配两个电阻一个电容,那就是24个元件。而用FPGA软件消抖,一个模块例化8次就搞定了。但反过来,如果只有一个电源按键,加个RC滤波反而更省事。

下面这张图,是我总结的消抖方案选择逻辑:

按键消抖方案选择流程 按键需要消抖 按键数量多? 是 否 软件消抖 延时采样法 环境恶劣? 是 否 硬件 RC滤波 两者皆可 看个人习惯 注:环境恶劣指工业现场、强电磁干扰、振动环境等 我的经验:原型用软件,产品用硬件。省钱省事两不误。

最后说一句,不管选哪种方案,同步器一定要加。这是FPGA设计的铁律——任何外部信号进FPGA,必须先过两级同步。我见过太多人在这上面栽跟头了。

好了,消抖原理就聊到这儿。下一节我们会上板实战,把代码下载到FPGA里,看看实际效果。到时候你会发现,理论说得再好,不如动手调一次来得实在。

小技巧: 调试时可以用示波器看按键波形。抖动信号看起来像一串毛刺,消抖后就是干净的高低电平跳变。看到那个对比,你会很有成就感的。