基于VHDL与FPGA的交通灯控制系统:从任务分析到Quartus II仿真的完整实践
2026/7/14 14:00:17 网站建设 项目流程

1. 项目背景与需求分析

十字路口交通灯控制系统是FPGA初学者的经典练手项目,它完美融合了数字电路设计、状态机控制和实际工程应用。我当年第一次用VHDL实现这个系统时,踩过不少坑,比如数码管显示乱码、状态切换不同步等问题。现在回头看,这个项目确实能让人快速掌握FPGA开发的精髓。

核心需求可以拆解为三个层次:基础功能要求实现东西、南北方向红绿灯的周期性切换(通常设计为50秒循环);进阶功能需要加入两位数码管倒计时显示;高阶功能则要处理紧急车辆通行等异常场景。实际道路中,当救护车或消防车通过时,所有方向必须强制亮红灯,这正是special信号的设计初衷。

硬件接口方面,系统需要:

  • 1个50MHz时钟输入(clk)
  • 1个紧急信号输入(special)
  • 两组数码管控制信号(位选l1/l2,段选display)
  • 两组三色LED控制信号(out_1/out_2)

2. 系统架构设计

2.1 多进程协同机制

整个设计采用三个并行的process实现功能解耦,这比单进程状态机更易维护。我在早期版本曾尝试用单一进程实现,结果代码臃肿且时序难以控制。

**时钟分频进程(reg)**负责将50MHz系统时钟分频为1Hz基准信号。这里有个优化技巧:采用两级计数器(clk1计数10000次,clk2计数5000次)比单级计数更节省资源。实测发现,直接计数50M次会导致综合后占用大量LE(逻辑单元)。

reg:process(clk, special) variable clk1: integer range 0 to 9999; variable clk2: integer range 0 to 4999; begin if rising_edge(clk) then if clk1=9999 then clk1:=0; if clk2=4999 then clk2:=0; if special='0' then change <= not change; counter <= (counter + 1) mod 50; end if; else clk2:=clk2+1; end if; else clk1:=clk1+1; end if; end if; end process;

2.2 状态控制设计

**红绿灯状态机(com进程)**采用自定义枚举类型定义三种状态:

type FSM is (s0_green, s1_yellow, s2_red);

东西方向与南北方向的状态转换存在相位差。例如当东西方向绿灯(s0)时,南北方向必须保持红灯(s2)。状态持续时间通过counter值划分:

  • 绿灯20秒(counter<20)
  • 黄灯5秒(20<=counter<25)
  • 红灯25秒(counter>=25)

特殊情况下(special='1'),所有输出强制为红灯,此时counter停止计数但change信号继续翻转,这是实现数码管闪烁的关键。

3. 数码管驱动实现

3.1 动态扫描原理

两位数码管共用段选信号,通过快速切换位选实现"视觉暂留"效果。在1kHz刷新率下(每位数码管点亮500μs),人眼会认为两者同时亮起。若像仿真时直接使用1Hz时钟,会明显看到交替闪烁。

段选译码采用查表法,将数字转换为7段码:

case c1 is when 0 => display_1<="0111111"; -- 显示0 when 1 => display_1<="0000110"; -- 显示1 ... when others=>display_1<="0000000"; -- 全灭 end case;

3.2 倒计时计算

倒计时数值由当前counter值动态计算。例如东西方向绿灯阶段(counter<20),剩余时间显示为20-counter。这里有个常见陷阱:当数值小于10时,十位数应显示空白而非0。优化后的处理逻辑:

if smg_1 mod 10 = 0 then -- 十位为0时不显示 c1:=10; else c1:=smg_1 mod 10; end if;

4. Quartus II开发全流程

4.1 工程创建与配置

新建工程时需正确选择FPGA型号(如Cyclone IV EP4CE6)。我推荐设置以下编译选项:

  • 开启Optimization Mode为Balanced
  • 设置TimeQuest Timing Analyzer
  • 勾选Generate Netlist for Functional Simulation

引脚分配要根据开发板原理图进行。例如:

  • clk → PIN_23(50MHz晶振)
  • out_1[0] → PIN_45(红色LED)
  • display_1[6:0] → PIN_30~36(数码管段选)

4.2 仿真技巧

为避免长时间等待,仿真时可修改代码将分频系数缩小:

-- 测试用快速分频 if clk1=9 then -- 原为9999 clk1:=0; if clk2=4 then -- 原为4999

ModelSim波形观测要点:

  1. 添加counter、LED_1/2等内部信号到波形窗口
  2. 设置special信号在特定时间置高
  3. 检查状态切换时数码管数值是否连续变化

5. 常见问题解决方案

5.1 编译错误排查

新手常遇到的语法错误包括:

  • 漏写process的begin/end
  • 变量赋值使用":="而信号赋值使用"<="
  • 枚举类型case语句未覆盖所有情况

建议开启Quartus的Messages窗口,双击错误会自动定位代码行。我曾遇到一个隐蔽bug:忘记给integer指定range范围,导致综合后异常。

5.2 功能异常调试

若数码管显示乱码:

  1. 检查段选极性(共阴/共阳)
  2. 确认位选信号切换频率(建议1kHz~10kHz)
  3. 测量各引脚电压是否达到逻辑电平标准

当红绿灯状态不同步时:

  1. 检查counter是否在0~49正常循环
  2. 验证special信号能否正确暂停counter
  3. 确认状态判断条件是否全覆盖(if/elsif/else)

6. 进阶优化方向

6.1 代码重构建议

将数码管译码部分提取为单独函数:

function seg7_decode(num: integer) return std_logic_vector is begin case num is when 0 => return "0111111"; ... end case; end function;

6.2 功能扩展思路

  • 增加传感器接口(如地磁线圈检测车辆)
  • 实现自适应配时(根据车流动态调整绿灯时长)
  • 添加无线通信模块远程控制

这个项目最让我受益的是理解了硬件并行的本质——三个进程就像流水线上的工人,各自独立工作又通过信号协同。后来做更复杂的图像处理项目时,这种模块化思维派上了大用场。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询