首先,简要阐述一下本次设计所实现的基本功能。
系统输入两组时钟,一个是50M时钟,一个是1HZ时钟,另外,系统还有一个复位信号,一个拨码开关信号。输出两组LED灯,分别表示东西方向、南北方向的红绿灯。每组灯为6位宽,表示各个方向的红、黄、绿灯。示意图如下所示:
要实现:
东西方向红灯亮,南北方向绿灯亮,保持35S;
东西方向红灯亮,南北方向黄灯亮,保持5S;
东西方向绿灯亮,南北方向红灯亮,维持35S;
东西方向黄灯亮,南北方向红灯亮,维持5S。
如此反复循环。
交通灯的自动工作受到拨码开关的控制,开关拨上时,系统正常工作,否则,全部亮红灯。各个状态的时间倒计时会通过数码管显示出来,数码管的高两位显示的是倒计时的时间,后六位可以显示任意指定的数字(可以是日期,可以是学号,等等)
下面讲解一下verilog实现的过程:
还是采用自顶层向下的设计思想,整体的原理图如下:
分为交通灯控制模块,和数码管显示模块。数码管显示模块的内容之前已经介绍过,这里不再详细介绍。但是这次使用的数码管,在硬件上和之前有所不同,但是大致的原理一样。所以这里只给出代码:
module segshow(
input sys_clk,sys_rest,
input [26:0] shu,
output reg [2:0] sel,
output reg [6:0] seg_led
);
//parameter MSNUM=14'd50000; //实物使用这个参数
parameter MSNUM=14'd2; //仿真使用这个参数
reg [12:0] MSCNT;
reg MS_flag;
reg [3:0] num_display;
reg [2:0] sel_num; //选择哪一位数码管被点亮
//wire define
wire [3:0] shu0 ; // 个位数
wire [3:0] shu1 ; // 十位数
wire [3:0] shu2 ; // 百位数
wire [3:0] shu3 ; // 千位数
wire [3:0] shu4 ; // 万位数
wire [3:0] shu5 ; // 十万位数
wire [3:0] shu6 ; // 百万位数
wire [3:0] shu7 ; // 千万位数
//提取显示数值所对应的十进制数的各个位
assign shu0 = shu % 4'd10; // 个位数
assign shu1 = shu / 4'd10 % 4'd10 ; // 十位数
assign shu2 = shu / 7'd100 % 4'd10 ; // 百位数
assign shu3 = shu / 10'd1000 % 4'd10 ; // 千位数
assign shu4 = shu / 14'd10000 % 4'd10; // 万位数
assign shu5 = shu / 17'd100000%4'd10; // 十万位数
assign shu6 = shu / 20'd1000000%4'd10; // 百万位数
assign shu7 = shu / 23'd10000000; // 千万位数
always @(posedge sys_clk or negedge sys_rest) begin //产生1ms脉冲
if(!sys_rest)
begin
MSCNT<=13'd0;
MS_flag<=1'b0;
end
else if(MSCNT==MSNUM-1)
begin
MSCNT<=13'd0;
MS_flag<=1'b1;
end
else
begin
MSCNT<=MSCNT+1;
MS_flag<=1'b0;
end
end
always @(posedge sys_clk or negedge sys_rest) begin
if(!sys_rest)
sel_num<=0;
else if(MS_flag)
begin
if(sel_num<3'd7)
sel_num<=sel_num+1;
else
sel_num<=0;
end
else
sel_num<=sel_num;
end
always @(posedge sys_clk or negedge sys_rest) begin
if(!sys_rest)
sel<=3'b000;
else
begin
case(sel_num)
3'd0: begin
sel<= 3'b000; //显示数码管最低位
num_display<=shu0;
end
3'd1: begin
sel<= 3'b001; //显示数码管第1位
num_display<=shu1;
end
3'd2: begin
sel<= 3'b010; //显示数码管第2位
num_display<=shu2;
end
3'd3: begin
sel<= 3'b011; //显示数码管第3位
num_display<=shu3;
end
3'd4: begin
sel<= 3'b100; //显示数码管第4位
num_display<=shu4;
end
3'd5: begin
sel<= 3'b101; //显示数码管第5位
num_display<=shu5;
end
3'd6: begin
sel<= 3'b110;
num_display<=shu6;
end
3'd7: begin
sel<= 3'b111; //显示数码管最高位
num_display<=shu7;
end
default sel<= 3'b000;
endcase
end
end
always @(posedge sys_clk or negedge sys_rest) begin
if(!sys_rest)
seg_led<=7'b0111111;
else
begin
case(num_display)
4'h0 : seg_led <= 7'b0111111;//0
4'h1 : seg_led <= 7'b0000110;//1
4'h2 : seg_led <= 7'b1011011;//2
4'h3 : seg_led <= 7'b1001111;//3
4'h4 : seg_led <= 7'b1100110;//4
4'h5 : seg_led <= 7'b1101101;//5
4'h6 : seg_led <= 7'b1111101;//6
4'h7 : seg_led <= 7'b0000111;//7
4'h8 : seg_led <= 7'b1111111;//8
4'h9 : seg_led <= 7'b1101111;//9
default : seg_led <= 7'b0111111;//0
endcase
end
end
endmodule
那么重点是LED的控制逻辑。这里的控制,主要用到了状态机,我用的是二段式状态机,先给出代码,再根据代码讲解。
module jtd_led(
clk_50m,clk_1hz,rst,k,light1,light2,data
);
input clk_50m;
input clk_1hz;
input rst;
input k; //手动控制信号
output [5:0] light1; //东西方向的灯 绿 黄 红 绿 黄 红
output [5:0] light2; //南北方向的灯 绿 黄 红 绿 黄 红
output [26:0] data;
reg [5:0] light1; //定义信号类型
reg [5:0] light2;
reg [3:0] state ;
reg [3:0] next_state ;
reg [5:0] jishu;
wire [3:0] jishu_ge;
wire [3:0] jishu_shi;
reg [5:0] jishu_num;
parameter S0 = 4'b0000 ;
parameter S1 = 4'b0001 ;
parameter S2 = 4'b0010 ;
parameter S3 = 4'b0100 ;
parameter S4 = 4'b1000 ;
//二段式状态机
always @(posedge clk_50m or negedge rst) begin
if(!rst)
state <= S0;
else
state <= next_state;
end
always @(posedge clk_1hz) begin
if(k==0) begin
case (state)
S0: if (!rst) begin
next_state <= S1;
jishu_num<=6'd35; //状态维持35S
light1 = 6'b001_001; //红灯
light2 = 6'b100_100; //绿灯
end
S1: if (jishu == 6'd0) begin
next_state <= S2;
jishu_num<=6'd5; //维持5S
light1 = 6'b001_001; //红灯
light2 = 6'b010_010; //黄灯
end
S2: if (jishu == 6'd0) begin
next_state <= S3;
jishu_num<=6'd35; //维持35S
light1 = 6'b100_001; //绿灯
light2 = 6'b001_001; //红灯
end
S3: if (jishu == 6'd0) begin
next_state <= S4;
jishu_num<=6'd5; //维持5S
light1 = 6'b010_010; //黄灯
light2 = 6'b001_001; //红灯
end
S4: if (jishu == 6'd0) begin
next_state <= S1;
jishu_num<=6'd35; //状态维持35S
light1 = 6'b001_001; //红灯
light2 = 6'b100_100; //绿灯
end
endcase
end
else //如果K1拨上去了
begin
light1 = 6'b001_001;
light2 = 6'b001_001; //全部亮红灯
end
end
//时钟控制模块 输入1HZ
always @(posedge clk_1hz or negedge rst) begin
if (!rst) begin
jishu <= 6'd0;
end
else if(k==0) begin // 工作在自动模式
if (jishu == 6'd0)
jishu <= jishu_num;
else
jishu <= jishu-1;
end
else
jishu<=jishu; //如果K1拨上去了 时间不变
end
assign jishu_ge=jishu%10;
assign jishu_shi=jishu/10;
assign data=jishu_ge*10000000+jishu_shi*1000000+030616;
endmodule
重点在于状态机的运转流程,以及状态的切换。程序一开始会从S0状态,一直运行到S4状态,但随后只会在 S1 S2 S3 S4里面循环。S0是给上电复位设置的初始状态。
在时钟控制模块里面,让jishu这个变量一直减一,在状态机里面,给jishu赋值,当jishu变量减到0时,就进行状态的切换。切换之后,到了下一个状态,又重新对jishu进行赋值。这样就实现的LED灯在不同的状态亮不同的时间了!文章来源:https://www.toymoban.com/news/detail-719739.html
然后时间又被传递后显示模块进行了显示。文章来源地址https://www.toymoban.com/news/detail-719739.html
到了这里,关于FPGA项目(9)——基于FPGA的交通灯设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!