**这是本文档旧的修订版!**

简易交通灯

本节将向您介绍Verilog语法之中的精髓内容——状态机,并且将利用状态机实现十字路口的交通灯。


====硬件说明与实现项目框图====

上图为十字路口交通示意图分之路与主路,要求如下: * 交通灯主路上绿灯持续15s的时间,黄灯3s的时间,红灯10s的时间; * 交通灯支路上绿灯持续7s的时间, 黄灯持续3秒的时间,红灯18秒的时间; 根据上述要求,状态机设计框架分析如下: * S1:主路绿灯点亮,支路红灯点亮,持续15s的时间; * S2:主路黄灯点亮,支路红灯点亮,持续3s的时间; * S3:主路红灯点亮,支路绿灯点亮,持续10s的时间; * S4:主路红灯点亮,支路黄灯点亮,持续3s的时间;

====Verilog代码====

首先是时钟分频部分:

//********************************************************
//   Copyright(c)2016, STEP FPGA 
//   All rights reserved
//   File name       :   divide.v
//   Module name     :   divide
//   Author          :   STEP
//   Email           :   info@stepfpga.com
//   Data            :   2016/08/01
//   Version         :   V1.0
//   Description     :   
//
//   Modification history
//   ----------------------------------------------------------------------------
// Version       
// Description
//
//********************************************************
 
 
//*******************
//DEFINE MODULE PORT
//*******************
module divide
(
	//INPUT
	clk			,
	rst_n			,
	//OUTPUT
	clkout			
);
	//*******************
	//DEFINE PARAMETER
	//*******************
	parameter			WIDTH	=	3;
	parameter			N		=	5;
 
	//*******************
	//DEFINE INPUT
	//*******************
	input 	clk,rst_n;     
 
    //*******************
	//DEFINE OUTPUT
	//*******************
	output	clkout;
 
	//********************
	//OUTPUT ATTRIBUTE
	//********************
	//REGS
	reg 	[WIDTH-1:0]		cnt_p,cnt_n;
	reg						clk_p,clk_n;
 
	assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;
 
	//Sequential logic style
	always @ (posedge clk)
		begin
			if(!rst_n)
				cnt_p<=0;
			else if (cnt_p==(N-1))
				cnt_p<=0;
			else cnt_p<=cnt_p+1;
		end
 
	always @ (negedge clk)
		begin
			if(!rst_n)
				cnt_n<=0;
			else if (cnt_n==(N-1))
				cnt_n<=0;
			else cnt_n<=cnt_n+1;
		end
 
	always @ (posedge clk)
		begin
			if(!rst_n)
				clk_p<=0;
			else if (cnt_p<(N>>1))  
				clk_p<=0;
			else 
				clk_p<=1;
		end
 
	always @ (negedge clk)
		begin
			if(!rst_n)
				clk_n<=0;
			else if (cnt_n<(N>>1))  
				clk_n<=0;
			else 
				clk_n<=1;
		end
endmodule     


//********************************************************
//   Copyright(c)2016, STEP FPGA 
//   All rights reserved
//   File name       :   traffic.v
//   Module name     :   traffic
//   Author          :   STEP
//   Email           :   info@stepfpga.com
//   Data            :   2016/08/01
//   Version         :   V1.0
//   Description     :   
//
//   Modification history
//   ----------------------------------------------------------------------------
// Version       
// Description
//
//********************************************************
 
 
//*******************
//DEFINE MODULE PORT
//*******************
module traffic
(
	//INPUT
	clk			,
	rst_n		,
	//OUTPUT
	out			
);
	//*******************
	//DEFINE INPUT
	//*******************
	input 	clk,rst_n;     
 
    //*******************
	//DEFINE OUTPUT
	//*******************
	output	reg[5:0]	out;
 
	//*******************
	//DEFINE PARAMETER
	//*******************   
 
	parameter      	S1 = 4'b00,    
					S2 = 4'b01,
					S3 = 4'b10,
					S4 = 4'b11;
 
	parameter		time_s1 = 4'd15,
					time_s2 = 4'd3,
					time_s3 = 4'd10,
					time_s4 = 4'd3;
 
	parameter		led_s1 = 6'b101011,
					led_s2 = 6'b001011,
					led_s3 = 6'b011101,
					led_s4 = 6'b011001;
 
	//*********************
	//INNER SIGNAL DECLARATION
	//*********************
	//REGS	
	reg 	[3:0] 	timecont;
	reg 	[1:0] 	cur_state,next_state; 
 
	//WIRES
	wire			clk1h;
 
	divide #(.WIDTH(32),.N(12000000)) CLK1H (
								.clk(clk),
								.rst_n(rst_n),
								.clkout(clk1h));
	//Sequential logic style
	always @ (posedge clk1h)
	begin
		if(!rst_n) cur_state <= S1;
        else cur_state <= next_state;
	end
 
	always @ (cur_state or rst_n or timecont)
	begin
		if(!rst_n) begin
			next_state = S1;
			end
		else begin
			case(cur_state)
				S1:begin
					if(timecont==1) next_state = S2;
					else next_state = S1;
				end
 
                S2:begin
					if(timecont==1) next_state = S3;
					else next_state = S2;
				end
 
                S3:begin
					if(timecont==1) next_state = S4;
					else next_state = S3;
				end
 
                S4:begin
					if(timecont==1) next_state = S1;
					else next_state = S4;
				end
 
				default: next_state = S1;
			endcase
		end
	end
 
	always @ (posedge clk1h)
	begin
		if(!rst_n) begin
			out <= led_s1;
			timecont <= time_s1;
		end else begin
			case(next_state)
				S1:begin
					out <= led_s1;
					if(timecont == 1) timecont <= time_s1;
					else timecont <= timecont - 1;
				end
 
				S2:begin
					out <= led_s2;
					if(timecont == 1) timecont <= time_s2;
					else timecont <= timecont - 1;
				end
 
				S3:begin
					out <= led_s3;
					if(timecont == 1) timecont <= time_s3;
					else timecont <= timecont - 1;
				end
 
				S4:begin
					out <= led_s4;
					if(timecont == 1) timecont <= time_s4;
					else timecont <= timecont - 1;
				end
 
				default:begin
					out <= led_s1;
					end
			endcase
		end
	end
endmodule



引脚分配


小脚丫上正好有4路按键和4路开关,可以用来作为输入信号分别控制数码管的输出。按照下面表格定义输入输出信号

信号 引脚 信号 引脚
segdata1(0) M7 segdata2(0) L14
segdata1(1) M8 segdata2(1) M13
segdata1(2) M9 segdata2(2) M14
segdata1(3) M10 segdata2(3) N14



信号 引脚 信号 引脚
segled1(0) A10 segled2(0) C12
segled1(1) C11 segled2(1) B14
segled1(2) F2 segled2(2) J1
segled1(3) E1 segled2(3) H1
segled1(4) E2 segled2(4) H2
segled1(5) A9 segled2(5) B12
segled1(6) B9 segled2(6) A11
segled1(7) F1 segled2(7) K1
segled1(8) C9 segled2(8) A12


配置好以后编译下载程序。这样可以通过按键或者开关来控制相应的数码管显示数字。如果你想显示16进制的AbCDeF在数码管,可以试试修改程序。这时候一定要定义一个16*9的存储器来初始化。

====小结====

了解了小脚丫数码管的工作原理,在下个实验我们将进行到有趣的时序逻辑。首先是如何控制时钟分频