尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
QuartusⅡ交通灯控制电路设计:状态机与Verilog实现
简介这份资源面向数字逻辑电路设计的学习者与课程实践者提供一套基于QuartusⅡ的十字路口交通灯控制电路完整工程。设计涵盖甲车道主道75秒、乙车道辅道25秒的交替通行逻辑黄灯先亮3秒再切换车道且黄灯期间每秒闪亮一次并支持按键进行模式切换适合作为数字系统设计、芯片仿真类实验或课程设计的参考方案。压缩包共167个文件约2.42MB以bdf原理图、vwf波形文件、bsf符号文件、cdb与hdb编译数据库、qpf与qsf工程配置、sof下载文件及rpt报告等为主完整保留了从原理图绘制、功能仿真到综合下载的工程链路。目前已有3293人学习下载读者可借此理清交通灯状态机与计时模块的划分思路对照波形验证黄灯闪烁与车道切换时序并参考工程目录组织方式完成自己的仿真与排错。1. 从一次数字逻辑课设翻车说起为什么交通灯控制电路值得认真做一遍带过多届数字逻辑课程设计我发现一个规律选交通灯控制电路的人最多但真正能把方案讲清楚、把仿真跑通、把验收点说明白的人最少。多数人卡在同一个地方——状态机设计得模棱两可仿真波形对不上答辩时被问一句“黄灯那两秒怎么来的”就答不上来。这份 QuartusⅡ 交通灯控制电路方案解决的就是从需求定义到芯片仿真验证的完整链路问题。它适合正在做数字逻辑电路设计课设的学生也适合需要快速搭一套可演示控制逻辑的从业者。核心不是画个红黄绿三色灯而是把定时、状态切换、紧急模式、倒计时显示这几个模块用可综合的 Verilog 串起来并在 QuartusⅡ 里跑通功能仿真和时序约束。2. 需求拆解与方案选型为什么用状态机加计数器的组合2.1 从路口实际时序反推功能清单交通灯控制电路的需求看起来简单但真正落到数字逻辑层面需要拆成可量化的功能点。我一般会先画一张路口时序图把主干道和支路两个方向的灯态变化按时间轴铺开。一个标准的十字路口主干道绿灯 30 秒、黄灯 3 秒、红灯 33 秒支路绿灯 25 秒、黄灯 3 秒、红灯 38 秒两个方向存在重叠等待区。这些时间参数不能拍脑袋定要跟倒计时显示模块的位宽对齐——30 秒需要 5 位二进制计数器3 秒需要 2 位如果统一用 6 位计数器高位补零逻辑就要在显示译码前处理掉。功能清单通常包括这几项正常模式下的四状态循环主绿、主黄、支绿、支黄、每个状态的持续时间可参数化、倒计时数码管实时显示剩余秒数、紧急模式下手动强制全红、复位后从主干道绿灯重新开始。其中紧急模式最容易被忽略但答辩时导师往往盯着这个问因为它考察的是你对状态机优先级和异步复位的理解深度。2.2 状态机与计数器分工的设计逻辑选型上我倾向于用 Moore 型有限状态机做灯态控制用独立的分频计数器做秒脉冲生成。为什么不把定时和状态切换揉在一起血泪经验一旦揉在一起仿真波形里状态跳转和计时器归零的时序关系就会变成玄学你根本说不清是状态先变还是计数器先清零。分开之后状态机只负责“当前该亮什么灯”计数器只负责“这个灯亮了多久”两者通过一个使能信号握手。具体来说状态机定义四个状态S0 主绿、S1 主黄、S2 支绿、S3 支黄。每个状态输出对应的灯控信号和该状态的持续时间预设值。计数器模块接收预设值在每个秒脉冲到来时递减减到零时输出一个 done 脉冲给状态机触发状态跳转。这样设计的好处是修改绿灯时长只需要改预设值参数状态机逻辑完全不用动。// 状态机核心片段Moore型输出仅与当前状态有关 localparam S0_MAIN_GREEN 2b00, S1_MAIN_YELLOW 2b01, S2_SIDE_GREEN 2b10, S3_SIDE_YELLOW 2b11; reg [1:0] current_state, next_state; reg [5:0] duration; // 当前状态持续秒数 always (posedge clk or negedge rst_n) begin if (!rst_n) current_state S0_MAIN_GREEN; else if (tick_done) current_state next_state; end always (*) begin case (current_state) S0_MAIN_GREEN: begin next_state S1_MAIN_YELLOW; duration 6d30; // 主干道绿灯30秒 end S1_MAIN_YELLOW: begin next_state S2_SIDE_GREEN; duration 6d3; end // S2、S3 同理略 endcase end这段代码的关键在于tick_done信号它来自计数器模块的借位输出。duration在每个状态进入时被锁存到计数器计数器从该值递减到 1 时产生一个周期的tick_done高电平。注意duration用组合逻辑赋值综合工具会推断出多路选择器如果时序紧张可以改成时序逻辑打一拍。2.3 分频与秒脉冲生成的参数计算QuartusⅡ 工程通常用 50MHz 板载晶振要得到 1Hz 秒脉冲分频系数是 25,000,000。这个数需要 25 位计数器来承载。我一般写一个参数化分频模块把分频系数作为 parameter 传入这样仿真时可以把系数改小到 10 甚至 5波形跑得快不用在仿真器里等几百万个周期。module clk_divider #(parameter DIV 25_000_000) ( input clk, input rst_n, output reg tick // 1Hz 秒脉冲 ); reg [24:0] cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 0; tick 0; end else if (cnt DIV - 1) begin cnt 0; tick 1; end else begin cnt cnt 1; tick 0; end end endmodule仿真时把DIV改成 10tick每 10 个时钟周期拉高一次状态跳转在波形里一目了然。这里有个踩坑点tick只拉高一个时钟周期如果状态机用tick做时钟沿会引入跨时钟域问题。正确做法是把tick当作使能信号状态机仍然用系统时钟在tick有效时判断是否跳转。3. 总体架构设计四个模块的职责划分与数据流3.1 模块划分与端口定义整个设计拆成四个模块分频模块clk_divider、状态机模块traffic_fsm、倒计时计数器模块countdown、数码管译码模块seg_decoder。顶层模块traffic_top只做例化和连线不写任何逻辑。这种划分方式的好处是每个模块可以独立仿真出问题能快速定位是哪个环节的波形不对。端口定义上顶层输入只有clk、rst_n、emergency三个信号输出是main_light[2:0]、side_light[2:0]和seg_data[7:0]、seg_sel[1:0]。灯控信号用独热码编码3b001表示绿灯3b010表示黄灯3b100表示红灯。倒计时显示用两个数码管分别显示主干道和支路的剩余秒数位选信号seg_sel做动态扫描。模块名输入输出功能clk_dividerclk, rst_ntick50MHz 分频到 1Hztraffic_fsmclk, rst_n, tick, emergencymain_light, side_light, duration状态跳转与灯控countdownclk, rst_n, tick, load, durationcount, done递减计数与借位seg_decoderclk, count_main, count_sideseg_data, seg_sel动态扫描译码3.2 状态机与计数器的握手时序状态机和计数器的握手是整个设计里最容易出问题的地方。我一般会画一张时序图状态机在tick上升沿判断done是否有效如果有效则跳转到下一状态并输出新的duration计数器在下一个tick到来时加载新的duration并开始递减。这里有一个周期的延迟意味着实际绿灯时间是 30 秒加一个时钟周期对于秒级应用完全可以忽略。紧急模式的处理需要特别小心。emergency信号有效时状态机强制跳转到全红状态同时计数器停止递减。emergency释放后状态机从主干道绿灯重新开始而不是回到之前的状态。这个设计决策是为了避免紧急解除后两个方向同时放行的危险情况。代码里用一个优先级最高的条件分支实现always (posedge clk or negedge rst_n) begin if (!rst_n) current_state S0_MAIN_GREEN; else if (emergency) current_state S_EMERGENCY; // 全红 else if (tick done) current_state next_state; end注意emergency是异步信号直接进状态机可能产生亚稳态。通常的做法是加两级同步器再使用但在课设场景下如果按键已经做了消抖直接使用也能通过功能仿真。3.3 倒计时显示的动态扫描策略两个数码管显示剩余秒数如果直接用静态驱动需要 14 根 IO用动态扫描只需要 8 根段选加 2 根位选。扫描频率一般取 1kHz人眼看不到闪烁。扫描计数器用 50MHz 时钟分频到 1kHz每次扫描切换一位数码管段选数据根据当前显示的位从count_main或count_side中取对应位。// 动态扫描1kHz 切换位选 always (posedge clk or negedge rst_n) begin if (!rst_n) scan_cnt 0; else if (scan_cnt 50_000) scan_cnt 0; // 50MHz/50k 1kHz else scan_cnt scan_cnt 1; end always (*) begin case (scan_cnt[15]) // 用高位做位选切换 1b0: begin seg_sel 2b01; seg_data seg_main; end 1b1: begin seg_sel 2b10; seg_data seg_side; end endcase end这里有个细节count_main和count_side是二进制数需要先转成 BCD 码再送七段译码。我一般写一个bin2bcd模块用 Double Dabble 算法把 6 位二进制转成两个 BCD 位。如果嫌麻烦也可以直接在计数器里用 BCD 计数每减到 0 时从 9 借位但这样计数器逻辑会复杂一些。4. 关键模块实现从 Verilog 代码到 QuartusⅡ 工程配置4.1 状态机模块的完整实现与参数化状态机模块的完整实现需要处理四个正常状态加一个紧急状态。每个状态的输出包括主灯和支灯的独热码以及该状态的持续时间。为了参数化我把四个持续时间定义成 localparam放在模块头部改时间只需要改这四个数。module traffic_fsm #( parameter MAIN_GREEN_TIME 6d30, parameter MAIN_YELLOW_TIME 6d3, parameter SIDE_GREEN_TIME 6d25, parameter SIDE_YELLOW_TIME 6d3 )( input clk, rst_n, tick, emergency, output reg [2:0] main_light, side_light, output reg [5:0] duration, output reg done ); // 状态编码 localparam S0 3d0, S1 3d1, S2 3d2, S3 3d3, S4 3d4; reg [2:0] state, next_state; reg [5:0] cnt; // 内部计数器 // 状态跳转与计时 always (posedge clk or negedge rst_n) begin if (!rst_n) begin state S0; cnt MAIN_GREEN_TIME; end else if (emergency) begin state S4; cnt 0; end else if (tick) begin if (cnt 1) begin state next_state; // 加载下一状态的时间 case (next_state) S0: cnt MAIN_GREEN_TIME; S1: cnt MAIN_YELLOW_TIME; S2: cnt SIDE_GREEN_TIME; S3: cnt SIDE_YELLOW_TIME; default: cnt MAIN_GREEN_TIME; endcase end else begin cnt cnt - 1; end end end // 下一状态组合逻辑 always (*) begin case (state) S0: next_state S1; S1: next_state S2; S2: next_state S3; S3: next_state S0; default: next_state S0; endcase end // 输出逻辑 always (*) begin case (state) S0: begin main_light 3b001; side_light 3b100; end S1: begin main_light 3b010; side_light 3b100; end S2: begin main_light 3b100; side_light 3b001; end S3: begin main_light 3b100; side_light 3b010; end S4: begin main_light 3b100; side_light 3b100; end default: begin main_light 3b100; side_light 3b100; end endcase end assign duration cnt; assign done (cnt 1) ? 1b1 : 1b0; endmodule这段代码把计数器和状态机合并了好处是省了一个模块的握手信号坏处是状态机模块变重了。如果课设要求模块化程度高可以把计数器拆出去用load和done握手。两种方式都能跑通看导师偏好。4.2 数码管译码与 BCD 转换七段数码管的译码表是固定的共阴极和共阳极的段码不同。QuartusⅡ 工程里通常用共阳极数码管段码低电平有效。0 到 9 的段码分别是8hc0, 8hf9, 8ha4, 8hb0, 8h99, 8h92, 8h82, 8hf8, 8h80, 8h90。这个表可以直接用 case 语句实现。BCD 转换用 Double Dabble 算法核心思想是每左移一位如果某个 BCD 位大于等于 5 就加 3。对于 6 位二进制数最多需要 6 次移位产生两个 BCD 位。代码不复杂但要注意移位次数和位宽对齐。// 6位二进制转BCDDouble Dabble算法 function [11:0] bin2bcd; input [5:0] bin; integer i; reg [11:0] bcd; begin bcd 12d0; bcd[5:0] bin; for (i 0; i 6; i i 1) begin if (bcd[9:6] 5) bcd[9:6] bcd[9:6] 3; if (bcd[13:10] 5) bcd[13:10] bcd[13:10] 3; bcd bcd 1; end bin2bcd bcd; end endfunction这个函数在综合时会被展开成组合逻辑资源消耗不大。如果倒计时最大 30 秒6 位二进制足够转换结果取低 8 位就是两个 BCD 码。4.3 QuartusⅡ 工程设置与引脚分配新建 QuartusⅡ 工程时器件选 Cyclone IV 系列具体型号看实验室板子。顶层文件设为traffic_top.v其余模块放在同一目录下Quartus 会自动识别例化关系。编译前需要做两件事一是设置顶层实体二是分配引脚。引脚分配根据板子原理图来时钟输入接 50MHz 晶振引脚复位按键接一个带消抖的 GPIO紧急模式按键接另一个。灯控输出接 LED 引脚数码管段选接 8 个 GPIO位选接 2 个 GPIO。分配完引脚后跑一次全编译看资源占用和时序报告。如果时序不满足通常是组合逻辑路径太长可以在状态机输出加一级寄存器打拍。注意引脚分配前务必确认板子原理图不同厂商的 FPGA 开发板引脚定义不同接错引脚可能导致 LED 常亮或不亮排查起来很费时间。5. 仿真验证与避坑指南波形对不上的排查思路5.1 ModelSim 功能仿真流程QuartusⅡ 自带 ModelSim-Altera 仿真工具也可以调用外部 ModelSim。我一般写一个 testbench把分频系数改小到 10这样 30 秒的绿灯在仿真里只需要 300 个时钟周期就能跑完。testbench 里例化顶层模块给clk加 20ns 周期的时钟rst_n先拉低 100ns 再拉高然后跑 5000ns 观察波形。timescale 1ns/1ns module tb_traffic; reg clk, rst_n, emergency; wire [2:0] main_light, side_light; wire [7:0] seg_data; wire [1:0] seg_sel; // 例化顶层分频系数改小 traffic_top #(.DIV(10)) u_top ( .clk(clk), .rst_n(rst_n), .emergency(emergency), .main_light(main_light), .side_light(side_light), .seg_data(seg_data), .seg_sel(seg_sel) ); initial begin clk 0; rst_n 0; emergency 0; #100 rst_n 1; #5000 $stop; end always #10 clk ~clk; // 20ns周期 endmodule仿真波形里重点看几个信号state是否按 S0→S1→S2→S3→S0 循环cnt是否在每个状态递减到 1 后跳转main_light和side_light是否在状态切换时同步变化。如果state卡在某个值不动检查tick是否正常产生如果cnt不递减检查tick是否接到了状态机的使能端。5.2 常见问题排查从现象到根因现象一仿真波形里状态跳转比预期快很多。原因通常是分频系数没改小或者 testbench 里例化时参数传递写错了。解决方法是检查clk_divider的DIV参数是否被顶层正确覆盖在 ModelSim 里查看cnt信号的最大值是否等于DIV-1。现象二倒计时数码管显示乱码或不变。原因可能是 BCD 转换函数的位宽不匹配或者动态扫描的位选信号接反了。解决方法是单独仿真seg_decoder模块给固定的count_main和count_side值看seg_data输出是否对应正确的段码。如果段码对但显示乱检查位选信号的扫描频率是否太快或太慢。现象三紧急模式按下后状态机不响应。原因通常是emergency信号没有做同步处理或者优先级判断写在了tick条件里面。解决方法是把emergency的判断放在always块的最前面优先级高于tick和done。如果按键有抖动加一个 20ms 的消抖计数器。现象四Quartus 编译报错“multiple drivers”。原因是在多个always块里对同一个reg变量赋值。解决方法是检查每个reg变量是否只在一个always块里被赋值组合逻辑和时序逻辑不要混在同一个块里。现象五时序报告显示 setup violation。原因是组合逻辑路径太长比如状态机的next_state经过多级 case 嵌套。解决方法是在next_state后面加一级寄存器或者把状态编码改成独热码减少组合逻辑级数。提示仿真通过不代表上板能跑上板前先用 SignalTap 抓一下实际波形重点看复位释放后第一个状态是否正确以及秒脉冲的周期是否接近 1 秒。6. 验收与答辩怎么证明你的交通灯控制电路是可靠的验收环节导师通常不会只看灯亮不亮而是会盯着几个关键指标问。我一般会准备一张验收清单把每个指标对应的测试方法和预期结果写清楚。第一项是状态循环正确性复位后主干道绿灯亮30 秒后转黄灯3 秒后支路绿灯亮依次循环。测试方法是秒表计时加肉眼观察误差在 1 秒内算合格。第二项是倒计时同步性数码管显示的剩余秒数必须和灯态变化同步不能出现灯变了数码管还停在 0 的情况。测试方法是在状态切换瞬间观察数码管是否立即刷新。第三项是紧急模式响应时间按下紧急按键后所有灯应在 100ms 内切换到全红。测试方法是用示波器抓按键信号和灯控信号的时间差。第四项是复位可靠性在任意状态下按复位键系统应回到主干道绿灯初始状态数码管显示 30。测试方法是随机时刻按复位重复 10 次看是否每次都能正确复位。答辩时最容易被问到的三个问题一是“黄灯时间为什么是 3 秒”回答要落到安全规范上通常黄灯时长根据路口宽度和车速计算课设里取 3 秒是经验值。二是“两个方向同时亮红灯的情况怎么处理”回答紧急模式下全红是安全设计正常模式下不会出现。三是“你的状态机是 Moore 型还是 Mealy 型”回答 Moore 型输出只与当前状态有关避免组合逻辑毛刺。我一般会在答辩前把仿真波形截图打印出来标出关键时间点导师问的时候直接指波形说话比空口解释有说服力。从那以后我每次带课设都要求学生先跑通仿真再上板仿真波形不对绝不碰硬件这个习惯帮我省了至少三周的返工时间。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

STM32F103实现1024点FFT频谱分析:从ADC采样到串口输出的完整工程示例

STM32F103实现1024点FFT频谱分析:从ADC采样到串口输出的完整工程示例

简介:这是一份面向STM32F103微控制器与ARM Cortex-M3平台的FFT例程包,适合嵌入式开发初学者和有一定单片机基础的工程师。例程基于STM32 HAL库构建,围绕快速傅里叶变换演示完整的频谱分析处理流程,可用于音频分析、振动检测、滤波…

📅 2026/10/23 21:54:50
STC8G1K08驱动WS2812:8脚单片机带240颗RGB灯实战

STC8G1K08驱动WS2812:8脚单片机带240颗RGB灯实战

简介:本资源是一份基于STC8G1K08-36I-SOP8单片机驱动WS2812炫彩LED灯的完整C51嵌入式开发项目,面向嵌入式初学者、单片机课程设计学生及硬件爱好者,解决单线协议RGB灯精准时序控制这一典型难点。压缩包共34个文件,含6个核心C源码&…

📅 2026/10/23 21:54:50
Office宏被阻止?MOTW标记与信任位置全解析

Office宏被阻止?MOTW标记与信任位置全解析

1. 这个报错到底在说什么如果你经常跟Excel、Word或者PowerPoint里的VBA宏打交道,大概率见过这个弹窗:“Microsoft 已经阻止宏运行,因为此文件的来源不受信任。” 点开详细信息,还会看到一句更具体的提示,大意是这个文…

📅 2026/10/23 21:54:49
MORE NEWS

更多资讯

📰

CMMI不是文档 checklist,而是软件团队能力体检表

简介:本资源为CMMI 3.0软件工程规范官方文档PDF版,面向软件过程改进工程师、CMMI评估师、质量保证负责人及中大型研发组织的过程体系建设者,系统解决组织级过程能力成熟度评估与持续优化问题。文档完整覆盖CMMI-DEV v2.0全部20个实践域&#…

📰

【收藏必备】Agno框架入门指南:零基础构建高性能多智能体应用与TaoToken统一Key接入

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

微信小程序完整源码:数字华容道益智游戏(原生开发,开箱即用)

简介:这是一份面向小程序开发者的学习型源码资源包,涵盖从基础语法到高级实战的完整项目实践,特别适合初学者系统入门与进阶者拓展游戏、地图、多媒体等复杂场景开发能力。资源包含多个可运行的小程序项目,涉及WXML/WXSS/JS三端协…

📰

IEEE标准节点仿真模型解析与应用实践

1. IEEE标准节点仿真模型概述在电力系统分析与规划领域,IEEE标准节点仿真模型(IEEE Standard Bus System Models)是工程师们最常用的基准测试案例。这套由电气电子工程师学会(IEEE)发布的标准化模型,包含了…

📰

长城汽车年销132.37万辆:拆解增长背后的结构、新能源与海外双引擎

刚看到“长城汽车2025年销量132.37万辆,同比增长7.33%”这条数据的时候,我第一反应不是“挺好”,而是赶紧去翻了翻全年车市大盘的情况。单独看一家车企的销量绝对值,很难判断它是真的跑赢了大盘,还是仅仅跟着行业水涨船…

📰

JavaEE四层架构:MyBatis→Spring→AOP→Spring MVC知识地图

简介:本资源是面向高校JavaEE课程学习者的期末复习题库,聚焦MyBatis框架核心知识点的系统性巩固与应试训练,覆盖ORM原理、SQL映射配置、事务管理、模糊查询安全、动态SQL及工作流程等关键考点。题库以标准四类题型(填空、判断、选…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬