FPGA入门实战:LED闪烁教程(VHDL与Verilog)
简介
欢迎来到实战指南,带您在FPGA上用VHDL或Verilog创建简单LED闪烁器。例程演示核心FPGA概念:并发进程、频率合成、多路复用器逻辑——均由25MHz时钟驱动。
项目概览
- 生成四种可选闪烁频率:100Hz、50Hz、10Hz、1Hz。
- 各频率保持50%占空比。
LED_EN开关置位时才启用LED。- 通过两独立开关选择所需频率。
- 部署硬件前必须仿真,尽早发现错误。
频率选择真值表
| 使能 | 开关1 | 开关2 | LED驱动频率 |
|---|---|---|---|
| 0 | - | - | (禁用) |
| 1 | 0 | 0 | 100Hz |
| 1 | 0 | 1 | 50Hz |
| 1 | 1 | 0 | 10Hz |
| 1 | 1 | 1 | 1Hz |
信号定义
| 信号名 | 方向 | 描述 |
|---|---|---|
| i_clock | 输入 | 25MHz系统时钟 |
| i_enable | 输入 | LED使能开关(逻辑0=关) |
| i_switch_1 | 输入 | 频率选择位1 |
| i_switch_2 | 输入 | 频率选择位0 |
| o_led_drive | 输出 | LED驱动信号 |
设计理念
四独立计数器进程并发运行,各在目标频率翻转局部信号。即使特定频率未选中,其计数器仍持续运行——这是FPGA并发标志。
开关实现组合多路复用器将所需翻转信号路由至LED驱动器。LED_EN开关作有效高位门控,使能低时LED关闭。
框图
VHDL实现
下方为满足规格的完整注释VHDL模块。计数器常数按公式计算:
count = (时钟频率 / 目标频率) × 0.5 以达50%占空比。
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity tutorial_led_blink is port ( i_clock : in std_logic; i_enable : in std_logic; i_switch_1 : in std_logic; i_switch_2 : in std_logic; o_led_drive : out std_logic ); end tutorial_led_blink; architecture rtl of tutorial_led_blink is -- 频率常数(半周期计数) constant c_CNT_100HZ : natural := 125000; -- 25MHz / 100Hz × 0.5 constant c_CNT_50HZ : natural := 250000; -- 25MHz / 50Hz × 0.5 constant c_CNT_10HZ : natural := 1250000; -- 25MHz / 10Hz × 0.5 constant c_CNT_1HZ : natural := 12500000; -- 25MHz / 1Hz × 0.5 -- 计数器信号 signal r_CNT_100HZ : natural range 0 to c_CNT_100HZ; signal r_CNT_50HZ : natural range 0 to c_CNT_50HZ; signal r_CNT_10HZ : natural range 0 to c_CNT_10HZ; signal r_CNT_1HZ : natural range 0 to c_CNT_1HZ; -- 翻转输出 signal r_TOGGLE_100HZ : std_logic := '0'; signal r_TOGGLE_50HZ : std_logic := '0'; signal r_TOGGLE_10HZ : std_logic := '0'; signal r_TOGGLE_1HZ : std_logic := '0'; -- 多路复用器选择 signal w_LED_SELECT : std_logic; begin -- 100Hz翻转进程 p_100_HZ : process (i_clock) is begin if rising_edge(i_clock) then if r_CNT_100HZ = c_CNT_100HZ-1 then r_TOGGLE_100HZ <= not r_TOGGLE_100HZ; r_CNT_100HZ <= 0; else r_CNT_100HZ <= r_CNT_100HZ + 1; end if; end if; end process p_100_HZ; -- 50Hz翻转进程 p_50_HZ : process (i_clock) is begin if rising_edge(i_clock) then if r_CNT_50HZ = c_CNT_50HZ-1 then r_TOGGLE_50HZ <= not r_TOGGLE_50HZ; r_CNT_50HZ <= 0; else r_CNT_50HZ <= r_CNT_50HZ + 1; end if; end if; end process p_50_HZ; -- 10Hz翻转进程 p_10_HZ : process (i_clock) is begin if rising_edge(i_clock) then if r_CNT_10HZ = c_CNT_10HZ-1 then r_TOGGLE_10HZ <= not r_TOGGLE_10HZ; r_CNT_10HZ <= 0; else r_CNT_10HZ <= r_CNT_10HZ + 1; end if; end if; end process p_10_HZ; -- 1Hz翻转进程 p_1_HZ : process (i_clock) is begin if rising_edge(i_clock) then if r_CNT_1HZ = c_CNT_1HZ-1 then r_TOGGLE_1HZ <= not r_TOGGLE_1HZ; r_CNT_1HZ <= 0; else r_CNT_1HZ <= r_CNT_1HZ + 1; end if; end if; end process p_1_HZ; -- 基于开关输入的多路复用器 w_LED_SELECT <= r_TOGGLE_100HZ when (i_switch_1 = '0' and i_switch_2 = '0') else r_TOGGLE_50HZ when (i_switch_1 = '0' and i_switch_2 = '1') else r_TOGGLE_10HZ when (i_switch_1 = '1' and i_switch_2 = '0') else r_TOGGLE_1HZ; -- 使能开关门控LED驱动 o_led_drive <= w_LED_SELECT and i_enable; end rtl;
Verilog实现
下方为等效Verilog模块。设计理念相同——四独立always块各在特定频率翻转,开关驱动组合多路复用器。
module tutorial_led_blink ( i_clock, i_enable, i_switch_1, i_switch_2, o_led_drive ); input i_clock; input i_enable; input i_switch_1; input i_switch_2; output o_led_drive; // 频率参数(半周期计数) parameter c_CNT_100HZ = 125000; parameter c_CNT_50HZ = 250000; parameter c_CNT_10HZ = 1250000; parameter c_CNT_1HZ = 12500000; // 计数器寄存器 reg [31:0] r_CNT_100HZ = 0; reg [31:0] r_CNT_50HZ = 0; reg [31:0] r_CNT_10HZ = 0; reg [31:0] r_CNT_1HZ = 0; // 翻转寄存器 reg r_TOGGLE_100HZ = 1'b0; reg r_TOGGLE_50HZ = 1'b0; reg r_TOGGLE_10HZ = 1'b0; reg r_TOGGLE_1HZ = 1'b0; // 多路复用器选择 reg r_LED_SELECT; wire w_LED_SELECT; // 100Hz翻转块 always @(posedge i_clock) begin if (r_CNT_100HZ == c_CNT_100HZ-1) begin r_TOGGLE_100HZ <= ~r_TOGGLE_100HZ; r_CNT_100HZ <= 0; end else r_CNT_100HZ <= r_CNT_100HZ + 1; end // 50Hz翻转块 always @(posedge i_clock) begin if (r_CNT_50HZ == c_CNT_50HZ-1) begin r_TOGGLE_50HZ <= ~r_TOGGLE_50HZ; r_CNT_50HZ <= 0; end else r_CNT_50HZ <= r_CNT_50HZ + 1; end // 10Hz翻转块 always @(posedge i_clock) begin if (r_CNT_10HZ == c_CNT_10HZ-1) begin r_TOGGLE_10HZ <= ~r_TOGGLE_10HZ; r_CNT_10HZ <= 0; end else r_CNT_10HZ <= r_CNT_10HZ + 1; end // 1Hz翻转块 always @(posedge i_clock) begin if (r_CNT_1HZ == c_CNT_1HZ-1) begin r_TOGGLE_1HZ <= ~r_TOGGLE_1HZ; r_CNT_1HZ <= 0; end else r_CNT_1HZ <= r_CNT_1HZ + 1; end // 组合多路复用器 always @(*) begin case ({i_switch_1, i_switch_2}) 2'b11: r_LED_SELECT <= r_TOGGLE_1HZ; 2'b10: r_LED_SELECT <= r_TOGGLE_10HZ; 2'b01: r_LED_SELECT <= r_TOGGLE_50HZ; 2'b00: r_LED_SELECT <= r_TOGGLE_100HZ; endcase end // 使能门控驱动LED assign o_led_drive = r_LED_SELECT & i_enable; endmodule
仿真与验证
编程FPGA前,运行行为仿真(如ModelSim、VCS、Vivado Simulator)。验证各翻转信号符合预期周期,多路复用器按开关组合正确路由信号。早期仿真消除昂贵硬件调试,确认50%占空比维持。
结语
本教程演示如何构建紧凑可复用LED闪烁器,可适配其他定时任务。同一基于计数器的频率合成模式可扩展至更复杂状态机,是FPGA开发者基础技能。
VHDL
- 掌握预防性维护:优化PM计划的策略
- 升级润滑剂分析计划:释放资产可靠性巅峰潜能
- 预防性维护计划优化10大实证策略:从无效到高效的转型指南
- Ultiboard PCB设计全攻略:从工具栏到实战的完整教程
- KiCad PCB设计实战教程:掌握开源EDA全流程开发
- 高效预防性维护程序设计六步法:从人员授权到持续迭代
- FPGA 开发入门:在 Xilinx Artix-7 上构建 4 位加法器
- 嵌入式 FPGA (eFPGA) 技术:革新 ASIC 与 SoC 设计
- C# Hello World — 构建你的第一个C#应用程序
- C# Hello World:构建你的第一个控制台应用程序
- 你的第一个 VHDL 程序:分步 Hello World 教程
- Verilog 入门:数字电路设计完整教程