FPGA项目(9)——基于FPGA的交通灯设计

        首先,简要阐述一下本次设计所实现的基本功能。

        系统输入两组时钟,一个是50M时钟,一个是1HZ时钟,另外,系统还有一个复位信号,一个拨码开关信号。输出两组LED灯,分别表示东西方向、南北方向的红绿灯。每组灯为6位宽,表示各个方向的红、黄、绿灯。示意图如下所示:

FPGA项目(9)——基于FPGA的交通灯设计_第1张图片

         要实现:

        东西方向红灯亮,南北方向绿灯亮,保持35S;

        东西方向红灯亮,南北方向黄灯亮,保持5S;

        东西方向绿灯亮,南北方向红灯亮,维持35S;

        东西方向黄灯亮,南北方向红灯亮,维持5S。

        如此反复循环。

        交通灯的自动工作受到拨码开关的控制,开关拨上时,系统正常工作,否则,全部亮红灯。各个状态的时间倒计时会通过数码管显示出来,数码管的高两位显示的是倒计时的时间,后六位可以显示任意指定的数字(可以是日期,可以是学号,等等)

        下面讲解一下verilog实现的过程:

        还是采用自顶层向下的设计思想,整体的原理图如下:

FPGA项目(9)——基于FPGA的交通灯设计_第2张图片

        分为交通灯控制模块,和数码管显示模块。数码管显示模块的内容之前已经介绍过,这里不再详细介绍。但是这次使用的数码管,在硬件上和之前有所不同,但是大致的原理一样。所以这里只给出代码:

  

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灯在不同的状态亮不同的时间了!

        然后时间又被传递后显示模块进行了显示。

你可能感兴趣的:(fpga开发,嵌入式硬件)