Quartus II 13.0实战:交通灯控制器的FPGA实现全流程解析
第一次接触FPGA开发时,最令人困惑的往往不是Verilog代码本身,而是如何将写好的代码真正"烧录"到硬件上运行。本文将带你完整走通从代码编写到硬件实现的每个环节,特别适合已经掌握Verilog基础但缺乏完整项目经验的开发者。
1. 工程创建与环境配置
在开始之前,请确保已安装Quartus II 13.0(或更新版本)和对应的器件支持包。我们以Cyclone IV EP4CE6E22C8为例,这是性价比较高的入门级FPGA开发板常用芯片。
新建工程关键步骤:
- 启动Quartus II,选择File → New Project Wizard
- 指定工程目录(建议使用英文路径)
- 选择目标器件:
- Family: Cyclone IV E
- Device: EP4CE6E22C8
- 添加已有Verilog文件(或稍后创建)
- 完成向导
推荐工程设置优化:
# 在Quartus II的Tcl Console中执行以下优化命令 set_global_assignment -name OPTIMIZATION_MODE "AGGRESSIVE PERFORMANCE" set_global_assignment -name PHYSICAL_SYNTHESIS_EFFORT "EXTRA" set_global_assignment -name FITTER_EFFORT "STANDARD FIT"提示:创建工程后立即设置版本控制(如Git),这是很多初学者容易忽略但极其重要的习惯。
2. 状态机设计与Verilog实现
交通灯控制器的核心是一个有限状态机(FSM)。我们采用Moore型状态机设计,输出仅取决于当前状态。以下是状态定义和转换逻辑:
状态编码方案:
| 状态名 | 二进制编码 | 主干道信号 | 支干道信号 |
|---|---|---|---|
| S0 | 00 | 绿灯 | 红灯 |
| S1 | 01 | 黄灯 | 红灯 |
| S2 | 11 | 红灯 | 绿灯 |
| S3 | 10 | 红灯 | 黄灯 |
Verilog核心代码实现:
module traffic_light( input clk, // 50MHz时钟 input reset_n, // 低电平复位 input sensor, // 支干道车辆检测 output reg [2:0] main_light, // 主干道灯控(R,Y,G) output reg [2:0] side_light // 支干道灯控(R,Y,G) ); // 状态定义 localparam S0 = 2'b00; localparam S1 = 2'b01; localparam S2 = 2'b11; localparam S3 = 2'b10; reg [1:0] current_state, next_state; reg [25:0] counter; // 26位计数器,约1.34秒满量程 // 状态转换逻辑 always @(posedge clk or negedge reset_n) begin if (!reset_n) begin current_state <= S0; counter <= 0; end else begin current_state <= next_state; counter <= (current_state != next_state) ? 0 : counter + 1; end end // 下一状态逻辑 always @(*) begin case(current_state) S0: next_state = (counter >= 26'd30000000 && sensor) ? S1 : S0; // 60秒 S1: next_state = (counter >= 26'd2500000) ? S2 : S1; // 5秒 S2: next_state = (counter >= 26'd15000000 || !sensor) ? S3 : S2; // 30秒 S3: next_state = (counter >= 26'd2500000) ? S0 : S3; // 5秒 default: next_state = S0; endcase end // 输出逻辑 always @(*) begin case(current_state) S0: {main_light, side_light} = 6'b001100; S1: {main_light, side_light} = 6'b010100; S2: {main_light, side_light} = 6'b100001; S3: {main_light, side_light} = 6'b100010; default: {main_light, side_light} = 6'b001100; endcase end endmodule3. 仿真验证与Testbench编写
在硬件实现前,必须通过仿真验证设计逻辑的正确性。我们使用ModelSim进行功能仿真。
Testbench关键部分:
`timescale 1ns/1ns module tb_traffic_light; reg clk; reg reset_n; reg sensor; wire [2:0] main_light; wire [2:0] side_light; traffic_light uut ( .clk(clk), .reset_n(reset_n), .sensor(sensor), .main_light(main_light), .side_light(side_light) ); // 时钟生成(50MHz) always #10 clk = ~clk; initial begin // 初始化 clk = 0; reset_n = 0; sensor = 0; // 复位释放 #100 reset_n = 1; // 测试场景1:无车辆时保持主干道绿灯 #200000000; // 观察4秒 // 测试场景2:支干道有车时转换 sensor = 1; #6000000000; // 观察120秒完整周期 $stop; end endmodule仿真结果检查要点:
- 复位后是否进入S0状态(主干道绿灯)
- 传感器触发后是否在60秒后开始转换
- 黄灯持续时间是否为精确的5秒
- 支干道绿灯时间是否不超过30秒
- 无车辆时是否快速返回默认状态
4. 引脚分配与硬件下载
仿真通过后,需要将设计映射到实际FPGA引脚。这是初学者最容易出错的环节。
引脚分配建议(基于常见开发板):
| 信号名称 | FPGA引脚 | 开发板对应接口 |
|---|---|---|
| clk | PIN_23 | 50MHz晶振 |
| reset_n | PIN_24 | 按键K1 |
| sensor | PIN_25 | 拨码开关SW1 |
| main_light[0] | PIN_40 | LED1 (红) |
| main_light[1] | PIN_41 | LED2 (黄) |
| main_light[2] | PIN_42 | LED3 (绿) |
| side_light[0] | PIN_43 | LED4 (红) |
| side_light[1] | PIN_44 | LED5 (黄) |
| side_light[2] | PIN_45 | LED6 (绿) |
引脚分配方法:
- 打开Assignment Editor (Assignments → Pin Planner)
- 逐个输入信号和对应引脚号
- 保存分配(生成.qsf文件)
下载流程:
- 编译整个工程(Processing → Start Compilation)
- 连接开发板并安装驱动
- 打开Programmer (Tools → Programmer)
- 添加生成的.sof文件
- 点击Start开始下载
注意:首次使用时可能需要配置编程硬件,选择USB-Blaster或其他对应下载器。
5. 常见问题与调试技巧
在实际硬件调试中,可能会遇到以下典型问题:
问题1:下载后LED无反应
排查步骤:
- 检查开发板供电是否正常
- 确认下载模式选择正确(JTAG)
- 使用示波器检查时钟信号
- 验证复位信号极性(开发板按键可能是低有效)
问题2:状态转换时间不准确
解决方案:
// 修改计数器判断条件,考虑时钟频率 parameter CLK_FREQ = 50_000_000; // 50MHz // 60秒计时条件改为: (counter >= CLK_FREQ * 60 - 1)问题3:信号抖动导致误触发
硬件滤波方案:
// 添加软件去抖逻辑 reg [19:0] sensor_filter; wire sensor_clean; always @(posedge clk) begin sensor_filter <= {sensor_filter[18:0], sensor}; end assign sensor_clean = &sensor_filter; // 持续20个周期高电平才认为有效进阶调试技巧:
- 使用SignalTap II嵌入式逻辑分析仪实时观察内部信号
- 在关键状态添加调试输出(通过空闲引脚)
- 分模块验证(先测试时钟分频,再验证状态机)
在完成基础功能后,可以考虑添加以下增强功能:
- 数码管倒计时显示
- 夜间模式(黄灯闪烁)
- 紧急车辆优先通行
- 通过UART接口远程监控状态