系统开发工具 | Vivado |
---|---|
系统开发语言 | Verilog |
仿真平台 | 杰创EDA远程二代网页版 |
此电路原理图是在杰创EDA远程二代网页版上进行绘制的,如图所示
交通信号灯系统控制的原理框图如图所示,将系统分成4个模块:
主控制器模块原理上是一个状态机,依据要求设计,设计出信号灯点亮规律的状态转换表,如表所示,其中0表示灭,1表示亮,状态表显示了信号灯在运行过程中每个状态应该持续的时间,以及状态之间的转换顺序。依据分频计数器,当分频计数器的时钟达到了对应时间则切换为下一个状态,就可以实现控制信号灯的亮灭。
图中有八个状态,实际编程的时候还应该符加四个状态,用于表示黄灯的闪烁,这四个状态对应黄灯为0,表示黄灯熄灭,1s切换一次状态就可以达到黄灯每秒闪烁一次的效果,符加状态如下表所示:
整个系统采用的时钟是1khz,原因是为了显示四位数码管上多位数字,而信号灯的状态切换是以秒为单位进行的,因此需要将1khz的时钟信号转化成1hz的,设计分频计数器,每500个周期,分频时钟信号反转一次。
状态机上共有8个状态,以这8个状态为周期进行循环,系统执行一个周期共计需要195秒,因此设计定时计数器进行计数,定时到195秒时就归零重新计时。
在讲译码模块之前,先讲一讲四位数码管如何使用,七段数码管是电子开发过程中常用的输出显示设备,本项目使用的是一个四八位一体,共阴极型七段数码管。右图是本实验用到的四八位一体,共阴极型,左图为单个静态数码管。
由于此设计七段数码管公共端连接到GND(共阴极型),当数码管的中的那一个段(a,b,c,d,e,f,g)被输入高电平,则相应的这一段被点亮。反之则不亮。四位一体的七段数码管在单个静态数码管的基础上加入了用于选择哪一位数码管的位选信号(1,2,3,4)端口。四个数码管的a,b,c,d,e,f,g,h,dp都连接在了一起,4个数码管分别由各自的 位选信号来控制,当位选信号为低电平时该位数码管被选择。同一时刻只有一位数码管被选中并点亮,下一时刻则切换到相邻位数码管,但因为切换速度很快,在视觉暂留效应的帮助下,我们看到的就是四位数码管被整体点亮。
为此可以设计出每个数字的译码表,如表所示
数字 | 十六进制下编码 |
---|---|
0 | 7’h7e |
1 | 7’h30 |
2 | 7’h6d |
3 | 7’h79 |
4 | 7‘h33 |
5 | 7’h5b |
6 | 7’h5f |
7 | 7’h70 |
8 | 7’h7f |
9 | 7’h7b |
输出数据的时候,在管脚上输出相应的编码,控制时钟以快速频率切换就可以实现显示两位数字效果。默认使用两位数字来显示倒计时,有红灯倒计时大于99秒的情况,也就是超出两位数字所表示的范围,规定超过99秒则不显示
源码免费下载链接
module traffic_light (
clk,
rst_n,
count,
ew,
sn
);
input clk,rst_n;
input [7:0] count;
output [5:0] ew,sn;
reg [5:0] ew,sn;
reg [3:0] pre_state,next_state;
reg [12:0] counter=13'b0;
reg clk_5hz=1'b0;
parameter
S0=4'b0000, //主干路四个状态
S1=4'b0001,
S2=4'b0010,
S3=4'b0011,
S4=4'b0100, //支干路四个状态
S5=4'b0101,
S6=4'b0110,
S7=4'b0111,
S8=4'b1000, //主干路直行黄灯
S9=4'b1001, //主干路左转黄灯
S10=4'b1010, //支干路直行黄灯
S11=4'b1011; //支干路左转黄灯
always @ (negedge clk or negedge rst_n)
begin
if(!rst_n)
pre_state<=S0;
else
pre_state<=next_state;
end
always @ (negedge clk) begin //这段代码是用来实现黄灯的闪烁功能
if(count>=60&&count<65)
begin
counter <=counter+1'b1;
if (counter == 'd499)begin //计数,形成0.5秒的时钟
clk_5hz <= !clk_5hz;
counter <= 'd0;
end
end
else if(count>=105&&count<110)
begin
counter <=counter+1'b1;
if (counter == 'd499)begin
clk_5hz <= !clk_5hz;
counter <= 'd0;
end
end
else if(count>=155&&count<160)
begin
counter <=counter+1'b1;
if (counter == 'd499)begin
clk_5hz <= !clk_5hz;
counter <= 'd0;
end
end
else if(count>=190&&count<195)
begin
counter <=counter+1'b1;
if (counter == 'd499)begin
clk_5hz <= !clk_5hz;
counter <= 'd0;
end
end
else
counter <='d0;
end
always @ (clk or pre_state)
begin
next_state<=3'bxxx;
if(count>=60 && count<65) begin //主干路直行黄灯
if(clk_5hz) next_state<=S8;
else
next_state <=S1; end
else if(count==8'd65) //主干路左转绿灯
next_state<=S2;
else if(count>=8'd105 && count<8'd110)begin //主干路左转黄灯
if(clk_5hz) next_state <=S9;
else
next_state<=S3;end
else if (count==8'd110) //支干路直行绿灯
next_state<=S4;
else if (count>=8'd155 && count<8'd160)begin //支干路直行黄灯
if(clk_5hz) next_state <=S10;
else
next_state<=S5;end
else if (count==8'd160) //支干路左转绿灯
next_state<=S6;
else if (count>=8'd190 && count<8'd195)begin //支干路左转黄的
if(clk_5hz) next_state <=S11;
else
next_state<=S7;end
else if (count == 0) //主干路直行绿灯
next_state<=S0;
else next_state <=pre_state;
end
always @ (pre_state)
begin
case (pre_state) //状态编码
S0:begin ew<=6'b001100;sn<=6'b001001;end
S1:begin ew<=6'b001010;sn<=6'b001001;end
S2:begin ew<=6'b100001;sn<=6'b001001;end
S3:begin ew<=6'b010001;sn<=6'b001001;end
S4:begin ew<=6'b001001;sn<=6'b001100;end
S5:begin ew<=6'b001001;sn<=6'b001010;end
S6:begin ew<=6'b001001;sn<=6'b100001;end
S7:begin ew<=6'b001001;sn<=6'b010001;end
S8:begin ew<=6'b001000;sn<=6'b001001;end
S9:begin ew<=6'b000001;sn<=6'b001001;end
S10:begin ew<=6'b001001;sn<=6'b001000;end
S11:begin ew<=6'b001001;sn<=6'b000001;end
default:begin ew<=6'b001100;sn<=6'b001001;end
endcase
end
endmodule
module countdiv (
clk,
rst_n,
clk_1hz
);
input clk,rst_n;
output clk_1hz;
reg clk_1hz=1'b0;
reg [12:0] count_clk=13'b0;
always @ (negedge clk) begin //监听时钟下降沿
count_clk <= count_clk + 1'b1;
if(count_clk == 'd499)begin //当计数器为449时,进行信号翻转
clk_1hz <= !clk_1hz;
count_clk <= 'd0; //计数器归零
end
end
endmodule
module counter (
clk,
rst_n,
out,
en
);
input clk,rst_n,en;
output [7:0] out;
reg [7:0] out;
always @ (negedge clk or negedge rst_n) //监听1hz的时钟信号
begin
if (!rst_n) //初始化为0
out<=8'd0;
else if(!en)
out<=out;
else if(out==8'd194) //当值为194时,下一个
out<=8'd0; //时钟下降沿到来置为0
else
out<=out+1'b1; //增1计数
end
endmodule
module tubeControl (
clk, //1khz时钟
clk_1hz, //1hz时钟
rst_n, //复位
count, //计数时间
out_main, //主干路a、b、c、d、e、f、g输出
out_minor, //支干路a、b、c、d、e、f、g输出
sel_main, //主干路片选信号
sel_minor //支干路片选信号
);
input clk,rst_n;
input clk_1hz;
input [7:0] count;
output [6:0] out_main;
output [6:0] out_minor;
output [1:0] sel_main;
output [1:0] sel_minor;
reg [1:0] sel_main;
reg [1:0] sel_minor;
reg [6:0] out_main;
reg [6:0] out_minor;
reg [6:0] num_main;
reg [6:0] num_minor;
reg [3:0] main_second=4'd10;
reg [3:0] main_minute=4'd10;
reg [3:0] minor_second=4'd10;
reg [3:0] minor_minute=4'd10;
reg [3:0] cnt_main;
reg [3:0] cnt_minor;
//译码表
parameter NUM_0=7'h7e;
parameter NUM_1=7'h30;
parameter NUM_2=7'h6d;
parameter NUM_3=7'h79;
parameter NUM_4=7'h33;
parameter NUM_5=7'h5b;
parameter NUM_6=7'h5f;
parameter NUM_7=7'h70;
parameter NUM_8=7'h7f ;
parameter NUM_9=7'h7b;
parameter NUM_10=7'h00; //这里表示不显示的译码
always @ (negedge clk)
begin
if(!rst_n)
num_main<='d0;
else if(count <= 8'd59) //主干路直行绿灯倒计时
num_main<=60-count;
else if(count<=8'd64) //主干路直行黄灯倒计时
num_main<=65-count;
else if(count<=8'd 104) //主干路左转绿灯倒计时
num_main<=105-count;
else if (count<=8'd109) //主干路左转黄灯倒计时
num_main<=110-count;
else if(count<=8'd194) //其他情况下红灯倒计时
num_main<=195-count;
end
always @ (negedge clk)
begin
if(!rst_n)
num_minor<='d0;
else if(count<=8'd109) //其他情况下红灯倒计时
num_minor<=110-count;
else if(count<=8'd154) //支干路直行绿灯倒计时
num_minor<=155-count;
else if(count<=8'd159) //支干路直行黄灯倒计时
num_minor<=160-count;
else if(count<=8'd189) //支干路左转绿灯倒计时
num_minor<=190-count;
else if(count<=8'd194) //支干路左转黄灯倒计时
num_minor<=195-count;
end
always @ (num_minor)
begin
if(num_minor==0)begin //初始化
minor_second<=10;
minor_minute<=10;end
else if(num_minor>99)begin //倒计时值大于99时,不显示
minor_second<=10;
minor_minute<=10;end
else
begin
minor_second<=num_minor%10; //计算倒计时时间
minor_minute<=num_minor/10;
end
end
always @ (num_main) begin
if(num_main==0)begin //初始化
main_second<=10;
main_minute<=10;end
else if(num_main>99)begin //倒计时值大于99时,不显示
main_minute<=10;
main_second<=10;end
else
begin
main_minute<=num_main/10;
main_second<=num_main%10;
end
end
always @ (*) begin
case(sel_main) //主干路片选控制
2'b10:cnt_main <= main_second;
2'b01:cnt_main <= main_minute;
endcase
end
always @ (*) begin
case(sel_minor) //支干路片选控制
2'b10:cnt_minor <= minor_second;
2'b01:cnt_minor <= minor_minute;
endcase
end
always @ (*) //译码
case (cnt_main)
0: out_main=NUM_0;
1: out_main=NUM_1;
2: out_main=NUM_2;
3: out_main=NUM_3;
4: out_main=NUM_4;
5: out_main=NUM_5;
6: out_main=NUM_6;
7: out_main=NUM_7;
8: out_main=NUM_8;
9: out_main=NUM_9;
10:out_main=NUM_10;
endcase
always @ (*)
case (cnt_minor)
0: out_minor=NUM_0;
1: out_minor=NUM_1;
2: out_minor=NUM_2;
3: out_minor=NUM_3;
4: out_minor=NUM_4;
5: out_minor=NUM_5;
6: out_minor=NUM_6;
7: out_minor=NUM_7;
8: out_minor=NUM_8;
9: out_minor=NUM_9;
10:out_minor=NUM_10;
endcase
always @ (posedge clk,negedge rst_n) //高频率切换片选管脚
begin //达到显示两位数效果
if(!rst_n)
sel_main<=2'b10;
else
sel_main<={sel_main[0],sel_main[1]};
end
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
sel_minor<=2'b10;
else
sel_minor<={sel_minor[0],sel_minor[1]};
end
endmodule
module top (
clk,
rst_n,
en,
ew,
sn,
out_main,
out_minor,
sel_main,
sel_minor
);
input clk,rst_n,en;
output [5:0] ew,sn;
wire [7:0] count;
wire [5:0] ew,sn;
output out_main;
output out_minor;
output [1:0] sel_main;
output [1:0] sel_minor;
wire [6:0] out_main;
wire [6:0] out_minor;
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));
endmodule
module top_tb;
reg clk,rst_n,en;
wire [5:0] ew,sn;
wire [6:0] out_main;
wire [6:0] out_minor;
wire [1:0] sel_main;
wire [1:0] sel_minor;
top U1(
.clk(clk),
.rst_n(rst_n),
.en(en),
.ew(ew),
.sn(sn),
.out_main(out_main),
.out_minor(out_minor),
.sel_main(sel_main),
.sel_minor(sel_minor));
initial
begin
clk<=1'd0;
forever #500 clk=~clk; //仿真一个1khz的时钟信号
end
initial
begin
rst_n=1'b1; //初始化参数
en=1'b0;
#1 rst_n=1'b0;
#1 rst_n=1'b1;en=1'b1;
end
endmodule
set_property PACKAGE_PIN E3 [get_ports clk]
set_property PACKAGE_PIN K5 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports {sn[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sn[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sn[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ew[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ew[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ew[0]}]
set_property PACKAGE_PIN B9 [get_ports en]
set_property IOSTANDARD LVCMOS33 [get_ports en]
t_property IOSTANDARD LVCMOS33 [get_ports {out_main[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_main[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_main[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_main[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_main[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_main[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_main[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_minor[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_minor[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_minor[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_minor[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_minor[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_minor[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {out_minor[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel_main[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel_main[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel_minor[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel_minor[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ew[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ew[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ew[3]}]
set_property PACKAGE_PIN M14 [get_ports {ew[4]}]
set_property PACKAGE_PIN L14 [get_ports {ew[5]}]
set_property PACKAGE_PIN K13 [get_ports {ew[1]}]
set_property PACKAGE_PIN M15 [get_ports {ew[2]}]
set_property PACKAGE_PIN T9 [get_ports {sn[4]}]
set_property PACKAGE_PIN L12 [get_ports {sn[5]}]
set_property PACKAGE_PIN D5 [get_ports {sn[1]}]
set_property PACKAGE_PIN D6 [get_ports {sn[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sn[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sn[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sn[3]}]
set_property PACKAGE_PIN D3 [get_ports {out_main[6]}]
set_property PACKAGE_PIN C4 [get_ports {out_main[5]}]
set_property PACKAGE_PIN D16 [get_ports {out_main[4]}]
set_property PACKAGE_PIN C7 [get_ports {out_main[3]}]
set_property PACKAGE_PIN D8 [get_ports {out_main[2]}]
set_property PACKAGE_PIN A12 [get_ports {out_main[1]}]
set_property PACKAGE_PIN D9 [get_ports {out_main[0]}]
set_property PACKAGE_PIN E12 [get_ports {sel_main[1]}]
set_property PACKAGE_PIN E13 [get_ports {sel_main[0]}]
set_property PACKAGE_PIN E11 [get_ports {out_minor[6]}]
set_property PACKAGE_PIN T10 [get_ports {out_minor[5]}]
set_property PACKAGE_PIN R5 [get_ports {out_minor[4]}]
set_property PACKAGE_PIN T5 [get_ports {out_minor[3]}]
set_property PACKAGE_PIN R6 [get_ports {out_minor[2]}]
set_property PACKAGE_PIN R7 [get_ports {out_minor[1]}]
set_property PACKAGE_PIN D14 [get_ports {out_minor[0]}]
set_property PACKAGE_PIN D15 [get_ports {sel_minor[1]}]
set_property PACKAGE_PIN F12 [get_ports {sel_minor[0]}]
set_property PACKAGE_PIN L15 [get_ports {ew[3]}]
set_property PACKAGE_PIN L13 [get_ports {ew[0]}]
set_property PACKAGE_PIN C6 [get_ports {sn[3]}]
set_property PACKAGE_PIN A2 [get_ports {sn[0]}]
此测试很重要,如果没有云平台进行测试只能进行仿真,仿真文件如上所示,时间单位为1us,每500us进行时钟信号反转就形成了1khz的时钟信号。
仿真结果如图所示,宏观上看首先测试信号灯是否显示正确,观察ew(表示主干路方向),sn(表示支干路方向)两个值
再观察微观测试结果,截取了一个有代表意义的片段60秒至65秒,这段时间主干路直行黄灯正在闪烁,对应ew的值在0a和08之间变换,由状态表知是在S1状态和S8状态之间切换,每1秒切换一次,一次持续0.5秒,实现了黄灯的闪烁。
再来观察数码管的仿真结果,截取了一个有意义的片段60秒到65秒,60秒是状态S0和S1的交界处,容易出错。系统采用的共阴极4位数码管,片选信号0表示有效,1表示无效,所以片选信号1表示十位,片选2表示个位根据译码表知,60秒前是7e 30代表01秒,是直行绿灯最后一秒倒计时;60秒后倒计时是7e 5b代表05秒,表示黄灯5秒倒计时的开始。
195秒时仿真结果,195秒后重新开始循环。
通常情况仿真结果正确就已经完成了,这里提供云平台的运行结果,仅用来观察系统运行结果,
视频展示效果
观看效果
如果有不清楚的地方欢迎留言评论
第一次写文章,欢迎大家交流,批评。