1.1 概述
按键在实际操作过程中,由于其机械结构的特性,在按下或释放的瞬间会产生短暂而不稳定的抖动信号,这种现象被称为“按键抖动”。抖动时间通常持续5ms到10ms之间。若不加以处理,这类波动将影响系统的稳定运行,并降低输入响应的准确性。
为确保系统可靠识别按键动作,必须对抖动进行消除处理。常见的消抖方法包括使用RS触发器、电容滤波等硬件方式,以及通过程序实现的软件消抖方案。本章节采用FPGA内部逻辑实现软件消抖,利用时序控制跳过抖动阶段,从而准确判断按键的真实状态。
软件消抖的核心思想是:在检测到按键状态变化后,延时一段时间(如10ms),再次确认按键是否仍处于该状态,若一致则认为是一次有效的操作。这种方法简单高效,适用于大多数基于FPGA的设计场景。
1.3 key模块的设计
鉴于按键去抖功能具有较高的通用性,可将其封装为独立的功能模块,便于后续项目中的复用。以下为key模块的设计说明:
首先,对系统主时钟进行分频处理,生成周期为10ms的使能信号,用于驱动状态机的定时判断。整个消抖流程由四个状态构成:
- KEY_S0:初始状态,持续监测按键是否被按下。一旦检测到按下信号,则进入下一状态。
- KEY_S1:等待10ms后重新检测按键状态。若按键仍然处于按下状态,则确认为有效按下,转入KEY_S2;否则返回KEY_S0,视为误触或抖动。
- KEY_S2:开始检测按键是否已释放。若检测到松开动作,则进入KEY_S3。
- KEY_S3:再次延时10ms后验证按键是否确实已释放。若确认释放,则回到初始状态KEY_S0;否则保持在KEY_S2。
当状态从KEY_S1成功转移至KEY_S2时,表示完成一次完整的按键按下过程,此时输出一个脉冲高电平信号key_cap,作为外部电路的动作触发信号。
module key #
(
parameter CLK_FREQ = 100000000
)
(
input clk_i, input key_i, output key_cap
);
//10ms parameter CNT_10MS = (CLK_FREQ/100 - 1'b1);
parameter KEY_S0 = 2'd0;
parameter KEY_S1 = 2'd1;
parameter KEY_S2 = 2'd2;
parameter KEY_S3 = 2'd3; reg [24:0] cnt10ms = 25'd0;
(*mark_debug = "true"*) reg [1:0] key_s = 2'b0;
(*mark_debug = "true"*) reg [1:0] key_s_r = 2'b0;
(*mark_debug = "true"*) wire en_10ms ;
assign en_10ms = (cnt10ms == CNT_10MS);
assign key_cap = (key_s==KEY_S2)&&(key_s_r==KEY_S1);
always @(posedge clk_i)begin
if(cnt10ms < CNT_10MS)
cnt10ms <= cnt10ms + 1'b1;
else
cnt10ms <= 25'd0;
end
always @(posedge clk_i)begin
key_s_r <= key_s;
end
always @(posedge clk_i)begin
if(en_10ms)begin
case(key_s)
KEY_S0:begin
if(!key_i)
key_s <= KEY_S1;
end
KEY_S1:begin
if(!key_i)
key_s <= KEY_S2; elsekey_s <= KEY_S0;
end
KEY_S2:begin
if(key_i)
key_s <= KEY_S3;
end
KEY_S3:begin
if(key_i)key_s <= KEY_S0;
else key_s <= KEY_S2;
end
endcase
end
end
endmodule
1.4 key模块的调用与LED控制
在顶层设计中调用上述key模块,每当key_cap信号产生一次有效脉冲,即触发连接的LED状态翻转一次。具体表现为:每按一次按键,对应的LED灯点亮或熄灭交替变化。
实现代码如下所示:
module Key_Jitter(
input clk_i,
input rst_n_i,
input key_i,
output [2:0] led_o
);
(*mark_debug = "true"*) reg [2:0] led_o;
(*mark_debug = "true"*) wire key_cap;
always @(posedge clk_i)begin
if(!rst_n_i)begin
led_o <= 4'b0000;
end
else if(key_cap)begin
led_o <= ~led_o;
end
end
key#(
.CLK_FREQ(100000000)
)key0(
.clk_i(clk_i),
.key_i(key_i),
.key_cap(key_cap)
);
endmodule
1.2 硬件资源配置
FPGA开发板上配备多个独立按键,均可与FPGA引脚直接连接。本实验选用SW1按键作为输入源,配合一个LED指示灯用于输出反馈。各按键工作原理相同,故以单一按键为例即可代表整体应用逻辑。
功能目标为:每次完整按下并释放SW1按键,LED状态发生一次切换——首次按下点亮,再次按下熄灭,依此类推。
仿真与验证流程
1、创建新的仿真测试文件,其源码如下所示:
`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 11:11:11 11/31/2025
// Design Name: zhou_bin_yyd
// Module Name:
// Project Name: Key_Jitter
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: Key_Jitter
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////
module Key_Jitter_TB;
// Inputs
reg clk_i;
reg rst_n_i;
reg key_i;
wire [3:0] led_o;
// Instantiate the Unit Under Test (UUT)
Key_Jitter uut (
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.key_i(key_i),
.led_o(led_o)
);
initial
begin
// Initialize Inputs
clk_i = 0;
forever
#5 clk_i=~clk_i;
end
initial
begin
// Initialize Inputs
rst_n_i = 0;
#100;
rst_n_i=1;
key_i = 1;
#10000;
forever
begin
key_i = 0;
// Wait 100 ns for global reset to finish
#100;
key_i=1; #1000;
key_i=0; #1000;
key_i=1; #2000;
key_i=0; #5000;
#50000000;
key_i=1;
key_i=0; #1000;
key_i=1; #2000;
key_i=0; #1000;
key_i=1; #2000;
#50000000;
key_i=0;
end
end
endmodule
2、进入仿真环境:选择 SIMULATION → Run Simulation → Run Behavioral Simulation,启动行为级仿真。
3、执行仿真过程。

4、观察仿真波形结果,验证消抖逻辑是否正确执行。
5、完成代码编译,生成可编程固件。
6、下载固件至FPGA设备,进行实物测试。实验结果显示:每次按下SW1按键,LED均能准确地实现亮灭切换,无误触发或漏响应现象。
为进一步体现消抖机制的重要性,可尝试调整延时参数至极小或极大值。此时会发现,尽管按键已被按下,但LED可能无反应或出现多次翻转,这正是未有效避开抖动区间所导致的结果。