FPGA-based traffic light design

The epidemic is ruthless, the world is sentimental, Wuhan come on,

At home, do nothing, put the previous courses on the Internet, and review by the way.

Just posted the code, not too detailed, you are all smart people, you will definitely understand it all at once!

The title is like this

Signal control system design

Content and requirements:

    To design an intersection traffic control system, the specific design requirements are as follows:

(1) East-west (indicated by A) and north-south (indicated by B) directions have green, yellow, and red lights, the duration of which is 40 seconds, 5 seconds and 45 seconds, respectively, the schematic diagram and timing of the switching of traffic lights The diagrams are shown in Figure 1 and Figure 2 respectively.

(2) The system is equipped with a clock to display the allowable time of each road in a countdown manner.

(3) When a special situation occurs on any one of the east-west or north-south roads, the system can be manually controlled by the traffic police to immediately enter a special operating state, that is, the red light is all on, the clock stops timing, and all vehicles on the east-west, north-south roads stop passing; when special After the running state is over, the system resumes work and continues normal operation.

 

Figure 2 Timing diagram of traffic lights

(4) Complete all processes: design specification documents, module design, code input, download verification, etc., and finally submit a course design report on the course design itself.

 

This is the timing module for traffic lights

The main thing is to complete the timing module of the traffic light, with few comments, forgive me!

module traffic_led(	
	input   sys_clk,
	input   sys_rst_n,
	input   en,      //使能标记位
	
	output reg  greenA, //LED灯
	output reg  greenB,
	output reg  redA,
	output reg  redB,
	output reg  yellowA,
	output reg  yellowB,
	
	
	output  reg [5:0]  timeA,
    output  reg [5:0]  timeB
);

//parameter NUM_MAX=26'd50_000_000; //1s延迟
parameter NUM_MAX=26'd50_000; //1ms,仿真时用的,1s太长了

parameter state0=4'b0001;
parameter state1=4'b0010;
parameter state2=4'b0100;
parameter state3=4'b1000;




reg [25:0] count; //延时1s  20ns  1000_000_000/20ns=50_000_000次
reg        s_flag;//秒标记位
reg [5:0]  count45; //45s延时
reg        flag45;  //45s标记位

reg [3:0]  current_state;
reg [3:0]  next_state;



//1s延时函数
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		count <=26'd0;
	else  if(en) begin
			if(count < NUM_MAX-1'b1) begin
				count <= count + 1'b1;
				s_flag<=1'b0;
			end
			else begin
				s_flag<=1'b1;
				count<=26'd0;
			end	
	end
	else 
		count <=  count;
end



//45s延时
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin 
		count45 <=6'd45;
		flag45 <=1'b0;
	end 
	else if(en) begin 
		if(s_flag) begin
			if(count45 == 6'b1) begin 
				count45 <=6'd45;
				flag45 <=1'b1;
			end
			else begin
				count45<=count45 - 1'b1;
				flag45 <=1'b0;
			end
		end 
		else begin
			count45 <= count45;
			flag45 <= flag45;
		end
	end
	else begin
	count45 <=count45;
	flag45  <=flag45;
	end
		
end




/*这是用状态机实现的,我使用的是两段式*/

//状态的跳转(时序逻辑)
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		current_state<=state0;
	else
		current_state <= next_state;	
end


//各个状态的下动作
always @(posedge sys_clk) begin
		if(en)begin   
			case(current_state)
				state0:begin
					if(count45 == 6'd5)
						next_state=state1;
					else begin 
						
						greenA <=1'b0;
						greenB <=1'b1;
						redA   <=1'b1;
						redB   <=1'b0;
			            yellowA<= 1'b1;
			            yellowB <=1'b1;
						timeA  <=count45;
						timeB  <=6'd0;
					end
				end
			    state1:begin
					if(flag45) 
						next_state=state2;
					else begin						
						greenA <=1'b1;
						greenB <=1'b1;
						redA   <=1'b1;
						redB   <=1'b0;
			            yellowA<= 1'b0;
			            yellowB <=1'b1;
						timeA  <=count45;
						timeB  <=6'd0;					    
					end
						
																
				end
				
				state2:begin
					if(count45 == 6'd5) 
						next_state=state3;
					else begin 
						greenA <=1'b1;
						greenB <=1'b0;
						redA   <=1'b0;
						redB   <=1'b1;
			            yellowA<= 1'b1;
			            yellowB <=1'b1;
						timeA  <= 6'd0;
						timeB  <=count45;
					end 
				end
				
				state3:begin
					if(flag45) 
						next_state=state0;						
					else begin 	
						greenA <=1'b1;
						greenB <=1'b1;
						redA   <=1'b0;
						redB   <=1'b1;
			            yellowA<= 1'b1;
			            yellowB <=1'b0;
						
						timeA  <= 6'd0;
						timeB  <=count45;
					end
						
				end
				default:next_state=state0;
				
			endcase
		end
		else begin
			greenA <=1'b1;
			greenB <=1'b1;
			redA   <=1'b0;
			redB   <=1'b0;
			yellowA<= 1'b1;
			yellowB <=1'b1;
		end
end

endmodule

Digital tube display module

This code is the code of the nixie tube display module that uses the punctual atom, just a simple modification.

/*数码管显示模块*/
module seg_led(
    input                   sys_clk    ,        // 时钟信号
    input                   sys_rst_n  ,        // 复位信号

    input         [5:0]     timeA,
    input         [5:0]     timeB,      
    input         [3:0]     point  ,        // 小数点具体显示的位置,从高到低,高电平有效
    input                   en     ,        // 数码管使能信号
    input                   sign   ,        // 符号位(高电平显示“-”号)

    output   reg  [3:0]     seg_sel,        // 数码管位选,最左侧数码管为最高位
    output   reg  [7:0]     seg_led         // 数码管段选
);

//分频参数
localparam CLK_DIVIDE=4'd10;
localparam MAX_NUM =13'd5000;  //10分频,用于1ms计时


reg    [3:0]             clk_cnt  ;        // 时钟分频计数器,十分频,计数5个系统周期
reg                      dri_clk  ;        // 数码管的驱动时钟,5MHz 200ns
reg    [15:0]            num;              //要显示的BCD数字
reg    [12:0]            cnt0;  // 数码管驱动时钟计数器,1000_000ns/200ns=5000 13bit
reg                      flag;        // 标志信号(标志着cnt0计数达1ms)
reg    [1:0]             cnt_sel  ;        // 数码管位选计数器
reg    [3:0]             num_disp ;        // 当前数码管显示的数据
reg                      dot_disp ;        // 当前数码管显示的小数点





//拆分显示
wire   [3:0]              data0    ;        // 个位数9 4bit
wire   [3:0]              data1    ;        // 十位数
wire   [3:0]              data2    ;        // 百位数
wire   [3:0]              data3    ;        // 千位数

assign data0 = timeA % 4'd10;
assign data1 = timeA /4'd10;  
assign data2 = timeB % 4'd10; 
assign data3 = timeB / 4'd10;

//对系统时钟10分频(1 5个系统周期 0 5个系统周期),得到的频率为5Mhz的数码管驱动时钟 200ms
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        clk_cnt <= 4'b0;
        dri_clk <= 1'b1; //信号开始为1
    end
    else if(clk_cnt==CLK_DIVIDE/2-1'b1) begin
        clk_cnt <= 4'b0;
        dri_clk <= ~dri_clk;
    end
    else begin
        clk_cnt <=clk_cnt + 1'b1;
        dri_clk <= dri_clk;
    end
end



//将14位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)
always @(posedge dri_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        num <= 16'b0;
    else begin
        if(!point[3]) begin
            num[15:12] <= data3;
            num[11:8]  <= data2;
            num[ 7:4]  <= data1;
            num[ 3:0]  <= data0;
        end
        else begin
            if(data2 || point[2]) begin
                num[11:0]<={data2,data1,data0};
                if(sign)
                    num[15:12] <= 4'd11;//d11表示负号
                else
                    num[15:12] <= 4'd0;     
            end
            else begin
                if(data1 || point[1]) begin
                    num[15:12] <= 4'd10;
                    num[7:0]<={data1,data0};
                    if(sign)
                        num[11:8] <= 4'd11;//d11表示负号
                    else
                        num[11:8] <= 4'd0;
                end
                else begin
                    num[15:12] <= 4'd10;
                    num[11:8] <= 4'd10;
                    num[3:0]<= data0;
                    if(sign)
                        num[7:4] <= 4'd11;//d11表示负号
                    else
                        num[7:4] <= 4'd0;
                end
            end
              
        end    
    end
end

//数码管驱动时钟,计时1ms,输出一个时钟周期的脉冲信号flag
always @ (posedge dri_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        cnt0 <=13'b0;
        flag <=1'b0;
    end
    else if(cnt0 < MAX_NUM -1'b1) begin
        cnt0 <=cnt0 + 1'b1;
        flag <= 1'b0;
    end
    else begin
        flag <= 1'b1;
        cnt0 <= 1'b0;
    end
end


//cnt_sel从0计数到3,用于选择当前处于显示状态的数码管

always @ (posedge dri_clk or negedge sys_rst_n) begin
     if(!sys_rst_n) 
        cnt_sel <= 2'b0;
     else if(flag) begin
        if(cnt_sel < 3)
            cnt_sel=cnt_sel+1'b1;
        else
            cnt_sel <= 2'b0;
     end
     else
        cnt_sel <=cnt_sel;
end

//进行循环位选
always @ (posedge dri_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        seg_sel  <= 4'b1111;              //位选信号低电平有效
        num_disp <= 4'd10;           //默认不显示
        dot_disp <= 1'b1;  //默认不显示小数点
    end
    else begin
        if(en) begin
            case (cnt_sel)
                2'd0 :begin
                    seg_sel  <= 4'b1110;  //显示数码管最低位
                    num_disp <= num[3:0] ;  //显示的数据
                    dot_disp <= ~point[0];  //显示的小数点
                end
                2'd1 :begin
                    seg_sel  <= 'b1101;  //显示数码管第1位
                    num_disp <= num[7:4] ;
                    dot_disp <= ~point[1];
                end
                2'd2 :begin
                    seg_sel  <= 4'b1011;  //显示数码管第2位
                    num_disp <= num[11:8];
                    dot_disp <= ~point[2];
                end
                2'd3 :begin
                    seg_sel  <= 4'b0111;  //显示数码管第3位
                    num_disp <= num[15:12];
                    dot_disp <= ~point[3];
                end
                default: begin
                    seg_sel  <= 4'b1111;              //位选信号低电平有效
                    num_disp <= 4'd10;           //默认
                    dot_disp <= 1'b1;  //默认不显示小数点
                end
            endcase
            
        end
        else begin
            seg_sel  <= 4'b1111;              //位选信号低电平有效
            num_disp <= 4'd10;           //默认显示
            dot_disp <= 1'b1;  //默认不显示小数点
        end
    end
end


always @ (posedge dri_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        seg_led <= 8'hc0;
    else begin
        case(num_disp)
            4'd0 : seg_led <= {dot_disp,7'b1000000}; //显示数字 0
            4'd1 : seg_led <= {dot_disp,7'b1111001}; //显示数字 1
            4'd2 : seg_led <= {dot_disp,7'b0100100}; //显示数字 2
            4'd3 : seg_led <= {dot_disp,7'b0110000}; //显示数字 3
            4'd4 : seg_led <= {dot_disp,7'b0011001}; //显示数字 4
            4'd5 : seg_led <= {dot_disp,7'b0010010}; //显示数字 5
            4'd6 : seg_led <= {dot_disp,7'b0000010}; //显示数字 6
            4'd7 : seg_led <= {dot_disp,7'b1111000}; //显示数字 7
            4'd8 : seg_led <= {dot_disp,7'b0000000}; //显示数字 8
            4'd9 : seg_led <= {dot_disp,7'b0010000}; //显示数字 9
            4'd10: seg_led <= 8'b11111111;           //不显示任何字符
            4'd11: seg_led <= 8'b10111111;           //显示负号(-)
            default:
            seg_led <={dot_disp,7'b11111111};//其他不显示
            
        endcase
    end
end


endmodule


Button delay module

Mainly used to debounce the start or stop button

module key_debounce(
    input            sys_clk,          //外部50M时钟
    input            sys_rst_n,        //外部复位信号,低有效
    
    input            key,              //外部按键输入
    output reg       key_flag,         //按键数据有效信号
	output reg       key_value         //按键消抖后的数据  
    );

//reg define    
reg [31:0] delay_cnt;
reg        key_reg;

//*****************************************************
//**                    main code
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n) begin 
    if (!sys_rst_n) begin 
        key_reg   <= 1'b1;
        delay_cnt <= 32'd0;
    end
    else begin
        key_reg <= key;
        if(key_reg != key)             //一旦检测到按键状态发生变化(有按键被按下或释放)
            delay_cnt <= 32'd1000000;  //给延时计数器重新装载初始值(计数时间为20ms)
        else if(key_reg == key) begin  //在按键状态稳定时,计数器递减,开始20ms倒计时
                 if(delay_cnt > 32'd0)
                     delay_cnt <= delay_cnt - 1'b1;
                 else
                     delay_cnt <= delay_cnt;
             end           
    end   
end

always @(posedge sys_clk or negedge sys_rst_n) begin 
    if (!sys_rst_n) begin 
        key_flag  <= 1'b0;
        key_value <= 1'b1;          
    end
    else begin
        if(delay_cnt == 32'd1) begin   //当计数器递减到1时,说明按键稳定状态维持了20ms
            key_flag  <= 1'b1;         //此时消抖过程结束,给出一个时钟周期的标志信号
            key_value <= key;          //并寄存此时按键的值
        end
        else begin
            key_flag  <= 1'b0;
            key_value <= key_value; 
        end  
    end   
end
    
endmodule 

 

Top-level module

The main realization of this module is to instantiate three modules and set a start or stop button, which is the emergency takeover button.

module trafficlight(
	input  sys_clk,
	input  sys_rst_n,
	input  key,      //开始或停止交通灯
	
	output  greenA,
	output  greenB,
	output  redA,
	output  redB,
	output  yellowA,
	output  yellowB,
	
	output     [3:0]     seg_sel,        // 数码管位选,最左侧数码管为最高位
    output     [7:0]     seg_led         // 数码管段选
);


parameter  POINT=4'd0000;
parameter  SIGN=1'b0;

wire [5:0] timeA;
wire [5:0] timeB;
wire    key_flag;
wire    key_value;
reg     en;

//开始或停止交通灯
always @ (posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)  
		en<=1'b1;
    else begin  
	  if(key_flag && (~key_value))  //判断按键是否有效按下
		en<=~en;
	  else 
		en<=en;
    end   		
end

traffic_led u_traffic_led(	
	.sys_clk(sys_clk),
	.sys_rst_n(sys_rst_n),
	.greenA(greenA),
	.greenB(greenB),
	.redA(redA),
	.redB(redB),
	.yellowA(yellowA),
	.yellowB(yellowB),
	.en(en),
	
	.timeA(timeA),
    .timeB(timeB)
);

seg_led u_seg_led(
    .sys_clk(sys_clk)    ,        // 时钟信号
    .sys_rst_n(sys_rst_n)  ,        // 复位信号

    .timeA(timeA),
    .timeB(timeB),      // 6位数码管要显示的数值
    .point(	POINT)  ,        // 小数点具体显示的位置,从高到低,高电平有效
    .en(en)     ,        // 数码管使能信号
    .sign(SIGN)   ,        // 符号位(高电平显示“-”号)

    .seg_sel(seg_sel),        // 数码管位选,最左侧数码管为最高位
    .seg_led(seg_led)         // 数码管段选
);

key_debounce u_key_debounce(
    .sys_clk(sys_clk),          //外部50M时钟
    .sys_rst_n(sys_rst_n),        //外部复位信号,低有效
    
    .key(key),              //外部按键输入
    .key_flag(key_flag),         //按键数据有效信号
	.key_value(key_value)         //按键消抖后的数据  
    );

endmodule

That's ok, simple!

 

Simulation diagram

The following is the simulation diagram. The simulation time is in milliseconds. One second is too long and the simulation is too troublesome.

This is the green light simulation time, 40s

This is the simulation time of the red light, 45s

This is the green light simulation time, 5s

Almost that's it

Guess you like

Origin blog.csdn.net/weixin_40943540/article/details/104765890