基于FPGA的交通灯设计

疫情无情,人间有情,武汉加油,

在家里,无所事事,把以前的课设给放到网上吧,也顺便复习一下。

就贴了一下代码,不是太详细,你们都是聪明人,一下子肯定看懂了!

题目是这样子的

信号控制系统设计

内容及要求:

    设计一个十字路口交通控制系统,具体设计要求如下:

(1)东西(用A表示)、南北(用B表示)方向均有绿灯、黄灯、红灯指示,其持续时间分别是40秒、5秒和45秒, 交通灯运行的切换示意图和时序图分别如图1、图2所示。

(2)系统设有时钟,以倒计时方式显示每一路允许通行的时间。

(3)当东西或南北两路中任一路出现特殊情况时,系统可由交警手动控制立即进入特殊运行状态,即红灯全亮,时钟停止计时,东西、南北两路所有车辆停止通行;当特殊运行状态结束后,系统恢复工作,继续正常运行。

扫描二维码关注公众号,回复: 12585618 查看本文章

图2  交通灯时序图

(4)完成全部流程:设计规范文档、模块设计、代码输入、下载验证等,最后就课程设计本身提交一篇课程设计报告。

 

这是交通灯的时序模块

主要的是完成交通灯的时序模块,注释很少,忘见谅!

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

数码管显示模块

这个代码我是采用正点原子的数码管显示模块的代码,只是进行了一下简单的修改。

/*数码管显示模块*/
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


按键的延迟模块

主要是用于开始或停止按钮的消抖

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 

 

顶层模块

这个模块主要实现的是实例化三个模块,设置一个开始或停止按钮,就是紧急接管按钮。

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

这样就ok了,简单吧!

 

仿真图

下面是仿真图,仿真的时间我使用的是毫秒为单位,一秒太长了,仿真太麻烦

这是绿灯仿真时间,40s

这是红灯的仿真时间,45s

这是绿灯仿真时间,5s

差不多就这样了

猜你喜欢

转载自blog.csdn.net/weixin_40943540/article/details/104765890