2. 按键消抖与单脉冲生成
任务说明
机械按键在按下与释放过程中会产生约5~20 ms的抖动,若直接作为时钟或触发信号,会导致LED翻转多次或逻辑误触发。本案例设计一个基于计数器采样的按键消抖电路,在按键状态稳定后产生一个单时钟周期脉冲,用于控制LED翻转。
设计原理
采用“连续采样确认”法:以系统时钟对按键输入持续采样,当检测到按键电平与稳定状态不同时启动计数器;若计数器计满设定的消抖时间(约20 ms)且期间电平未变化,则确认按键状态改变,输出一个单周期脉冲。该脉冲作为LED翻转的触发信号。
完整代码
module key_debounce_pulse (
input clk, // 系统时钟 50MHz
input rst_n, // 异步复位,低有效
input key_in, // 按键输入(按下为低电平)
output reg led_out, // LED输出(翻转控制)
output reg pulse_out // 单周期脉冲输出
);
// 参数定义:20ms消抖时间 @50MHz = 1_000_000 个时钟周期
parameter CNT_MAX = 20'd1_000_000;
// 内部信号
reg key_reg1, key_reg2; // 两级同步寄存器
reg key_stable; // 当前稳定状态
reg [19:0] cnt; // 消抖计数器
reg cnt_en; // 计数使能
wire key_changed; // 按键电平变化标志
// 两级同步,消除亚稳态
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_reg1 <= 1'b1;
key_reg2 <= 1'b1;
end else begin
key_reg1 <= key_in;
key_reg2 <= key_reg1;
end
end
// 检测电平变化(以上一拍稳定值为基准)
assign key_changed = (key_reg2 != key_stable);
// 消抖计数器控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 20'd0;
cnt_en <= 1'b0;
end else if (key_changed) begin
cnt <= 20'd0; // 检测到变化,重新计数
cnt_en <= 1'b1;
end else if (cnt_en) begin
if (cnt == CNT_MAX - 1) begin
cnt_en <= 1'b0; // 计数完成,关闭使能
end else begin
cnt <= cnt + 1'b1;
end
end
end
// 稳定状态更新与脉冲产生
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_stable <= 1'b1; // 默认高电平(未按下)
pulse_out <= 1'b0;
end else if (cnt_en && (cnt == CNT_MAX - 1)) begin
key_stable <= key_reg2; // 更新稳定状态
pulse_out <= 1'b1; // 产生单周期脉冲
end else begin
pulse_out <= 1'b0; // 其余时间脉冲为低
end
end
// LED翻转控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
led_out <= 1'b0;
else if (pulse_out)
led_out <= ~led_out; // 每次有效按键翻转一次
end
endmodule
运行与验证
环境要求:Vivado或Quartus(Verilog仿真),或ModelSim/QuestaSim。
仿真验证步骤:
- 将上述代码保存为
key_debounce_pulse.v。 - 编写testbench,提供50MHz时钟,模拟按键抖动(在按下/释放时加入随机毛刺),观察
pulse_out与led_out。 - 编译并运行仿真。
通过条件:
- 按下按键(key_in拉低)后,经过约20ms(即1,000,000个时钟周期),
pulse_out输出一个且仅一个高电平脉冲,宽度为1个时钟周期。 - 释放按键后同样只产生一个脉冲。
- 每次有效脉冲后
led_out电平翻转一次。 - 抖动期间(计数未完成时)
pulse_out保持低电平,无额外脉冲产生。
仿真激励要点:在testbench中,将 key_in 在按下和释放时各加入若干次10μs~1ms间隔的随机跳变(模拟机械抖动),再保持稳定电平。观察最终输出是否符合上述条件。
常见问题
- 脉冲宽度不正确:检查
CNT_MAX是否与时钟频率匹配。若时钟为25MHz,则需将参数改为500_000。 - LED翻转多次:确认
pulse_out只在计数完成的那一个周期为高,其余时间必须为低。若在else分支中未将pulse_out清零,会导致持续高电平。 - 亚稳态问题:两级同步寄存器
key_reg1/key_reg2不可省略,否则在异步按键输入下可能采样到不稳定电平。