本文分享利用FPGA实现的交通信号灯,FPGA型号为野火征途Pro开发板,具体功能如下:
此项目旨在模拟东西和南北两路口交通信号灯,初始态两路口均为红灯亮,接着,东西路口绿灯亮,南北路口红灯亮,数码管同时显示15秒倒计时,当倒计时时间小于3秒,东西路口绿灯灭,黄灯开始闪烁。黄灯闪烁若干次,即当倒计时结束,东西路口红灯亮同时南北路口绿灯亮,数码管重新赋值15秒倒计时,同理当倒计时时间小于3秒,南北路口绿灯熄灭,黄灯闪烁,闪烁若干次至倒计时结束,不断重复上述操作。
[5:0]LED输出解释:6位数据从高到底依次表示东西路口的绿灯,黄灯和红灯,例如:输出100001即东西路口绿灯亮,红黄不亮,南北口红灯亮,绿黄不亮。
分频模块 div_2hz
首先设计需要秒为单位的倒计时,本文使用的开发板输出系统时钟为50Mhz,因此需要进行分频。为后续对黄灯闪烁的精确控制,1秒需要亮灭两个过程,所以设计2hz分频模块,即0.5秒为一个时钟周期。
module div_2hz(
input sys_clk, //50Mhz系统时钟
input sys_rst_n, //复位信号
output reg clk_2hz //2hz分频输出信号
);
reg [24:0]num_2hz; //定义reg型信号存储当前计数值
parameter num1=12_499_999; //定义常量 后面计数器的最大值,即计0-12_499_999共计12_500_000个数
//12_500_000计数器 因为需要得到2hz频率,需要对50Mhz进行分频,同时输出时钟占空比50%,即计数最大值为分频数一半
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
num_2hz<=25'd0;
else if(num_2hz==num1)
num_2hz<=25'd0;
else
num_2hz<=num_2hz+1;
//当达到计数最大值,对输出信号进行反转,得到2hz时钟输出
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
clk_2hz<=1'b1;
else if(num_2hz==num1)
clk_2hz<=~clk_2hz;
endmodule
交通信号灯模块 tra_sign
本模块为项目的核心,利用状态机实现交通信号灯功能以及数码管倒计时的显示。模块输入为2hz分频时钟以及系统复位信号,输出为[5:0]LED信号以及倒计时时间信号。
定义五个状态s0-s4,s0代表初始复位态,此时默认交通信号灯全灭;
s1状态表示东西路口绿灯,南北路口红灯;
s2状态表示东西路口黄灯闪烁,南北路口红灯;
s3状态表示东西路口红灯,南北路口绿灯;
s4状态表示东西路口红灯,南北路口黄灯闪烁;
此模块难点在于倒计时时间与交通信号灯的同步,这里采用always@(*)的组合逻辑方法,具体原理详见下述解释
module tra_sign (
input clk_2hz, //2hz分频时钟
input sys_rst_n,
output reg[5:0]led, //模拟交通灯的6位数据
output [4:0]num2 //倒计时数据输出
);
reg [4:0]num1;
reg [2:0]state;
//定义5个状态
parameter s0=3'd0;
parameter s1=3'd1;
parameter s2=3'd2;
parameter s3=3'd3;
parameter s4=3'd4;
//时序逻辑:数据30-1不断地循环倒数,实质每倒数两次位1s
always@(posedge clk_2hz or negedge sys_rst_n)
if(!sys_rst_n)
num1<=5'd30;
else if(num1==1)
num1<=5'd30;
else
num1<=num1-1;
//组合逻辑:num2为倒计时时间,其赋值需要进行双重判断,首先num1不能为30,否则相当于上个30个数倒数结束,15秒倒计时结束,应当给num2倒计时时间重新赋值15。在num1不为30条件下进行取余判断,因为num1倒数两次才为1s,两条件均满足,将此时倒计时时间(num1/2)立即赋值num2
assign num2=(num1!=30)?((num1%2==0)?num1/2:num2):15;
//组合逻辑:每个状态下将交通信号灯信号[5:0]LED赋对应值
always@(*)
if(!sys_rst_n)
led<=6'b000_000;
else if(state==s1)
led<=6'b100_001;
else if(state==s2) //s2状态黄灯需要闪烁,此时凸显出num1的作用,对其进行奇偶判断进行亮灭赋值
if(num1%2==0)
led<=6'b010_001;
else
led<=6'b000_001;
else if(state==s3)
led<=6'b001_100;
else if(state==s4) //s4同上s2
if(num1%2==0)
led<=6'b001_010;
else
led<=6'b001_000;
//组合逻辑:5个状态转换条件的描述
always@(*)
if(!sys_rst_n)
state<=s0;
else case(state)
s0:state<=s1; //s0复位态无条件转s1
s1:
if(num2<=3) //s1状态当倒计时小于等于3秒转移s2
state<=s2;
else
state<=state;
s2:
if(num2==15) //若一轮倒计时结束,则状态转换
state<=s3;
else
state<=state;
s3:
if(num2<=3) //s3状态当倒计时小于等于3秒转移s4
state<=s4;
else
state<=state;
s4:
if(num2==15) //若一轮倒计时结束,则状态转换
state<=s1;
else
state<=state;
default:state<=state;
endcase
endmodule
交通信号灯测试文件 tra_sign_tb
`timescale 1ns/1ns
module tra_sign_tb();
reg sys_rst_n;
reg clk_2hz;
wire [5:0]led;
wire [4:0]num2;
initial begin
clk_2hz = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 clk_2hz = ~clk_2hz;
tra_sign tra_sign_inst(
.clk_2hz(clk_2hz),
.sys_rst_n(sys_rst_n),
.led(led),
.num2(num2)
);
endmodule
仿真结果:
仿真图中复位状态此时交通信号灯全灭,对应LED为000000,当复位结束时,num2从0111开始自减即15秒倒计时开始。此时为s1状态即LED输出100001,即东西口绿灯,南北口红灯
当倒计时时间小于等于3秒,即num2为0011开始,进入s2状态,此时东西口绿灯灭,黄灯进行闪烁,1s进行一个亮灭周期。LED输出010001和000001,当一轮倒计时结束,num2重新赋值15进行新一轮倒计时,进入s3状态,此时东西口红灯,南北口绿灯,LED输出001100
当倒计时再次小于等于3秒,此时南北口绿灯灭,黄灯进行1s亮灭周期闪烁,即前0.5s亮后0.5秒灭,当15秒倒计时再次结束,num2重新赋值,如此循环往复。
本文利用一个时序逻辑多个组合逻辑,避免了时序逻辑混乱造成的时钟沿对齐困难等问题,达到交通灯功状态转换以及同步倒计时功能。
至此借助modelsim模拟交通灯功能完毕,后面会将完整烧录程序分享。
如有表达不准确或错误处,多多谅解,后面会不断分享项目设计