Verilog实战:手把手教你用Vivado搭建交通灯控制系统(含数码管倒计时)
Verilog实战从零构建智能交通灯控制系统含数码管动态显示在数字电路设计领域交通灯控制系统是一个经典的FPGA实训项目。这个看似简单的系统实际上融合了状态机设计、时钟分频、数码管动态扫描等多项关键技术。本文将手把手带你用Verilog HDL在Vivado平台上实现一个带倒计时显示的智能交通灯控制器特别适合有一定Verilog基础但想提升实战能力的开发者。1. 系统架构设计1.1 需求分析与模块划分我们的交通灯控制系统需要满足以下核心功能控制东西和南北两个方向的信号灯红、黄、绿实现主干道和支干道不同的通行时间配置提供倒计时显示功能四位数码管黄灯状态实现1Hz闪烁效果基于这些需求我们将系统划分为四个关键模块模块名称功能描述主控制器采用有限状态机(FSM)实现交通灯状态转换逻辑分频计数器将板载高频时钟(如100MHz)分频为1Hz信号定时计数器记录当前状态持续时间触发状态转换数码管译码器将倒计时数值转换为七段数码管显示信号实现动态扫描1.2 状态机设计交通灯控制的核心是一个12状态的有限状态机parameter S04b0000, // 主干道直行绿灯 S14b0001, // 主干道直行黄灯(亮) S24b0010, // 主干道左转绿灯 S34b0011, // 主干道左转黄灯(亮) S44b0100, // 支干道直行绿灯 S54b0101, // 支干道直行黄灯(亮) S64b0110, // 支干道左转绿灯 S74b0111, // 支干道左转黄灯(亮) S84b1000, // 主干道直行黄灯(灭) S94b1001, // 主干道左转黄灯(灭) S104b1010, // 支干道直行黄灯(灭) S114b1011; // 支干道左转黄灯(灭)状态转换逻辑遵循以下时序规则主干道直行绿灯60秒 → 黄灯5秒闪烁主干道左转绿灯40秒 → 黄灯5秒闪烁支干道直行绿灯45秒 → 黄灯5秒闪烁支干道左转绿灯30秒 → 黄灯5秒闪烁2. 关键模块实现2.1 主控制器模块主控制器模块的核心是一个状态机它根据定时计数器的值决定当前应该处于哪个状态always (negedge clk or negedge rst_n) begin if(!rst_n) pre_state S0; else pre_state next_state; end always (clk or pre_state) begin next_state 3bxxx; if(count60 count65) begin if(clk_5hz) next_state S8; // 黄灯亮 else next_state S1; // 黄灯灭 end // 其他状态转换逻辑... end每个状态对应特定的信号灯输出组合always (pre_state) begin case (pre_state) S0: begin ew6b001100; sn6b001001; end // 主干道直行绿支干道红 S1: begin ew6b001010; sn6b001001; end // 主干道直行黄支干道红 // 其他状态输出... endcase end2.2 分频计数器实现系统需要将高频时钟分频为1Hz信号用于计时以及更高频率的信号用于数码管动态扫描module countdiv ( input clk, rst_n, output reg clk_1hz 1b0 ); reg [12:0] count_clk 13b0; always (negedge clk) begin count_clk count_clk 1b1; if(count_clk d499) begin clk_1hz !clk_1hz; count_clk d0; end end endmodule提示分频系数需要根据实际板载时钟频率调整。例如对于100MHz时钟1Hz信号需要50,000,000分频。2.3 数码管动态显示四位数码管显示采用动态扫描技术关键点包括位选控制快速切换显示位利用视觉暂留效应段码译码将数字转换为七段码倒计时计算根据当前状态计算剩余时间// 数码管译码表 parameter NUM_07h7e, NUM_17h30, NUM_27h6d, NUM_37h79, NUM_47h33, NUM_57h5b, NUM_67h5f, NUM_77h70, NUM_87h7f, NUM_97h7b, NUM_107h00; // 不显示 always (negedge clk) begin if(!rst_n) num_main d0; else if(count 8d59) num_main 60 - count; // 主干道直行倒计时 // 其他状态倒计时计算... end3. Vivado工程实现3.1 顶层模块设计顶层模块将各个子模块连接起来module top ( input clk, rst_n, en, output [5:0] ew, sn, output [6:0] out_main, out_minor, output [1:0] sel_main, sel_minor ); wire [7:0] count; wire clk_1hz; countdiv U0(.clk(clk), .rst_n(rst_n), .clk_1hz(clk_1hz)); counter U1(.clk(clk_1hz), .rst_n(rst_n), .out(count), .en(en)); traffic_light U2(.clk(clk), .rst_n(rst_n), .count(count), .ew(ew), .sn(sn)); tubeControl U3(.clk(clk), .clk_1hz(clk_1hz), .rst_n(rst_n), .count(count), .out_main(out_main), .out_minor(out_minor), .sel_main(sel_main), .sel_minor(sel_minor)); endmodule3.2 引脚约束文件正确的引脚约束对FPGA实现至关重要。以下是Xilinx Artix-7系列FPGA的约束示例set_property PACKAGE_PIN E3 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property PACKAGE_PIN K5 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] # 信号灯输出引脚约束 set_property PACKAGE_PIN M15 [get_ports {ew[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {ew[2]}] # 数码管段选引脚约束 set_property PACKAGE_PIN D3 [get_ports {out_main[6]}] set_property IOSTANDARD LVCMOS33 [get_ports {out_main[6]}]4. 调试技巧与常见问题4.1 仿真测试方法在Vivado中创建测试激励文件module top_tb; reg clk, rst_n, en; // 实例化顶层模块 top U1(.clk(clk), .rst_n(rst_n), .en(en), ...); initial begin clk 1d0; forever #500 clk ~clk; // 1kHz时钟 end initial begin rst_n 1b1; en 1b0; #1 rst_n 1b0; // 复位 #1 rst_n 1b1; en 1b1; // 开始运行 end endmodule4.2 常见问题解决数码管显示闪烁或不稳定检查动态扫描频率推荐200Hz以上确保位选信号切换与段码更新同步状态机卡在某个状态检查定时计数器是否正常递增验证状态转换条件是否覆盖所有情况黄灯闪烁不正常确认分频时钟信号质量检查状态机在黄灯状态的切换逻辑注意实际调试时建议使用Vivado的ILA集成逻辑分析仪抓取内部信号波形这是定位时序问题的利器。5. 功能扩展与优化完成基础功能后可以考虑以下增强功能自适应时序调整根据交通流量动态调整绿灯时长夜间模式在车流量少时切换为黄灯闪烁模式远程监控通过UART接口上传交通灯状态紧急车辆优先加入外部中断响应机制// 紧急模式实现示例 always (posedge emergency) begin if(emergency) begin next_state EMERGENCY_MODE; // 所有方向红灯特定方向绿灯 end end在FPGA资源允许的情况下还可以考虑增加更多方向的交通灯控制实现行人过街请求按钮功能添加故障自检测机制这个交通灯控制系统项目虽然基础但涵盖了FPGA开发的多个关键技术点。通过这个实践你不仅能掌握Verilog的状态机设计和时序控制还能深入理解数字系统从设计到实现的完整流程。当看到自己设计的交通灯在开发板上按照预期运行时那种成就感正是电子设计的魅力所在。