2. 按键控制LED
任务目标
使用按键作为输入,控制开发板上LED的亮灭状态。每次按下按键时,LED状态翻转(按下前灭则按下后亮,反之亦然)。核心学习点在于:按键存在机械抖动,必须进行消抖处理;使用简单状态机(或等效的边沿检测逻辑)确保每次按键只触发一次状态翻转。
硬件环境与依赖
- 开发板:任意主流FPGA开发板(如Altera DE10-Lite、Xilinx Artix-7系列)
- 外设:1个按键(按下为低电平,即低有效)、1个LED(高电平点亮)
- 时钟:50MHz系统时钟(可根据实际板卡调整分频参数)
核心原理与设计思路
按键消抖的常用方法是“延时采样”:检测到按键电平变化后,等待约10~20ms,再次读取电平,若与变化后状态一致,则确认按键状态稳定。本设计采用状态机实现:空闲态等待按键按下,进入消抖态计时,计时结束后若按键仍为按下状态,则翻转LED并回到空闲态等待释放。该状态机同时天然完成了“按下沿检测”,避免一次按压过程中多次触发。
关键完整代码
module key_led (
input clk, // 50MHz
input rst_n, // 低电平复位
input key_in, // 按键输入,按下为0
output reg led_out // LED输出,1点亮
);
// 参数定义
parameter CNT_20MS = 1_000_000; // 50MHz时钟下20ms计数
// 状态编码
localparam IDLE = 2'b00;
localparam PRESS_DETECT = 2'b01;
localparam RELEASE_WAIT = 2'b10;
reg [1:0] state, next_state;
reg [19:0] cnt;
reg key_reg0, key_reg1; // 两级同步寄存器
// 按键同步(消除亚稳态)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_reg0 <= 1'b1;
key_reg1 <= 1'b1;
end else begin
key_reg0 <= key_in;
key_reg1 <= key_reg0;
end
end
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= IDLE;
else
state <= next_state;
end
// 计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt <= 20'd0;
else if (next_state != state) // 状态变化时清零
cnt <= 20'd0;
else if (state == PRESS_DETECT || state == RELEASE_WAIT)
cnt <= cnt + 1'b1;
end
// 状态转移组合逻辑
always @(*) begin
next_state = state;
case (state)
IDLE: begin
if (key_reg1 == 1'b0) // 检测到按下
next_state = PRESS_DETECT;
end
PRESS_DETECT: begin
if (cnt >= CNT_20MS) begin
if (key_reg1 == 1'b0) // 消抖后确认按下
next_state = RELEASE_WAIT;
else
next_state = IDLE; // 抖动,回到空闲
end
end
RELEASE_WAIT: begin
if (key_reg1 == 1'b1) // 等待释放
next_state = IDLE;
end
endcase
end
// LED输出控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
led_out <= 1'b0;
else if (state == PRESS_DETECT && next_state == RELEASE_WAIT)
led_out <= ~led_out; // 确认按下时翻转
end
endmodule
运行与验证
- 引脚约束:根据板卡原理图,将
clk连接50MHz时钟引脚,key_in连接按键引脚,led_out连接LED引脚,rst_n连接复位按键(或直接接高电平)。 - 编译烧录:在Quartus/Vivado中新建工程,添加上述代码,完成综合、布局布线后下载到FPGA。
- 功能验证:
- 上电后LED处于熄灭状态(若复位信号有效则更明确)。
- 快速按一下按键(按下时间大于20ms),LED状态翻转(灭→亮)。
- 再次按下,LED翻转(亮→灭)。
- 快速连续按多次,每次按下LED都翻转一次,不会出现一次按压多次翻转或状态错乱。
通过判定标准:每次完整按压(按下并释放)LED恰好翻转一次;快速连按时翻转次数与按压次数一致;按住不放时LED只翻转一次,不会持续闪烁。
常见问题
- LED不响应:检查按键是否低有效(按下为0),若为高有效需反转判断条件;检查时钟频率与
CNT_20MS参数是否匹配。 - 一次按压多次翻转:说明消抖时间不足或状态机逻辑有误,确认
CNT_20MS值是否正确(50MHz下20ms = 1_000_000)。 - LED状态上电不确定:确保复位信号有效,或在初始化时给
led_out赋确定初值。