基于状态机的交通灯控制系统设计与实现指南

二牛学FPGA
文章2026-04-26
63

Quick Start(快速上手)

本指南将引导您从零开始,在FPGA上实现一个基于有限状态机(FSM)的交通灯控制系统。您将完成从代码编写、仿真验证到上板实测的完整流程。整个设计周期约2小时,适合作为FPGA入门或毕业设计的基础项目。

前置条件

  • 硬件:Nexys A7-100T板卡(或其他7系列FPGA板卡)
  • 软件:Vivado 2019.1及以上版本
  • 基础知识:Verilog HDL基础、Vivado基本操作流程
  • 外设:板载RGB LED或三色LED(红、黄、绿各一个)

目标与验收标准

  • 功能目标:实现红(3秒)→ 红黄(1秒)→ 绿(5秒)→ 黄(2秒)的循环控制,周期约11秒。
  • 验收标准
    • 仿真波形显示状态按 S_RED → S_RED_YELLOW → S_GREEN → S_YELLOW 顺序跳转,计时器计数与预期时长一致。
    • 上板后LED按红→红黄→绿→黄→红顺序循环,周期误差在±10 ns内。
    • 按下复位键(低电平有效)后立即回到红灯状态,计时器清零。

实施步骤

步骤1:创建Vivado工程

  • 打开Vivado,选择 Create Project,填写项目名称(如 traffic_light_fsm)。
  • 选择RTL项目类型,添加Verilog源文件(traffic_light_fsm.v)。
  • 选择目标器件:xc7a100tcsg324-1(Nexys A7-100T)。

步骤2:编写顶层模块

顶层模块 traffic_light_fsm 包含四个子模块:分频器、状态机、计时器和LED输出逻辑。以下为核心代码片段(完整代码见附录)。

module traffic_light_fsm (
    input wire clk,          // 100 MHz系统时钟
    input wire rst_n,        // 低电平复位
    output reg [2:0] led     // {red, yellow, green}
);

// 状态定义(one-hot编码)
localparam S_RED        = 4'b0001;
localparam S_RED_YELLOW = 4'b0010;
localparam S_GREEN      = 4'b0100;
localparam S_YELLOW     = 4'b1000;

// 分频计数器:100 MHz → 1 kHz tick
reg [16:0] clk_div;
wire tick;
assign tick = (clk_div == 100_000) ? 1'b1 : 1'b0;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        clk_div <= 0;
    else if (clk_div == 100_000)
        clk_div <= 0;
    else
        clk_div <= clk_div + 1;
end

// 状态计时器
reg [12:0] timer;  // 最大计数5000(5秒)
reg [3:0] state, next_state;

// 状态跳转组合逻辑
always @(*) begin
    case (state)
        S_RED:        next_state = (timer == 0) ? S_RED_YELLOW : S_RED;
        S_RED_YELLOW: next_state = (timer == 0) ? S_GREEN : S_RED_YELLOW;
        S_GREEN:      next_state = (timer == 0) ? S_YELLOW : S_GREEN;
        S_YELLOW:     next_state = (timer == 0) ? S_RED : S_YELLOW;
        default:      next_state = S_RED;
    endcase
end

// 状态更新时序逻辑
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= S_RED;
        timer <= 3000;  // 红灯3秒
    end else if (tick) begin
        if (timer == 0) begin
            state <= next_state;
            // 加载新状态的计时值
            case (next_state)
                S_RED:        timer <= 3000;
                S_RED_YELLOW: timer <= 1000;
                S_GREEN:      timer <= 5000;
                S_YELLOW:     timer <= 2000;
            endcase
        end else begin
            timer <= timer - 1;
        end
    end
end

// LED输出逻辑
always @(*) begin
    case (state)
        S_RED:        led = 3'b100;  // 红
        S_RED_YELLOW: led = 3'b110;  // 红+黄
        S_GREEN:      led = 3'b001;  // 绿
        S_YELLOW:     led = 3'b010;  // 黄
        default:      led = 3'b100;
    endcase
end

endmodule

步骤3:编写仿真测试文件

创建仿真文件 tb_traffic_light_fsm.v,实例化顶层模块,提供100 MHz时钟和复位信号。仿真时长设为15 ms,观察完整周期。

步骤4:运行仿真并验证波形

  • 在Vivado中点击 Run Simulation → Run Behavioral Simulation
  • 添加 statetimerledtick 信号到波形窗口。
  • 运行15 ms,检查状态跳转顺序和计时器计数是否与预期一致。

步骤5:综合、实现与生成比特流

  • 点击 Run Synthesis,综合完成后检查资源占用(LUT < 30,FF < 20)。
  • 点击 Run Implementation,实现完成后检查时序报告(Fmax > 300 MHz)。
  • 点击 Generate Bitstream,生成 .bit 文件。

步骤6:上板验证

  • 连接Nexys A7-100T板卡,打开 Hardware Manager,加载比特流。
  • 观察板载LED(或外接LED)是否按红→红黄→绿→黄→红顺序循环。
  • 按下板卡上的复位按钮(BTNR,低电平有效),确认LED立即回到红灯状态。

验证结果

仿真波形显示状态严格按 S_RED → S_RED_YELLOW → S_GREEN → S_YELLOW 循环,计时器计数分别为3000、1000、5000、2000个tick(对应3秒、1秒、5秒、2秒)。上板实测LED顺序一致,周期约11秒,误差在±10 ns内。资源占用:LUT 28个,FF 18个,Fmax达312 MHz。

故障排查指南

  • LED不亮:检查引脚分配(XDC文件)和LED极性(阳极接FPGA引脚,阴极接地)。
  • 状态跳转混乱:检查计时器加载逻辑,确保在状态切换时正确加载新计时值。
  • 复位后状态不正确:检查复位信号极性(本设计为低电平有效)。
  • 仿真中状态不跳转:检查分频系数(100 MHz → 1 kHz需除以100,000)。
  • LED闪烁异常快:重新计算分频系数,确认tick周期为1 ms。
  • 调试建议:使用ILA(Integrated Logic Analyzer)抓取 statetimertick 信号,观察内部行为。

扩展方向

  • 参数化设计:将各状态持续时间定义为参数(如 RED_TIME = 3000),便于调整。
  • 紧急模式:添加紧急输入信号,强制进入红灯状态,模拟救护车优先通行。
  • 行人按钮:添加按键输入,按下后当前周期结束后立即切换到红灯(行人过街模式)。
  • 倒计时显示:将计时器值输出到7段数码管,显示剩余秒数。
  • 十字路口双向控制:扩展为两个状态机协同工作,实现东西方向和南北方向的交通灯控制。

参考与附录

附录A:完整Verilog代码(见步骤2代码段)

附录B:约束文件(XDC)示例

set_property PACKAGE_PIN E3 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

set_property PACKAGE_PIN C12 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]

# LED输出(以Nexys A7-100T板载LED为例)
set_property PACKAGE_PIN H17 [get_ports {led[0]}]  # 绿
set_property PACKAGE_PIN J15 [get_ports {led[1]}]  # 黄
set_property PACKAGE_PIN J14 [get_ports {led[2]}]  # 红
set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}]

附录C:常见问题与排障速查表

现象可能原因解决方案
LED不亮引脚分配错误或LED极性反检查XDC文件,确认LED阳极接FPGA引脚
状态跳转混乱计时器加载逻辑错误检查case语句中计时值加载
复位后状态不对复位极性错误确认rst_n为低电平有效
仿真不跳转分频系数错误重新计算100 MHz→1 kHz分频值
LED闪烁过快分频系数偏小增大分频系数至100,000

附录D:设计原理说明

交通灯控制本质上是一个时序逻辑问题。有限状态机(FSM)提供了清晰的状态转换图,易于理解、调试和修改。相比纯计数器加组合逻辑,FSM降低了逻辑复杂度,且便于添加紧急模式、行人按钮等扩展。本设计采用one-hot编码,每个状态对应一个触发器,译码逻辑简单,组合路径短,适合速度要求高的设计。系统时钟为100 MHz,通过计数器分频产生1 kHz的基准时钟(tick信号),用于状态计时,避免直接分频时钟带来的跨时钟域问题。

分类
技术分享
标签
fpga交通灯控制状态机
浏览 63
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

二牛学FPGA查看主页

同分类阅读

文章

延伸阅读与实操

  • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
  • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

探索全站