基于FPGA的交通灯
前言
交通信号灯往往由红、绿、黄三种颜色的灯组成。红灯亮的时候,禁止通行;绿灯亮的时候,可以通行;黄灯亮的时候,提示通行时间已经结束,马上要转换为红灯。
本设计采用Verilog HDL语言编写,实现了十字路口的交通灯设计。
一、系统设计
根据实验任务,我们可以大致规划出系统的控制流程:交通灯控制模块将需要显示的时间 数据连接到数码管显示模块,同时将状态信号连接到led灯控制模块,然后数码管显示模块和 led灯控制模块驱动交通信号灯外设工作。系统总体框架图如图所示
二、硬件设计
拓展外接电路如下:
三.软件设计
1.顶层设计
顶层设计完成了对其它三个子模块的例化、实现了子模块间 的信号连接、并将led灯和数码管的驱动信号输出给外接设备(交通信号灯外设)。
traffic_light u0_traffic_light(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.ew_time (ew_time),
.sn_time (sn_time),
.state (state)
);
seg_led u1_seg_led(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n),
.ew_time (ew_time),
.sn_time (sn_time),
.en (1'b1),
.sel (sel),
.seg_led (seg_led)
);
led u2_led(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n),
.state (state ),
.led (led )
);
2.交通灯控制设计
交通灯控制模块是本次实验的核心代码,这个模块 控制信号灯的状态转换,将实时的状态信号state[1:0]输出给led灯控制模块(led),同时将各个方向的实时时间数据输出给数码管显示模块 (seg_led)。
//计数周期为0.5s的计数器
always @ (posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
clk_cnt <= 25'b0;
else if (clk_cnt < WIDTH - 1'b1)
clk_cnt <= clk_cnt + 1'b1;
else
clk_cnt <= 25'b0;
end
//产生频率为1hz的时钟
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
clk_1hz <= 1'b0;
else if(clk_cnt == WIDTH - 1'b1)
clk_1hz <= ~ clk_1hz;
else
clk_1hz <= clk_1hz;
end
/切换交通信号灯工作的4个状态,并产生数码管要显示的时间数据
always @(posedge clk_1hz or negedge sys_rst_n)begin
if(!sys_rst_n)begin
state <= 2'd0;
time_cnt <= TIME_LED_G ; //状态1持续的时间
end
else begin
case (state)
2'b0: begin //状态1
ew_time <= time_cnt + TIME_LED_Y - 1'b1;//东西方向数码管要显示的时间数据
sn_time <= time_cnt - 1'b1; //南北方向数码管要显示的时间数据
if (time_cnt > 1)begin //time_cnt等于1的时候切换状态
time_cnt <= time_cnt - 1'b1;
state <= state;
end
else begin
time_cnt <= TIME_LED_Y; //状态2持续的时间
state <= 2'b01; //切换到状态2
end
end
2'b01: begin //状态2
ew_time <= time_cnt - 1'b1;
sn_time <= time_cnt - 1'b1;
if (time_cnt > 1)begin
time_cnt <= time_cnt - 1'b1;
state <= state;
end
else begin
time_cnt <= TIME_LED_G; //状态3持续的时间
state <= 2'b10; //切换到状态3
end
end
2'b10: begin //状态3
ew_time <= time_cnt - 1'b1;
sn_time <= time_cnt + TIME_LED_Y - 1'b1;
if (time_cnt > 1)begin
time_cnt <= time_cnt - 1'b1;
state <= state;
end
else begin
time_cnt <= TIME_LED_Y; //状态4持续的时间
state <= 2'b11; //切换到转态4
end
end
2'b11: begin //状态4
ew_time <= time_cnt - 1'b1;
sn_time <= time_cnt - 1'b1;
if (time_cnt > 1)begin
time_cnt <= time_cnt - 1'b1;
state <= state;
end
else begin
time_cnt <= TIME_LED_G;
state <= 2'b0; //切换到状态1
end
end
default: begin
state <= 2'b0;
time_cnt <= TIME_LED_G;
end
endcase
end
end
3.数码管显示设计
接收交通灯控制模块传递过来的实时时间数据,并以此驱动对应的数码管,将数据显示出来。
//计数1ms
always @ (posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt_1ms <= 15'b0;
else if (cnt_1ms < WIDTH - 1'b1)
cnt_1ms <= cnt_1ms + 1'b1;
else
cnt_1ms <= 15'b0;
end
//计数器,用来切换数码管点亮的4个状态
always @ (posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt_state <= 2'd0;
else if (cnt_1ms == WIDTH - 1'b1)
cnt_state <= cnt_state + 1'b1;
else
cnt_state <= cnt_state;
end
//先显示东西方向数码管的十位,然后是个位。再显示南北方向数码管的十位,然后个位
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
sel <= 4'b1111;
num <= 4'b0;
end
else if(en) begin
case (cnt_state)
3'd0 : begin
sel <= 4'b1110; //驱动东西方向数码管的十位
num <= data_ew_0;
end
3'd1 : begin
sel <= 4'b1101; //驱动东西方向数码管的个位
num <= data_ew_1;
end
3'd2 : begin
sel <= 4'b1011; //驱动南北方向数码管的十位
num <= data_sn_0;
end
3'd3 : begin
sel <= 4'b0111; //驱动南北方向数码管的个位
num <= data_sn_1 ;
end
default : begin
sel <= 4'b1111;
num <= 4'b0;
end
endcase
end
else begin
sel <= 4'b1111;
num <= 4'b0;
end
end
//数码管要显示的数值所对应的段选信号
always @ (posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
seg_led <= 8'b0;
else begin
case (num)
4'd0 : seg_led <= 8'b1100_0000;
4'd1 : seg_led <= 8'b1111_1001;
4'd2 : seg_led <= 8'b1010_0100;
4'd3 : seg_led <= 8'b1011_0000;
4'd4 : seg_led <= 8'b1001_1001;
4'd5 : seg_led <= 8'b1001_0010;
4'd6 : seg_led <= 8'b1000_0010;
4'd7 : seg_led <= 8'b1111_1000;
4'd8 : seg_led <= 8'b1000_0000;
4'd9 : seg_led <= 8'b1001_0000;
default : seg_led <= 8'b1100_0000;
endcase
end
end
4.LED灯控制设计
根据接收到的实时状态信号state[1:0],驱动led发光。
//计数时间为0.2s的计数器,用于让黄灯闪烁
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt <= 25'b0;
else if (cnt < TWINKLE_CNT - 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= 25'b0;
end
//在交通灯的四个状态里,使相应的led灯发光
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
led <= 6'b100100;
else begin
case(state)
2'b00:led<=6'b100010; //led寄存器从高到低分别驱动:东西向
//红绿黄灯,南北向红绿黄灯
2'b01: begin
led[5:1]<=5'b10000;
if(cnt == TWINKLE_CNT - 1'b1) //计数满0.2秒让黄灯的亮灭状况切换一次
//产生闪烁的效果
led[0] <= ~led[0];
else
led[0] <= led[0];
end
2'b10:led<=6'b010100;
2'b11: begin
led[5:4]<=2'b00;
led[2:0]<=3'b100;
if(cnt == TWINKLE_CNT - 1'b1)
led[3] <= ~led[3];
else
led[3] <= led[3];
end
default:led<=6'b100100;
endcase
end
end
endmodule
总结
实现了简单十字路口的控制,可以调整切换时间。