FPGA开发板按键与LED控制实战:从消抖到状态机

发布时间:2026/7/17 19:51:59
FPGA开发板按键与LED控制实战:从消抖到状态机 1. 紫光盘古50K开发板按键与LED基础配置作为一名长期从事嵌入式开发的工程师第一次拿到紫光盘古50K开发板时最直观的感受就是其丰富的外设接口和清晰的布局设计。这块开发板搭载了8个用户可编程按键K1-K8和8个LED指示灯构成了我们实现按键控制LED循环点亮的基础硬件环境。开发板的按键采用低电平有效设计这意味着当按键按下时对应的GPIO引脚会检测到低电平信号。这种设计在嵌入式系统中非常常见主要基于两个考虑一是大多数MCU/FPGA的上拉电阻配置更为稳定二是在电路设计上按键接地比接电源更不容易引入干扰。具体到盘古50K开发板每个按键都通过10kΩ电阻上拉到3.3V按下时通过机械触点连接到GND。LED电路则是典型的三极管驱动方案。开发板上每个LED都通过一个PNP三极管如S8550进行驱动这种设计可以提供足够的驱动电流每个LED约15-20mA保护主控芯片的GPIO口不被大电流损坏实现逻辑反向GPIO输出低电平时LED点亮实际调试中发现开发板原理图中LED驱动三极管的基极电阻为1kΩ这意味着GPIO输出低电平时三极管基极电流约为(3.3V-0.7V)/1kΩ2.6mA足以使三极管饱和导通。2. 开发环境搭建与工程创建在开始编码前需要准备好完整的开发环境。紫光盘古50K开发板使用的是PGL50H FPGA芯片配套的开发工具是Pango Design SuitePDS。我推荐安装最新版的PDS 2023.3这个版本对50K系列的支持最为完善。安装过程有几个关键点需要注意驱动程序安装时务必关闭所有杀毒软件避免误拦截安装路径不要包含中文或特殊字符安装完成后需要重启电脑使驱动生效新建工程时选择正确的器件型号PGL50H-6FBG484。工程创建后需要配置引脚约束文件.xdc。对于我们的LED控制实验至少需要定义set_property -dict {PACKAGE_PIN AB12 IOSTANDARD LVCMOS33} [get_ports {led[0]}] set_property -dict {PACKAGE_PIN AB13 IOSTANDARD LVCMOS33} [get_ports {key[0]}]引脚分配信息可以在开发板配套的原理图中找到。建议将8个LED和8个按键的引脚一次性全部约束好即使暂时只用其中几个这样后续扩展功能时就不需要反复修改约束文件了。3. Verilog按键消抖逻辑实现按键消抖是嵌入式系统中最基础也最容易出问题的环节之一。机械按键在按下和释放时会产生5-10ms的抖动如果不处理会导致单次按键被误识别为多次触发。在FPGA中我们通常采用状态机计时器的消抖方案。下面是一个经过实际验证的可靠消抖模块代码module debounce ( input clk, // 50MHz时钟 input key_in, // 原始按键输入 output reg key_out // 消抖后输出 ); reg [19:0] cnt; reg key_reg; always (posedge clk) begin key_reg key_in; if (key_reg ! key_in) cnt 20d100000; // 2ms计时(50MHz时钟) else if (cnt ! 0) cnt cnt - 1; else key_out key_reg; end endmodule这个设计的工作原理是检测按键状态变化时启动计时器2ms计时期间持续监测按键状态只有计时结束且状态稳定的信号才会输出实测发现对于盘古开发板的按键2ms消抖时间足够可靠。如果环境干扰较大可以适当延长到5ms修改cnt初始值为250000。4. LED循环点亮的状态机设计实现LED循环点亮的核心是一个状态机它根据按键输入改变LED的显示模式。我们设计一个包含4种显示模式的状态机全灭状态从左到右流水灯从右到左流水灯呼吸灯效果状态转换由按键K1控制每按一次切换到下一个模式。状态机的Verilog实现如下module led_controller ( input clk, input rst_n, input key_press, output reg [7:0] led ); reg [1:0] state; reg [23:0] counter; reg [3:0] pos; reg pwm_dir; // 状态定义 localparam S_OFF 2b00; localparam S_LEFT 2b01; localparam S_RIGHT 2b10; localparam S_BREATH 2b11; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state S_OFF; counter 0; pos 0; pwm_dir 0; led 8h00; end else begin counter counter 1; // 按键处理 if (key_press) state state 1; // 状态机 case (state) S_OFF: led 8h00; S_LEFT: begin if (counter[20]) begin // 约0.5秒移位一次 pos pos 1; led (8h01 pos); if (pos 7) pos 0; end end S_RIGHT: begin if (counter[20]) begin pos pos 1; led (8h80 pos); if (pos 7) pos 0; end end S_BREATH: begin if (counter[19:10] 10h3FF) // PWM周期1024个时钟 pwm_dir ~pwm_dir; led {8{counter[19:10] (pwm_dir ? counter[9:0] : ~counter[9:0])}}; end endcase end end endmodule这个设计有几个值得注意的技巧使用counter的不同位域实现多速率定时移位速度、PWM频率呼吸灯效果通过比较计数器的高低位实现无需额外PWM模块状态转换简洁明了易于扩展新模式5. 系统集成与功能验证将各个模块集成到顶层文件中完成最终的系统搭建module top ( input clk50m, input [7:0] key, output [7:0] led ); wire key_press; debounce debounce_inst ( .clk(clk50m), .key_in(~key[0]), // 注意按键是低电平有效 .key_out(key_press) ); led_controller led_ctl_inst ( .clk(clk50m), .rst_n(1b1), // 开发板有专用复位电路 .key_press(key_press), .led(led) ); endmodule在实验室环境下进行功能验证时建议按照以下步骤进行静态测试不编程状态下用万用表测量按键引脚电压应为3.3V按下时应变为0V简单IO测试编写最简单的LED点亮程序验证基本IO功能按键响应测试确认按键消抖效果完整功能测试验证所有4种显示模式的正确切换常见问题排查如果LED完全不亮检查约束文件中的引脚分配是否正确测量LED供电电压如果按键无响应检查消抖模块的时钟输入是否正确测量按键信号是否到达FPGA引脚如果显示模式切换不正常用SignalTap逻辑分析仪抓取状态机信号6. 性能优化与扩展思路基础功能实现后可以考虑以下几个优化方向增加模式记忆功能利用FPGA的配置Flash存储当前模式断电后不丢失// 在led_controller模块中添加 reg [1:0] saved_state; always (posedge clk) begin if (key_press) begin saved_state state 1; // 这里需要调用Flash编程IP核保存状态 end end实现加速度检测按键按住时间越长LED流动速度越快// 修改led_controller中的计数器判断 wire [3:0] speed (key_hold_time 10) ? 20 : (key_hold_time 20) ? 19 : 18; if (counter[speed]) begin // 动态速度控制 pos pos 1; // ... end添加音频反馈为每次按键操作添加滴声提示module beep ( input clk, input trigger, output pwm_out ); // 简单的音频发生器实现 endmodule多按键组合控制利用K1-K8实现更复杂的控制逻辑wire [7:0] keys_pressed; debounce debounce_inst[7:0] (.clk(clk50m), .key_in(~key), .key_out(keys_pressed)); always (*) begin case (keys_pressed) 8b00000001: // 仅K1按下 8b00000011: // K1和K2同时按下 // ...其他组合 endcase end在实际项目中我发现紫光盘古50K开发板的GPIO驱动能力相当不错可以直接驱动小型继电器模块。这意味着这个LED控制项目可以轻松扩展为实际的工业控制应用比如流水线指示灯控制、设备状态显示等。FPGA的并行处理特性使其特别适合这类需要实时响应的控制场景。