## 简易交通灯
本节将向您介绍Verilog语法之中的精髓内容——状态机,并且将利用状态机实现十字路口的交通灯。
\\
### 1. 硬件说明与实现项目框图
{{ ::jiaotongdeng.png?300 |}}
上图为十字路口交通示意图分之路与主路,要求如下:
* 交通灯主路上绿灯持续15s的时间,黄灯3s的时间,红灯10s的时间;
* 交通灯支路上绿灯持续7s的时间, 黄灯持续3秒的时间,红灯18秒的时间;
{{ ::状态机框架.png?300 |}}
根据上述要求,状态机设计框架分析如下:
* S1:主路绿灯点亮,支路红灯点亮,持续15s的时间;
* S2:主路黄灯点亮,支路红灯点亮,持续3s的时间;
* S3:主路红灯点亮,支路绿灯点亮,持续10s的时间;
* S4:主路红灯点亮,支路黄灯点亮,持续3s的时间;
//
{{ ::状态示意图.png?500 |}}
//
}
### 2. 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'd7,
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
\\
\\
### 3. 引脚分配
小脚丫上正好有4路按键和4路开关,可以用来作为输入信号分别控制数码管的输出。按照下面表格定义输入输出信号
\\
^ 信号 ^ 引脚 ^ 信号 ^ 引脚 ^
| **clk** | J5 | **rst** | J9 ^
| **out[0]** | E14 | **out[1]** | E15 ^
| **out[2]** | G15 | **out[3]** | D12 ^
| **out[4]** | C14 | **out[5]** | C15 ^
\\
配置好以后编译下载程序。您也可以试试修改程序,观察修改代码对于FPGA内部电路所造成的影响。\\
### 4. 小结
状态机是一类很重要的时序逻辑电路,是许多数字系统的核心部件,掌握状态机的使用是利用FPGA与CPLD进行开发的一项必会技能,本小节的交通灯程序即是利用三段式状态机描述方法实现的,希望读者能够快速掌握这项技能。