EDA实验(Quartus Ⅱ+fpga) (四)---基于状态机的智能交通灯控制器设计
1. 智能交通灯控制器的设计背景十字路口的交通管理一直是城市基础设施中的重要环节。传统交通灯采用固定时序控制无法根据实时车流量调整信号灯时长容易造成交通资源浪费。这次我们要用FPGA和状态机设计一个能自动感应车辆的智能交通灯控制器让乡间公路有车时才放行没车时保持主干道畅通。我在实际项目中遇到过这样的场景郊区主干道车流量大但相邻的乡间公路可能几十分钟才有一辆车经过。如果让两条路机械地轮流放行主干道司机会非常不满。这个设计就是要用Verilog状态机解决这个问题通过传感器信号动态调整路权分配。2. 状态机设计原理2.1 状态定义与转移条件这个交通灯控制器需要管理六盏灯主干道红黄绿MR/MY/MG和乡道红黄绿CR/CY/CG。我将其划分为四个核心状态MGCR状态默认状态主干道绿灯MG1乡道红灯CR1MYCR状态过渡状态主干道黄灯MY1乡道红灯CR1MRCG状态切换状态主干道红灯MR1乡道绿灯CG1MRCY状态过渡状态主干道红灯MR1乡道黄灯CY1状态转移由三个关键条件触发传感器信号S1乡道有车主干道最小通行时间60s乡道最大通行时间20s2.2 状态转移图实现用Verilog代码实现时我习惯先画出状态转移图。这个设计的状态转移逻辑是MGCR --[S1且计时≥60s]-- MYCR --[黄灯4s]-- MRCG MRCG --[S0或计时≥20s]-- MRCY --[黄灯4s]-- MGCR实际编码时要注意从MGCR到MYCR必须满足两个条件有车请求主干道已通行60s而从MRCG到MRCY只需满足任一条件无车请求或已达20s。这个细节直接影响交通效率。3. Quartus II工程实现3.1 模块接口设计顶层模块的输入输出定义如下module jiaotongdeng( input clk, // 50MHz时钟 input s, // 乡道车辆传感器 input rst, // 复位信号 output reg MG,CR,MY,MR,CG,CY, // 六盏交通灯 output [6:0] SG0,SG1, // 两位数码管 output [5:0] c // LED状态指示 );特别说明我增加了数码管输出SG0/SG1用于显示倒计时c[5:0]信号用于调试时观察灯状态。实际部署时可以精简。3.2 时钟分频处理FPGA板载时钟通常是50MHz需要分频得到1Hz信号用于计时。这里给出两种方案仿真用分频快速验证always (posedge clk) begin cnt cnt 1; if(cnt 1) begin // 每2个时钟周期翻转 clk1 1b1; cnt 0; end else clk1 1b0; end实际硬件分频always (posedge clk) begin cnt cnt 1; if(cnt 49999999) begin // 50MHz→1Hz clk1 1b1; cnt 0; end else clk1 1b0; end调试时先用仿真分频快速验证逻辑硬件测试时再切换为实际分频。这个技巧能节省大量调试时间。3.3 状态机核心代码状态寄存器和次态逻辑是设计的核心parameter s00, s11, s22, s33; // 状态编码 reg [1:0] cs, next_state; // 状态转移逻辑 always(cs) case(cs) s0: if(tim0 s1) next_state s1; // MGCR→MYCR s1: if(tim0) next_state s2; // MYCR→MRCG s2: if(tim0 || s0) next_state s3; // MRCG→MRCY s3: if(tim0) next_state s0; // MRCY→MGCR default: next_state s0; endcase // 状态寄存器更新 always(posedge clk1 or negedge rst) if(!rst) cs s0; else cs next_state;这里有个优化点状态判断使用tim0而非具体计数值这样修改各状态时长时只需改参数定义不用改动状态机逻辑。4. 计时器与灯控实现4.1 倒计时模块设计每个状态需要独立的计时器reg [7:0] timCG8d25, timMG8d89, timY8d3; // 25s,89s,3s reg [7:0] tim; // 当前倒计时值 always(negedge clk1) begin case(cs) s0: if(timMG0) begin // MGCR状态计时 timMG timMG - 1; tim timMG; end else begin tim 0; timMG 8d89; // 重置计时器 end // 其他状态类似... endcase end注意这里用89秒而非直接写60秒是因为代码中timMG[7:0]是BCD码格式89对应十六进制0x59即十进制59秒开始倒计时。4.2 灯控信号生成根据当前状态输出灯信号always(negedge clk1) case(cs) s0: begin MY0; MG1; MR0; CG0; CY0; CR1; end s1: begin MY1; MG0; MR0; CG0; CY0; CR1; end s2: begin MY0; MG0; MR1; CG1; CY0; CR0; end s3: begin MY0; MG0; MR1; CG0; CY1; CR0; end endcase建议为每个输出信号添加寄存器缓冲避免产生毛刺。我曾经因为省略寄存器导致LED出现微光闪烁排查了很久才发现是组合逻辑的竞争冒险。5. 数码管显示优化5.1 BCD码转换将倒计时值转换为数码管显示assign gw tim[3:0]; // 个位 assign sw tim[7:4]; // 十位 // 七段译码 always(posedge clk1) begin case(gw) 0:SG07b1000000; 1:SG07b1111001; // 0-9编码 // ...其他数字 endcase case(sw) 0:SG17b1000000; 1:SG17b1111001; // ...其他数字 endcase end5.2 显示消抖处理实测中发现数码管在数值变化时会出现残影后来增加了显示锁存解决了这个问题reg [6:0] SG0_reg, SG1_reg; always(posedge clk1) begin SG0 SG0_reg; SG1 SG1_reg; end6. 调试经验与技巧6.1 SignalTap II逻辑分析仪在Quartus II中使用SignalTap II抓取内部信号新建.stp文件添加要观察的信号如cs、tim等设置采样时钟为clk11Hz触发条件设置为rst下降沿下载到FPGA后实时查看状态变化这个方法帮我发现了状态机在MYCR状态停留时间不足的问题原来是timY的初始值设错了。6.2 功能仿真要点编写Testbench时注意这些关键场景initial begin rst0; s0; #100 rst1; // 复位测试 #600000 s1; // 60s后触发乡道请求 #200000 s0; // 20s后取消请求 end仿真时要重点检查复位后是否进入MGCR状态60秒内S1是否被忽略黄灯持续时间是否严格4秒乡道通行超20秒是否强制退出7. 硬件部署注意事项7.1 管脚分配策略根据原理图分配管脚时要注意时钟信号分配到专用时钟管脚如AF14数码管段选信号接GPIO驱动能力强的管脚LED输出添加330Ω限流电阻实际项目中我曾因忘记加限流电阻烧毁过LED教训深刻。7.2 电源滤波处理在FPGA电源引脚附近放置0.1μF去耦电容每对VCC/GND引脚一个电容尽量靠近FPGA引脚放置使用多层陶瓷电容(MLCC)这个细节能有效避免随机复位问题特别是当交通灯状态快速切换时。8. 扩展功能建议8.1 紧急车辆优先通行可增加紧急信号输入input emergency; // 高电平有效 // 修改状态转移条件 if(emergency) next_state s3; // 强制进入黄灯过渡8.2 车流量自适应控制通过统计传感器触发次数动态调整时长reg [7:0] car_count; always (posedge s) car_count car_count 1; // 根据车流量计算绿灯时间 assign timMG (car_count 10) ? 8d99 : 8d59;这种设计在真实智能交通系统中很常见不过需要更复杂的算法支持。