2. 按键消抖与单脉冲检测
任务目标
设计一个按键消抖与单脉冲检测电路,在FPGA上实现:当按键被按下时,消除机械抖动影响,仅输出一个周期宽度的单脉冲信号,用于后续逻辑的触发控制。
实现方案
采用计数器延时消抖法:检测到按键电平变化后启动计数器,经过约10ms稳定期后再次采样按键电平,若电平稳定则确认状态变化,并产生一个时钟周期的脉冲输出。该方案无需外部器件,资源占用少,适用于大多数机械按键场景。
完整代码
module key_debounce (
input clk, // 系统时钟,50MHz
input rst_n, // 异步复位,低有效
input key_in, // 按键输入,按下为低电平
output reg key_pulse // 单脉冲输出,高有效,宽度1个时钟周期
);
// 参数定义:10ms消抖时间 @50MHz = 500_000个时钟周期
parameter DELAY_CNT = 500_000;
// 状态编码
localparam IDLE = 2'b00; // 空闲态
localparam PRESS = 2'b01; // 按下确认中
localparam RELEASE = 2'b10; // 释放确认中
reg [1:0] state;
reg [18:0] cnt; // 计数器,2^19 > 500_000
reg key_in_d1; // 输入同步打拍
// 输入同步,消除亚稳态
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
key_in_d1 <= 1'b1;
else
key_in_d1 <= key_in;
end
// 主状态机
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
cnt <= 0;
key_pulse <= 1'b0;
end
else begin
// 默认输出为0
key_pulse <= 1'b0;
case (state)
IDLE: begin
// 检测到按键按下(低电平)
if (key_in_d1 == 1'b0) begin
state <= PRESS;
cnt <= 0;
end
end
PRESS: begin
// 等待消抖时间
if (cnt < DELAY_CNT) begin
cnt <= cnt + 1'b1;
end
else begin
// 消抖结束,再次采样
if (key_in_d1 == 1'b0) begin
// 确认按下,产生单脉冲
key_pulse <= 1'b1;
state <= RELEASE;
cnt <= 0;
end
else begin
// 抖动,回到空闲
state <= IDLE;
end
end
end
RELEASE: begin
// 等待释放
if (key_in_d1 == 1'b1) begin
if (cnt < DELAY_CNT) begin
cnt <= cnt + 1'b1;
end
else begin
// 确认释放
state <= IDLE;
end
end
else begin
// 按键仍按下,重置计数器
cnt <= 0;
end
end
default: state <= IDLE;
endcase
end
end
endmodule
仿真测试代码
module tb_key_debounce;
reg clk = 0;
reg rst_n = 0;
reg key_in = 1;
wire key_pulse;
// 50MHz时钟
always #10 clk = ~clk;
key_debounce uut (
.clk(clk),
.rst_n(rst_n),
.key_in(key_in),
.key_pulse(key_pulse)
);
// 模拟按键抖动
initial begin
// 复位
#20 rst_n = 1;
// 模拟按下抖动:低-高-低-高-低
#100 key_in = 0;
#5 key_in = 1;
#5 key_in = 0;
#5 key_in = 1;
#5 key_in = 0; // 稳定按下
// 等待消抖完成(10ms = 500_000周期 @50MHz)
// 仿真中可减小DELAY_CNT以加速,此处等待脉冲
wait (key_pulse == 1);
$display("PASS: Key press detected, pulse generated at time %0t", $time);
// 等待释放
#100 key_in = 1;
#5 key_in = 0;
#5 key_in = 1;
#5 key_in = 0;
#5 key_in = 1; // 稳定释放
#100;
$display("Test completed");
$finish;
end
endmodule
运行与验证
仿真验证:在Vivado或ModelSim中运行仿真,观察key_pulse信号。当按键稳定按下约10ms后,key_pulse应输出一个高电平脉冲,宽度恰好为1个时钟周期(20ns @50MHz)。仿真中为加速可临时将DELAY_CNT改小(如100),验证逻辑正确后再恢复实际值。
板级验证:将key_pulse连接至LED,按下按键后LED应闪烁一次(亮一个时钟周期,人眼不可见),可再接一个计数器分频后观察。通过条件:每次按下按键,key_pulse恰好产生一个脉冲,无重复触发;快速连续按键时每个按下动作均能正确检测。
关键说明
- 输入同步寄存器
key_in_d1用于消除跨时钟域亚稳态风险 - 状态机包含按下确认和释放确认两个稳定期,防止按键在释放时产生额外脉冲
- 若系统时钟频率不同,需按公式
DELAY_CNT = 10ms × 时钟频率重新计算参数