# 数字电路课程设计

# 1. 课设要求

学习利用计数器和状态机设计十字路口交通灯控制器。设计一个简单十字路口交通灯控制器。该控制器控制甲乙两道的红、黄、绿三色灯,指挥交通和行人安全通行。复杂十字路口交通灯控制器要比简单交通灯控制器增加一些功能,如倒计时时间显示,左转弯(左拐)、指示灯闪烁及特殊紧急情况的处理等.

# 2. 设计方案

甲道 --> 绿灯 25s 黄灯 5s 左转灯 15s 黄灯 5s 红灯 40s
乙道 --> 红灯 50s 绿灯 20s 黄灯 5s 左转灯 10s 黄灯 5s

# 3. 设计思想描述

  1. 保证主干道的绿灯时间大于支路的绿灯时间,保证主干道的通行能力大于支路的通行能力.

  2. 当使能端为高电平时,甲道状态为 绿->黄->左->黄->红 ,乙道状态为 红->绿->黄->左->黄 ,并依次循环进行下去.

  3. 每个方向上的红灯时间都应该为绿灯 + 黄灯 + 左转灯 + 黄灯时间

  4. 状态 LAMPA: 控制 A 方向的四盏灯的亮灭,状态 LAMPB: 控制 B 方向的四盏灯的亮灭,状态如下表所示.
    <table>
    <tr>
    <th colspan="4">LAMPA</th>
    <th colspan="4">LAMPB</th>
    </tr>
    <tr>
    <th> 绿 </th>
    <th> 黄 </th>
    <th> 左 </th>
    <th> 红 </th>
    <th> 绿 </th>
    <th> 黄 </th> <!-- 高电平亮 -->
    <th> 左 </th>
    <th> 红 </th>
    </tr>
    <tr>
    <th>0</th>
    <th>0</th>
    <th>0</th>
    <th>1</th>
    <th>0</th>
    <th>0</th>
    <th>0</th>
    <th>1</th>
    </tr>
    <tr>
    <th>0</th>
    <th>0</th>
    <th>1</th>
    <th>0</th>
    <th>0</th>
    <th>0</th>
    <th>1</th>
    <th>0</th>
    </tr>
    <tr>
    <th>0</th>
    <th>1</th>
    <th>0</th>
    <th>0</th>
    <th>0</th>
    <th>1</th>
    <th>0</th>
    <th>0</th>
    </tr>
    <tr>
    <th>1</th>
    <th>0</th>
    <th>0</th>
    <th>0</th>
    <th>1</th>
    <th>0</th>
    <th>0</th>
    <th>0</th>
    </tr>
    </table>

  5. 将 DE2-70 实验板上的 50MHz 时钟信号分频为 1Hz 的时钟信号,用于计时.

  6. 使用七段数字显示器显示甲乙两道上的倒计时时间,将每条通道的倒计时时间分为两个部分,一个部分为十位数,一个部分为个位数,使用两个七段数字显示器分别显示十位数和个位数.

  7. 对应 DE2-70 管脚图屏蔽掉开发板上的小数点位显示

  8. 当遇到紧急情况时,甲乙两道全部变成无倒计时的红灯.

  9. 夜间警惕模式,甲乙两道都变成黄灯闪烁

# 4. 代码实现

# 4.1 BCD 码转七段显示译码器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 实现将BCD码转为七段显示译码器
module _BCDto7LED(
input [3:0] bcd_high,//高位BCD码
input [3:0] bcd_low,//低位BCD码
output reg [6:0] segh,//高位七段显示码
output reg [6:0] segl//低位七段显示码
);
always @(bcd_high , bcd_low)
begin
//高位七段显示码
// g~a 7~1
case(bcd_high):
4'h0: segh = 7'b1000000;//数字0 ---a----
4'h1: segh = 7'b1111001;//数字1 | |
4'h2: segh = 7'b0100100;//数字2 f b
4'h3: segh = 7'b0110000;//数字3 | |
4'h4: segh = 7'b0011001;//数字4 ---g----
4'h5: segh = 7'b0010010;//数字5 | |
4'h6: segh = 7'b0000010;//数字6 e c
4'h7: segh = 7'b1111000;//数字7 | |
4'h8: segh = 7'b0000000;//数字8 ---d----
4'h9: segh = 7'b0010000;//数字9
default : segh = 7'b1111111;//其他情况全灭
endcase
//低位七段显示码
// g~a 7~1
case(bcd_low):
4'h0: segl = 7'b1000000;//数字0 ---a----
4'h1: segl = 7'b1111001;//数字1 | |
4'h2: segl = 7'b0100100;//数字2 f b
4'h3: segl = 7'b0110000;//数字3 | |
4'h4: segl = 7'b0011001;//数字4 ---g----
4'h5: segl = 7'b0010010;//数字5 | |
4'h6: segl = 7'b0000010;//数字6 e c
4'h7: segl = 7'b1111000;//数字7 | |
4'h8: segl = 7'b0000000;//数字8 ---d----
4'h9: segl = 7'b0010000;//数字9
default : segl = 7'b1111111;//其他情况全灭
endcase
end
endmodule

# 4.2 时钟降频

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//降频模块
// 50MHz -> 1Hz 计数范围0~24999999
module Divider50MHz(
input CLK_50MHzInput;//50MHz时钟输入
input RST;//复位
output reg CLK_1HzOut//1Hz时钟输出
);
reg[24:0] Count;//计数器,达到最大值输出信号翻转
always@(posedge CLK_50MHzInput,negedge RST)
begin
if(!RST)
begin
CLK_1HzOut <= 0;//输出信号清零
Count <= 0;//计数器清零
end
else
begin
if(Count<25000000)
begin
Count <= Count+1'b1;//计数器加1
end
else
begin
Count<=0;//计数器清零
CLK_1HzOut<=~CLK_1HzOut;//输出信号翻转
end
end
end
endmodule

# 4.3 主体逻辑代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
module traffic(
input CLK,//时钟
input EN,//使能
input [1:0] mode ,//设置模式:00为正常模式,01为紧急模式,10为夜间模式
output reg [3:0] LAMPA,//A路灯,控制A方向上四盏灯的亮灭
output reg [3:0] LAMPB,//B路灯,控制B方向上四盏灯的亮灭
output [7:0] ACOUNT,//A路计数器,控制A方向上两个数码管的显示
output [7:0] BCOUNT//B路计数器,控制B方向上两个数码管的显示
);

//信号定义
reg [7:0] numa,numb;//倒计时时间
reg [3:0] Anext,Bnext;//定义灯切换顺序
reg [7:0] ared,ayellow,agreen,aleft,bred,byellow,bgreen,bleft;
assign ACOUNT=numa;//采用无阻塞赋值,使得ACOUNT和BCOUNT的值在同一时刻更新
assign BCOUNT=numb;//
reg tempa,tempb;//定义临时变量,用于判断是否需要切换灯 0表示需要切换灯,1表示不需要切换灯
reg [2:0] counta,countb;//控制黄灯闪烁
//倒计时参数初始化
always @(EN)
if(!EN)
begin
ared[3:0] <=0;
ared[7:4] <=4;//A道红灯40s
ayellow <=5;//A道黄灯5s
agreen[3:0] <=5;
agreen[7:4] <=2;//A道绿灯25s
aleft[3:0] <=5;
aleft[7:4] <=1;//A道左转灯15s
bred[3:0] <=0;
bred[7:4] <=5;//B道红灯50s
byellow <=5;//B道黄灯5s
bleft[3:0] <=0;
bleft[7:4] <=1;//B道左转灯10s
bgreen[3:0] <=0;
bgreen[7:4] <=2;//B道绿灯20s
end

//灯参数初始化
parameter RED=4'b0001;
parameter LEFT=4'b0010;
parameter YELLOW=4'b0100;
parameter GREEN=4'b1000;
parameter DARK=4'b0000;

always @(posedge CLK) //A道灯
begin
if(EN)
begin
case (mode)
2'b00: //紧急情况和夜间模式均为激活
begin
if(!tempa)
begin
tempa<=1;
case (Anext)//确定灯顺序
0: begin numa<=agreen; LAMPA<=GREEN; Anext<=1; end
1: begin numa<=ayellow; LAMPA<=YELLOW; Anext<=2; end
2: begin numa<=aleft; LAMPA<=LEFT; Anext<=3; end
3: begin numa<=ayellow; LAMPA<=YELLOW; Anext<=4; end
4: begin numa<=ared; LAMPA<=RED; Anext<=0; end
default: ;
endcase
end
else
begin
if(numa>1)
if(!numa[3:0])
begin
numa[3:0]<=4'b1001;
numa[7:4]<=numa[7:4]-1;
end
else
numa[3:0]<=numa[3:0]-1;
if(numa==2)
tempa<=0;
end//注意这段代码.
/*1.为什么选择numa>1而不是numa>0:
因为numa=1时,倒计时为0,此时灯应该切换,而不是继续倒计时
2.为什么判断numa==2时tempa<=0:
注意此时要实现灯倒计时的切换,有一个时间周期的延迟,所以要在numa==2时,将tempa<=0,这样在numa==1时,才能切换灯
*/

end
2'b01://紧急情况下灯变成红色,无倒计时
begin
numa<=8'b11111111;
LAMPA<=RED;
tempa<=0;

end
2'b10://夜间模式,黄灯闪烁
begin
if(counta<1)
begin
counta<=counta+1'b1;
LAMPA<=DARK;
end
else
begin
counta<=0;
LAMPA<=YELLOW;
numa<=8'b11111111;
end
tempa<=0;
end
default: ;
endcase
end
else
begin
LAMPA<=DARK;
Anext<=0;
tempa<=0;
numa<=8'b11111111;
end
end

always @(posedge CLK) //B道灯
begin
if(EN)
begin
case (mode)
2'b00: //紧急情况和夜间模式均为激活
begin
if(!tempb)
begin
tempb<=1;
case (Bnext)//确定灯顺序
0: begin numb<=bred; LAMPB<=RED; Bnext<=1; end
1: begin numb<=bgreen; LAMPB<=GREEN; Bnext<=2; end
2: begin numb<=byellow; LAMPB<=YELLOW; Bnext<=3; end
3: begin numb<=bleft; LAMPB<=LEFT; Bnext<=4; end
4: begin numb<=byellow; LAMPB<=YELLOW; Bnext<=0; end
default: ;
endcase
end
else
begin
if(numb>1)
if(!numb[3:0])
begin
numb[3:0]<=4'b1001;
numb[7:4]<=numb[7:4]-1;
end
else
numb[3:0]<=numb[3:0]-1;
if(numb==2)
tempb<=0;

end
end
2'b01://紧急情况下灯变成红色,无倒计时
begin
numb<=8'b11111111;
LAMPB<=RED;
tempb<=0;
end
2'b10://夜间模式,黄灯闪烁
begin
if(countb<1)
begin
countb<=countb+1'b1;
LAMPB<=DARK;
end
else
begin
countb<=0;
LAMPB<=YELLOW;
numb<=8'b11111111;
end
tempb<=0;
end
default: ;
endcase
end
else
begin
LAMPB<=DARK;
Bnext<=0;
tempb<=0;
numb<=8'b11111111;
end
end
endmodule

# 4.4 顶层模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module TrafficTop (
input CLK,
input EN,
input[1:0]mode,
output[6:0] segAH,//A道倒计时高位七段数字显示码
output[6:0] segAL,//A道倒计时低位七段数字显示码
output[6:0] segBH,//B道倒计时高位七段数字显示码
output[6:0] segBL,//B道倒计时低位七段数字显示码
output[3:0] LAMPA,//A道四盏灯
output[3:0] LAMPB,//B道四盏灯
output reg [7:0] dis//小数点
);
wire clk_1Hz;//1Hz时钟信号
wire [7:0] ACOUNT,BCOUNT;
wire RST=1'b1;//复位信号
//调用分频
Divider50MHz(CLK,RST,clk_1Hz);
//调用交通灯控制模块
traffic(clk_1Hz,EN,mode,LAMPA,LAMPB,ACOUNT,BCOUNT);
//显示倒计时
_BCDto7LED(ACOUNT[7:4],ACOUNT[3:0],segAH,segAL);
_BCDto7LED(BCOUNT[7:4],BCOUNT[3:0],segBH,segBL);
//隐藏小数点
always@(EN)
if(EN)
begin
dis<=8'hFF;
end
endmodule

# 5. 实验过程

# 5.1 New Project Wizard

  1. 新建工程文件
    注意输入的顶层实体名必须与之后设计文件的顶层实体名一致
  2. 添加 traffictop.v 文件
  3. 选择设计器件,本次实验采用的是 Altera 公司提供的 DE2-70 开发板.
    Device family 选择 CycloneⅡ,Package 选择 FBGA,PinCount 选择 896,speed grade 选择 6

# 5.2 Start Analysis & Synthesis

将 RTL (寄存器传输级) 转换为网表级,并进行综合分析,生成综合报告

# 5.3 Start Compilation

# 5.4 Pins Assignment

手工分配引脚

NodeNameLocation
CLKPIN_AD15
ENPIN_AA23
mode[1]PIN_AB25
mode[0]PIN_AB26
dis[7]PIN_G2
dis[6]PIN_K2
dis[5]PIN_K6
dis[4]PIN_L6
dis[3]PIN_M4
dis[2]PIN_AC19
dis[1]PIN_AC17
dis[0]PIN_AF12
LAMPA[3]PIN_AJ7
LAMPA[2]PIN_AD8
LAMPA[1]PIN_AD9
LAMPA[0]PIN_AC11
LAMPB[3]PIN_AB12
LAMPB[2]PIN_AC12
LAMPB[1]PIN_AB13
LAMPB[0]PIN_AC13
segAH[6]PIN_G1
segAH[5]PIN_H3
segAH[4]PIN_H2
segAH[3]PIN_H1
segAH[2]PIN_J2
segAH[1]PIN_J1
segAH[0]PIN_K3
segAL[6]PIN_E4
segAL[5]PIN_F4
segAL[4]PIN_G4
segAL[3]PIN_H8
segAL[2]PIN_H7
segAL[1]PIN_H4
segAL[0]PIN_H6
segBH[6]PIN_K5
segBH[5]PIN_K4
segBH[4]PIN_K1
segBH[3]PIN_L3
segBH[2]PIN_L2
segBH[1]PIN_L1
segBH[0]PIN_M3
segBL[6]PIN_M2
segBL[5]PIN_M1
segBL[4]PIN_N3
segBL[3]PIN_N2
segBL[2]PIN_P3
segBL[1]PIN_P2
segBL[0]PIN_P1

# 5.5 Programmer

将设计下载到 FPGA 中

# 5.6 功能仿真

  1. 添加波形文件
  2. 插入信号结点
  3. CLK 设为周期为 20ns 的时钟信号,EN 设为 1,mode 设为 00
  4. 设置仿真模式为 Functional
  5. 生成功能仿真网表
  6. 启动仿真

# 6. 实验结果

见视频

# 7. 实验收获与体会

  1. 注意 verilog 语言的书写规范,如 beginend 要成对出现, caseendcase 要成对出现, ifelse 要成对出现, alwaysend 要成对出现, alwaysbegin 要成对出现, alwaysendcase 要成对出现, alwayscase 要成对出现, alwaysif 要成对出现
  2. 名称命名要规范,如 CLK_1HzOutCLK_50MHzInput 中的 CLK 表示时钟, 1Hz 表示 1Hz 时钟, Out 表示输出, 50MHz 表示 50MHz 时钟, Input 表示输入, Divider50MHz 表示 50MHz 时钟的分频模块, Divider 表示分频, 50MHz 表示 50MHz 时钟, traffic 表示交通灯控制模块, trafficTop 表示顶层模块, _BCDto7LED 表示 BCD 码转七段显示译码器
  3. 引脚设置要仔细,要与开发板上的引脚对应,否则会出现无法下载的情况或者下载后无法正常工作的情况
  4. 功能仿真时,要注意时钟信号的周期,要与设计时的时钟信号周期一致,否则会出现仿真结果与实际结果不一致的情况