亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> VHDL

手把手教你构建首个 FPGA LED 闪烁器:分步教程

分步指南:构建你的第一个 FPGA LED 闪烁器

第一部分:设计 VHDL 或 Verilog 代码

本教程将教你编写 VHDL 和 Verilog 代码,以用户定义频率驱动 LED。根据工作流选择适合的语言。

编写 HDL 时必须验证设计行为是否符合预期。错误难免,因此仿真必不可少。教程分为两个关键阶段:

  1. HDL 设计
  2. HDL 仿真

跳过仿真会导致昂贵的上板调试。请将仿真视为强制检查点。

项目需求

编写 HDL 使 LED 以 100 Hz、50 Hz、10 Hz 或 1 Hz、占空比 50% 闪烁。两个拨码开关选择频率,额外的 LED_EN 开关置高电平才启用 LED。FPGA 运行在 25 MHz 晶振时钟下。

频率选择真值表:

使能开关 1开关 2LED 驱动频率
0––禁用
100100 Hz
10150 Hz
11010 Hz
1111 Hz

信号汇总:

信号名方向描述
i_clock输入25 MHz 时钟
i_enable输入使能开关(逻辑 0 = LED 关)
i_switch_1输入频率选择开关 1
i_switch_2输入频率选择开关 2
o_led_drive输出LED 驱动信号

四个并行计数器进程监控 25 MHz 时钟,为各目标频率产生翻转信号。即使某频率未被选中,其计数器仍持续运行——这是硬件并发的核心原则。

拨码开关构成多路选择器,将选中的翻转信号路由至 LED 输出。多路选择器属纯组合逻辑,无需时钟。

下图展示架构框图:

手把手教你构建首个 FPGA LED 闪烁器:分步教程

VHDL 实现

 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 -- 生成所需频率的常量: -- 公式:(25 MHz / 目标频率 * 50% 占空比) -- 100 Hz: 25,000,000 / 100 * 0.5 = 125,000 constant c_CNT_100HZ : natural := 125000; constant c_CNT_50HZ : natural := 250000; constant c_CNT_10HZ : natural := 1250000; constant c_CNT_1HZ : natural := 12500000; -- 计数器信号 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 -- 所有进程在各自频率翻转特定信号,持续运行 p_100_HZ : process (i_clock) is begin if rising_edge(i_clock) then if r_CNT_100HZ = c_CNT_100HZ-1 then -- -1 因计数器从 0 开始 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; 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; 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; 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; -- 仅当 i_enable 为高时允许 o_led_drive 驱动(与门)。 o_led_drive <= w_LED_SELECT and i_enable; end rtl; 

Verilog 实现

 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; // 常量(参数)生成所需频率: // 输入时钟 25 kHz(任意选取)。 // 公式:(25 kHz / 100 Hz * 50% 占空比) // 100 Hz: 25,000 / 100 * 0.5 = 125 parameter c_CNT_100HZ = 125; parameter c_CNT_50HZ = 250; parameter c_CNT_10HZ = 1250; parameter c_CNT_1HZ = 12500; // 计数器信号 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; begin // 所有 always 块在各自频率翻转特定信号,持续运行 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 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 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 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 assign o_led_drive = r_LED_SELECT & i_enable; // 替代多路选择器写法(功能相同,但可读性稍差) // assign w_LED_SELECT = i_switch_1 ? (i_switch_2 ? r_TOGGLE_1HZ : r_TOGGLE_10HZ) : //(i_switch_2 ? r_TOGGLE_50HZ : r_TOGGLE_100HZ); // assign o_led_drive = w_LED_SELECT & i_enable; end endmodule 

下一步:在 VHDL 或 Verilog 中仿真该设计,确认行为正确后再部署。

VHDL

  1. ModelSim 仿真中查看 VHDL 变量的完整技巧
  2. 用 OSVVM 掌握约束随机验证
  3. 精通 VHDL Wait 语句:Wait On、Wait Until 与组合用法
  4. VHDL基础测验第4部分:验证你的理解
  5. VHDL过程示例:标准逻辑向量自增
  6. 善用VHDL记录类型构建整洁可复用FIFO接口
  7. 掌握 VHDL Loop 与 Exit 构造:实用指南
  8. 在 VHDL 进程中使用敏感度列表实现可靠的 RTL 设计
  9. 优雅结束 VHDL 测试平台仿真
  10. 使用VHDL不纯函数提升FSM可读性与可维护性
  11. 在块RAM中设计健壮的VHDL环形缓冲区FIFO
  12. 在Linux VPS上用Jenkins、Vivado、GitHub自动化FPGA开发