FPGA交通灯实战:用Verilog在Quartus里实现可调时序的十字路口控制(附仿真代码)
·
FPGA交通灯实战:用Verilog在Quartus里实现可调时序的十字路口控制(附仿真代码)
十字路口交通信号控制是数字电路设计的经典案例,也是FPGA初学者掌握状态机设计的绝佳实践项目。本文将带你从零开始,用Verilog HDL在Quartus Prime开发环境中实现一个具备可调时序参数的交通灯控制器,并通过ModelSim完成功能验证。
1. 项目需求分析与设计思路
现代交通灯控制系统需要满足三个核心需求:安全性、灵活性和可扩展性。我们的设计将围绕这三点展开:
- 基础功能:东西方向绿灯30秒+黄灯2秒,南北方向红灯32秒;随后切换为南北方向绿灯30秒+黄灯2秒,东西方向红灯32秒
- 扩展功能:各方向红绿灯时间可通过参数灵活配置
- 特殊状态:紧急情况下可手动切换为全方向黄灯闪烁模式
1.1 状态机设计
采用Moore型有限状态机(FSM)作为控制核心,定义四个状态:
parameter S_EW_GREEN = 2'b00; // 东西绿灯,南北红灯
parameter S_EW_YELLOW = 2'b01; // 东西黄灯,南北红灯
parameter S_NS_GREEN = 2'b10; // 南北绿灯,东西红灯
parameter S_NS_YELLOW = 2'b11; // 南北黄灯,东西红灯
状态转移图如下:
+---------------+
| |
v |
S_EW_GREEN -> S_EW_YELLOW
^ |
| v
S_NS_YELLOW <- S_NS_GREEN
1.2 模块划分
系统由五个核心模块构成:
- 时钟分频模块:将板载高频时钟分频为1Hz基准信号
- 状态控制模块:实现状态机及定时逻辑
- 灯控输出模块:根据状态生成灯控信号
- 倒计时显示模块:驱动数码管显示剩余时间
- 参数配置模块:存储可调时间参数
2. Verilog实现详解
2.1 顶层模块设计
module traffic_light(
input clk, // 板载时钟(如50MHz)
input reset_n, // 复位信号(低有效)
input emergency, // 紧急模式开关
output reg [2:0] ew_light, // 东西方向灯控[红,黄,绿]
output reg [2:0] ns_light, // 南北方向灯控[红,黄,绿]
output [6:0] seg, // 七段数码管段选
output [3:0] an // 数码管位选
);
// 参数化设计,便于修改时序
parameter EW_GREEN_TIME = 30; // 东西绿灯时间
parameter EW_YELLOW_TIME = 2; // 东西黄灯时间
parameter NS_GREEN_TIME = 30; // 南北绿灯时间
parameter NS_YELLOW_TIME = 2; // 南北黄灯时间
wire clk_1Hz; // 1Hz时钟信号
wire [5:0] count_val; // 当前倒计时值
// 实例化各子模块
clock_divider clk_div(.clk(clk), .reset_n(reset_n), .clk_out(clk_1Hz));
state_controller ctrl(
.clk(clk_1Hz),
.reset_n(reset_n),
.emergency(emergency),
.ew_green_time(EW_GREEN_TIME),
.ew_yellow_time(EW_YELLOW_TIME),
.ns_green_time(NS_GREEN_TIME),
.ns_yellow_time(NS_YELLOW_TIME),
.current_state(state),
.count(count_val)
);
light_driver lights(
.state(state),
.emergency(emergency),
.ew_light(ew_light),
.ns_light(ns_light)
);
segment_display disp(
.clk(clk),
.reset_n(reset_n),
.value(count_val),
.seg(seg),
.an(an)
);
endmodule
2.2 状态控制模块
这是系统的核心逻辑,采用三段式状态机写法:
module state_controller(
input clk,
input reset_n,
input emergency,
input [5:0] ew_green_time,
input [5:0] ew_yellow_time,
input [5:0] ns_green_time,
input [5:0] ns_yellow_time,
output reg [1:0] current_state,
output reg [5:0] count
);
// 状态定义
parameter S_EW_GREEN = 2'b00;
parameter S_EW_YELLOW = 2'b01;
parameter S_NS_GREEN = 2'b10;
parameter S_NS_YELLOW = 2'b11;
// 状态寄存器
reg [1:0] state, next_state;
// 第一段:状态寄存器更新
always @(posedge clk or negedge reset_n) begin
if(!reset_n)
state <= S_EW_GREEN;
else if(!emergency) // 非紧急模式下才更新状态
state <= next_state;
end
// 第二段:状态转移逻辑
always @(*) begin
case(state)
S_EW_GREEN:
next_state = (count == 1) ? S_EW_YELLOW : S_EW_GREEN;
S_EW_YELLOW:
next_state = (count == 1) ? S_NS_GREEN : S_EW_YELLOW;
S_NS_GREEN:
next_state = (count == 1) ? S_NS_YELLOW : S_NS_GREEN;
S_NS_YELLOW:
next_state = (count == 1) ? S_EW_GREEN : S_NS_YELLOW;
default:
next_state = S_EW_GREEN;
endcase
end
// 第三段:计数器逻辑
always @(posedge clk or negedge reset_n) begin
if(!reset_n) begin
count <= ew_green_time;
current_state <= S_EW_GREEN;
end else if(emergency) begin
count <= 0; // 紧急模式下停止计数
current_state <= state;
end else begin
current_state <= state;
case(state)
S_EW_GREEN:
count <= (count == 1) ? ew_yellow_time : count - 1;
S_EW_YELLOW:
count <= (count == 1) ? ns_green_time : count - 1;
S_NS_GREEN:
count <= (count == 1) ? ns_yellow_time : count - 1;
S_NS_YELLOW:
count <= (count == 1) ? ew_green_time : count - 1;
endcase
end
end
endmodule
2.3 灯控输出模块
module light_driver(
input [1:0] state,
input emergency,
output reg [2:0] ew_light, // [red, yellow, green]
output reg [2:0] ns_light
);
// 灯控编码
parameter LIGHT_OFF = 3'b000;
parameter RED_ON = 3'b100;
parameter YELLOW_ON = 3'b010;
parameter GREEN_ON = 3'b001;
always @(*) begin
if(emergency) begin
// 紧急模式:双黄灯闪烁
ew_light = YELLOW_ON;
ns_light = YELLOW_ON;
end else begin
case(state)
2'b00: begin // S_EW_GREEN
ew_light = GREEN_ON;
ns_light = RED_ON;
end
2'b01: begin // S_EW_YELLOW
ew_light = YELLOW_ON;
ns_light = RED_ON;
end
2'b10: begin // S_NS_GREEN
ew_light = RED_ON;
ns_light = GREEN_ON;
end
2'b11: begin // S_NS_YELLOW
ew_light = RED_ON;
ns_light = YELLOW_ON;
end
default: begin
ew_light = LIGHT_OFF;
ns_light = LIGHT_OFF;
end
endcase
end
end
endmodule
3. 关键实现技巧
3.1 参数化设计
通过Verilog参数实现时间可配置:
module traffic_light #(
parameter EW_GREEN = 30,
parameter EW_YELLOW = 2,
parameter NS_GREEN = 30,
parameter NS_YELLOW = 2
) (
// 端口定义
);
实例化时可覆盖默认值:
traffic_light #(
.EW_GREEN(45),
.NS_GREEN(60)
) my_traffic_light(
// 端口连接
);
3.2 数码管动态扫描
实现四位倒计时显示:
module segment_display(
input clk,
input reset_n,
input [5:0] value,
output reg [6:0] seg,
output reg [3:0] an
);
reg [1:0] scan_cnt;
reg [3:0] digit;
reg [15:0] refresh_cnt;
// 刷新率约1kHz (50MHz/2^16)
always @(posedge clk or negedge reset_n) begin
if(!reset_n)
refresh_cnt <= 0;
else
refresh_cnt <= refresh_cnt + 1;
end
always @(posedge clk) begin
if(refresh_cnt == 0)
scan_cnt <= scan_cnt + 1;
end
// 数码管位选
always @(*) begin
case(scan_cnt)
2'b00: begin
an = 4'b1110;
digit = value % 10; // 个位
end
2'b01: begin
an = 4'b1101;
digit = value / 10; // 十位
end
default: begin
an = 4'b1111;
digit = 0;
end
endcase
end
// 七段译码
always @(*) begin
case(digit)
0: seg = 7'b1000000;
1: seg = 7'b1111001;
// ... 其他数字编码
default: seg = 7'b1111111;
endcase
end
endmodule
4. ModelSim仿真验证
4.1 测试平台搭建
`timescale 1ns/1ps
module tb_traffic_light;
reg clk;
reg reset_n;
reg emergency;
wire [2:0] ew_light;
wire [2:0] ns_light;
traffic_light dut (
.clk(clk),
.reset_n(reset_n),
.emergency(emergency),
.ew_light(ew_light),
.ns_light(ns_light)
);
// 时钟生成 (50MHz)
initial begin
clk = 0;
forever #10 clk = ~clk;
end
// 测试流程
initial begin
reset_n = 0;
emergency = 0;
#100 reset_n = 1;
// 观察正常模式
#2000000; // 观察2ms仿真时间
// 测试紧急模式
emergency = 1;
#500000;
emergency = 0;
#1000000;
$stop;
end
endmodule
4.2 仿真结果分析
正常模式下应观察到:
- 东西绿灯亮30个时钟周期
- 东西黄灯亮2个时钟周期
- 南北绿灯亮30个时钟周期
- 南北黄灯亮2个时钟周期
紧急模式下:
- 双方向黄灯持续闪烁
- 计数器停止计数
5. Quartus工程实现
5.1 引脚分配示例(以DE10-Standard开发板为例)
| FPGA引脚 | 外设功能 |
|---|---|
| PIN_AF14 | 50MHz时钟输入 |
| PIN_AA15 | 复位按钮 |
| PIN_AB12 | 紧急模式开关 |
| PIN_AA24 | 东西方向红灯 |
| PIN_AB23 | 东西方向黄灯 |
| PIN_AA23 | 东西方向绿灯 |
| ... | ... |
5.2 时序约束
# 时钟约束
create_clock -name clk -period 20 [get_ports clk]
# 输出延迟约束
set_output_delay -clock clk 5 [get_ports {ew_light[*] ns_light[*]}]
6. 进阶优化方向
- 车流量自适应:通过传感器输入动态调整各方向绿灯时长
- 夜间模式:低流量时段切换为黄灯闪烁
- 倒计时显示优化:增加秒数十位显示
- 多路口协同:多个FPGA通过UART通信实现联动控制
以下是一个车流量检测的状态机改进示例:
// 在状态控制模块中添加
input [3:0] ew_traffic; // 东西方向车流量检测
input [3:0] ns_traffic; // 南北方向车流量检测
// 动态调整绿灯时间
wire [5:0] dynamic_ew_green = EW_GREEN_TIME + ew_traffic*2;
wire [5:0] dynamic_ns_green = NS_GREEN_TIME + ns_traffic*2;
通过这个完整的FPGA交通灯项目实践,你不仅能掌握Verilog状态机设计方法,还能学习到参数化设计、仿真验证等工程化开发技巧。建议在基础版本实现后,尝试挑战进阶功能以深化理解。
更多推荐



所有评论(0)