本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计
1.1.1 概述
发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,可以高效的将电能转化为光能,在现代社会具有广泛的用途,如照明、平板显示、医疗器件等。可通过高低电平的变化来控制LED灯的明灭状态,当输出信号为低电平时,LED灯亮,反之,当输出信号为高电平时,LED灯灭。
1.1.2 设计目标
内容:开发板上有红黄绿 LED 灯各四个,分别放在东西南北方向。参考交通灯的情况,即每个方向都是绿灯亮 10 秒,然后黄灯亮 5 秒,然后红灯亮 15 秒。绿灯按照东西和南北的顺序依次亮。
具体思路:
1. 首先分东西方向和南北方向来设计电路。设计两个不同的状态机来指示不同的状态。东西方向的灯状态相同,南北方向的灯状态相同。
2. 4个方向的灯依次为红绿黄的依次循环时间为 15+10+5=30 秒,所以可以设计一个计数器,计时 30 秒钟,表示一个循环。计数器可以分两个写,一个计时 1秒,一个计时 30秒。
3. 然后再根据计数器的计数值的不同,决定状态机的不同状态。
4. 首先设计东西方向的状态机,复位的时候,绿灯亮,计数器计到 10 秒,黄灯亮,计到15 秒,红灯亮,计满 30 秒,又是绿灯亮......依次循环。
5. 接着设计南北方向的状态机,复位的时候,红灯亮,计数器计到15 秒,绿灯亮,计到20 秒,黄灯亮,计满 30 秒,又是红灯亮......依次循环。
1.1.3信号列表
1.1.4 设计思路
根据题目功能要求,东西南北四个方向LED灯按照“红灯-绿灯-黄灯”的顺序依次循环时间为 15+10+5=30 秒,所以可以设计一个计数器,计时 30 秒钟表示一个循环。该计数器可以分两个写,一个计时 1 秒,一个计时 30秒。
因为在数字电路中的延时都是通过计数器实现的,计数器*时钟周期=延时时间。本模块中,由于输入时钟是50MHz,时钟周期为20ns,功能要求每1秒变化一次。我们通过counter来表示延时,当其值为1s/20ns=5000_0000时,表示1秒时间到。
两个计数器的架构图:
时钟计数器counter:该计数器用于计算1s的时钟个数,加一条件为1,表示一直计数;数到5000_0000下,则表示数到1秒的时间了。
秒计数器:该计数器用于计算1个周期内三色LED按顺序各点亮1次的时间,1周期的时间为30秒,加一条件为时钟计数器的结束条件,表示时钟计数器每数完1s,秒计数器计数加一;数到30下,则表示数到30秒的时间了。
下面是两个计数器的代码:
1. parameter COUNT_TIME = 26'd5000_0000; 2. parameter CYCLE_TIME = 5'd30 ; 3. parameter COUNT_WID = 26 ; 4. parameter SEC_WID = 5 ; 5. 6. reg [COUNT_WID-1:0] counter ; 7. wire add_counter ; 8. wire end_counter ; 9. reg [SEC_WID-1:0] second ; 10. wire add_second ; 11. wire end_second ; 12. 13. always @(posedge clk or negedge rst_n) begin 14. if (rst_n==0) begin 15. counter <= 0; 16. end 17. else if(add_counter) begin 18. if(end_counter) 19. counter <= 0; 20. else 21. counter <= counter+1 ; 22. end 23. end 24. assign add_counter = 1; 25. assign end_counter = add_counter && counter == COUNT_TIME-1 ; 26. 27. 28. always @(posedge clk or negedge rst_n) begin 29. if (rst_n==0) begin 30. second <= 0; 31. end 32. else if(add_second) begin 33. if(end_second) 34. second <= 0; 35. else 36. second <= second+1 ; 37. end 38. end 39. assign add_second = end_counter; 40. assign end_second = add_second && second == CYCLE_TIME-1 ;
按照题目要求:分东西方向和南北方向来设计电路,因此设计两个不同的状态机来指示不同的状态——同一时间内,东西方向的灯状态相同,南北方向的灯状态相同。
两个状态机的架构:
东西方向的状态机:该状态机用于设定东西方向LED的颜色跳转状态。
1) 上电后,就跳转到绿灯亮状态,绿灯亮;
2) 10 秒后,黄灯亮,跳转条件为秒计数器计数10下,即add_second &&second==10-1,则表示数到10秒了;
3) 5 秒后,红灯亮,跳转条件为秒计数器计数15下,即add_second &&second==15-1,则表示数到15秒了;
4) 15 秒后,又是绿灯亮,跳转条件为秒计数器计满30下,即add_second &&second==30-1,则表示数到30秒了......依次循环。
南北方向的状态机:该状态机用于设定南北方向LED的颜色跳转状态。
1) 上电后,就跳转到红灯亮状态,红灯亮;
2) 15 秒后,绿灯亮,跳转条件为秒计数器计数15下,即add_second &&second==15-1,则表示数到15秒了;
3) 10 秒后,黄灯亮,跳转条件为秒计数器计数25下,即add_second &&second==25-1,则表示数到25秒了;
4) 5 秒后,又是红灯亮,跳转条件为秒计数器计满30下,即add_second &&second==30-1,则表示数到30秒了......依次循环。
下面是东西、南北方向的两个状态机代码:
41. parameter LED_NUM = 3 ; 42. parameter STA_W = 2 ; 43. 44. parameter STA_G = 2'd1 ; 45. parameter STA_Y = 2'd2 ; 46. parameter STA_R = 2'd3 ; 47. parameter IDLE = 2'd0 ; 48. parameter GREEN = 3'b110 ; 49. parameter YELLOW = 3'b101 ; 50. parameter RED = 3'b011 ; 51. 52. input clk ; 53. input rst_n ; 54. output [LED_NUM-1:0] led_east ; 55. output [LED_NUM-1:0] led_south ; 56. output [LED_NUM-1:0] led_west ; 57. output [LED_NUM-1:0] led_north ; 58. 59. reg [LED_NUM-1:0] led_east ; 60. reg [LED_NUM-1:0] led_south ; 61. reg [LED_NUM-1:0] led_west ; 62. reg [LED_NUM-1:0] led_north ; 63. 64. reg [STA_W-1:0] ew_state_c ; 65. reg [STA_W-1:0] ew_state_n ; 66. wire idle2sta_g_start_ew ; 67. wire sta_g2sta_y_start_ew ; 68. wire sta_y2sta_r_start_ew ; 69. wire sta_r2sta_g_start_ew ; 70. 71. reg [STA_W-1:0] sn_state_c ; 72. reg [STA_W-1:0] sn_state_n ; 73. wire idle2sta_r_start_sn ; 74. wire sta_r2sta_g_start_sn ; 75. wire sta_g2sta_y_start_sn ; 76. wire sta_y2sta_r_start_sn ; 77. 78. always @(posedge clk or negedge rst_n) begin 79. if (rst_n==0) begin 80. ew_state_c <= STA_G ; 81. end 82. else begin 83. ew_state_c <= ew_state_n; 84. end 85. end 86. 87. always @(*) begin 88. case(ew_state_c) 89. STA_G :begin 90. if(sta_g2sta_y_start_ew) 91. ew_state_n = STA_Y ; 92. else 93. ew_state_n = ew_state_c ; 94. end 95. STA_Y :begin 96. if(sta_y2sta_r_start_ew) 97. ew_state_n = STA_R ; 98. else 99. ew_state_n = ew_state_c ; 100. end 101. STA_R :begin 102. if(sta_r2sta_g_start_ew) 103. ew_state_n = STA_G ; 104. else 105. ew_state_n = ew_state_c ; 106. end 107. default : ew_state_n = STA_G ; 108. endcase 109. end 110. 111. assign sta_g2sta_y_start_ew = ew_state_c==STA_G && add_second && second == 10-1; 112. assign sta_y2sta_r_start_ew = ew_state_c==STA_Y && add_second && second == 15-1; 113. assign sta_r2sta_g_start_ew = ew_state_c==STA_R && add_second && second == 30-1; 114. 115. always @(posedge clk or negedge rst_n)begin 116. if(rst_n==1'b0)begin 117. led_east<=GREEN; 118. end 119. else if(ew_state_c==STA_G)begin 120. led_east<=GREEN; 121. end 122. else if(ew_state_c==STA_Y)begin 123. led_east<=YELLOW; 124. end 125. else if(ew_state_c==STA_R)begin 126. led_east<=RED; 127. end 128. else begin 129. led_east<=GREEN; 130. end 131. end 132. 133. always @(posedge clk or negedge rst_n)begin 134. if(rst_n==1'b0)begin 135. led_west<=GREEN; 136. end 137. else if(ew_state_c==STA_G)begin 138. led_west<=GREEN; 139. end 140. else if(ew_state_c==STA_Y)begin 141. led_west<=YELLOW; 142. end 143. else if(ew_state_c==STA_R)begin 144. led_west<=RED; 145. end 146. else begin 147. led_west<=GREEN; 148. end 149. end 150. 151. 152. 153. always @(posedge clk or negedge rst_n) begin 154. if (rst_n==0) begin 155. sn_state_c <= STA_R ; 156. end 157. else begin 158. sn_state_c <= sn_state_n; 159. end 160. end 161. 162. always @(*) begin 163. case(sn_state_c) 164. STA_R :begin 165. if(sta_r2sta_g_start_sn) 166. sn_state_n = STA_G ; 167. else 168. sn_state_n = sn_state_c ; 169. end 170. STA_G :begin 171. if(sta_g2sta_y_start_sn) 172. sn_state_n = STA_Y ; 173. else 174. sn_state_n = sn_state_c ; 175. end 176. STA_Y :begin 177. if(sta_y2sta_r_start_sn) 178. sn_state_n = STA_R ; 179. else 180. sn_state_n = sn_state_c ; 181. end 182. default : sn_state_n = STA_R ; 183. endcase 184. end 185. 186. assign sta_r2sta_g_start_sn = sn_state_c==STA_R && add_second && second == 15-1; 187. assign sta_g2sta_y_start_sn = sn_state_c==STA_G && add_second && second == 25-1; 188. assign sta_y2sta_r_start_sn = sn_state_c==STA_Y && add_second && second == 30-1; 189. 190. always @(posedge clk or negedge rst_n)begin 191. if(rst_n==1'b0)begin 192. led_south<=RED; 193. end 194. else if(sn_state_c==STA_G)begin 195. led_south<=GREEN; 196. end 197. else if(sn_state_c==STA_Y)begin 198. led_south<=YELLOW; 199. end 200. else if(sn_state_c==STA_R)begin 201. led_south<=RED; 202. end 203. else begin 204. led_south<=GREEN; 205. end 206. end 207. 208. always @(posedge clk or negedge rst_n)begin 209. if(rst_n==1'b0)begin 210. led_north<=RED; 211. end 212. else if(sn_state_c==STA_G)begin 213. led_north<=GREEN; 214. end 215. else if(sn_state_c==STA_Y)begin 216. led_north<=YELLOW; 217. end 218. else if(sn_state_c==STA_R)begin 219. led_north<=RED; 220. end 221. else begin 222. led_north<=GREEN; 223. end 224. end
1.1.5参考设计代码
225. /************************************************************************************ 226. The code is designed and produced by MDY Science and Education Co., Ltd, which has the entire ownership. It is only for personal learning, which cannot be used for commercial or profit-making purposes without permission. 227. 228. MDY's Mission: Develop Chip Talents and Realize National Chip Dream. 229. 230. We sincerely hope that our students can learn the real IC / FPGA code through our standard and rigorous code. 231. 232. For more FPGA learning materials, please visit the Forum: http://fpgabbs.com/ and official website: http://www.mdy-edu.com/index.html 233. 234. *************************************************************************************/ 235. 236. 237. module traf_light2( 238. clk , 239. rst_n , 240. led_east , 241. led_south , 242. led_west , 243. led_north 244. ); 245. 246. 247. 248. parameter COUNT_TIME = 26'd5000_0000; 249. parameter CYCLE_TIME = 5'd30 ; 250. parameter COUNT_WID = 26 ; 251. parameter SEC_WID = 5 ; 252. parameter LED_NUM = 3 ; 253. parameter STA_W = 2 ; 254. 255. parameter STA_G = 2'd1 ; 256. parameter STA_Y = 2'd2 ; 257. parameter STA_R = 2'd3 ; 258. parameter GREEN = 3'b110 ; 259. parameter YELLOW = 3'b101 ; 260. parameter RED = 3'b011 ; 261. 262. 263. input clk ; 264. input rst_n ; 265. output [LED_NUM-1:0] led_east ; 266. output [LED_NUM-1:0] led_south ; 267. output [LED_NUM-1:0] led_west ; 268. output [LED_NUM-1:0] led_north ; 269. 270. reg [LED_NUM-1:0] led_east ; 271. reg [LED_NUM-1:0] led_south ; 272. reg [LED_NUM-1:0] led_west ; 273. reg [LED_NUM-1:0] led_north ; 274. 275. reg [COUNT_WID-1:0] counter ; 276. wire add_counter ; 277. wire end_counter ; 278. reg [SEC_WID-1:0] second ; 279. wire add_second ; 280. wire end_second ; 281. 282. reg [STA_W-1:0] ew_state_c ; 283. reg [STA_W-1:0] ew_state_n ; 284. wire idle2sta_g_start_ew ; 285. wire sta_g2sta_y_start_ew ; 286. wire sta_y2sta_r_start_ew ; 287. wire sta_r2sta_g_start_ew ; 288. 289. reg [STA_W-1:0] sn_state_c ; 290. reg [STA_W-1:0] sn_state_n ; 291. wire idle2sta_r_start_sn ; 292. wire sta_r2sta_g_start_sn ; 293. wire sta_g2sta_y_start_sn ; 294. wire sta_y2sta_r_start_sn ; 295. 296. 297. always @(posedge clk or negedge rst_n) begin 298. if (rst_n==0) begin 299. counter <= 0; 300. end 301. else if(add_counter) begin 302. if(end_counter) 303. counter <= 0; 304. else 305. counter <= counter+1 ; 306. end 307. end 308. assign add_counter = 1; 309. assign end_counter = add_counter && counter == COUNT_TIME-1 ; 310. 311. 312. always @(posedge clk or negedge rst_n) begin 313. if (rst_n==0) begin 314. second <= 0; 315. end 316. else if(add_second) begin 317. if(end_second) 318. second <= 0; 319. else 320. second <= second+1 ; 321. end 322. end 323. assign add_second = end_counter; 324. assign end_second = add_second && second == CYCLE_TIME-1 ; 325. 326. 327. always @(posedge clk or negedge rst_n) begin 328. if (rst_n==0) begin 329. ew_state_c <= STA_G ; 330. end 331. else begin 332. ew_state_c <= ew_state_n; 333. end 334. end 335. 336. always @(*) begin 337. case(ew_state_c) 338. STA_G :begin 339. if(sta_g2sta_y_start_ew) 340. ew_state_n = STA_Y ; 341. else 342. ew_state_n = ew_state_c ; 343. end 344. STA_Y :begin 345. if(sta_y2sta_r_start_ew) 346. ew_state_n = STA_R ; 347. else 348. ew_state_n = ew_state_c ; 349. end 350. STA_R :begin 351. if(sta_r2sta_g_start_ew) 352. ew_state_n = STA_G ; 353. else 354. ew_state_n = ew_state_c ; 355. end 356. default : ew_state_n = STA_G ; 357. endcase 358. end 359. 360. assign sta_g2sta_y_start_ew = ew_state_c==STA_G && add_second && second == 10-1; 361. assign sta_y2sta_r_start_ew = ew_state_c==STA_Y && add_second && second == 15-1; 362. assign sta_r2sta_g_start_ew = ew_state_c==STA_R && add_second && second == 30-1; 363. 364. always @(posedge clk or negedge rst_n)begin 365. if(rst_n==1'b0)begin 366. led_east<=GREEN; 367. end 368. else if(ew_state_c==STA_G)begin 369. led_east<=GREEN; 370. end 371. else if(ew_state_c==STA_Y)begin 372. led_east<=YELLOW; 373. end 374. else if(ew_state_c==STA_R)begin 375. led_east<=RED; 376. end 377. else begin 378. led_east<=GREEN; 379. end 380. end 381. 382. always @(posedge clk or negedge rst_n)begin 383. if(rst_n==1'b0)begin 384. led_west<=GREEN; 385. end 386. else if(ew_state_c==STA_G)begin 387. led_west<=GREEN; 388. end 389. else if(ew_state_c==STA_Y)begin 390. led_west<=YELLOW; 391. end 392. else if(ew_state_c==STA_R)begin 393. led_west<=RED; 394. end 395. else begin 396. led_west<=GREEN; 397. end 398. end 399. 400. 401. 402. always @(posedge clk or negedge rst_n) begin 403. if (rst_n==0) begin 404. sn_state_c <= STA_R ; 405. end 406. else begin 407. sn_state_c <= sn_state_n; 408. end 409. end 410. 411. always @(*) begin 412. case(sn_state_c) 413. STA_R :begin 414. if(sta_r2sta_g_start_sn) 415. sn_state_n = STA_G ; 416. else 417. sn_state_n = sn_state_c ; 418. end 419. STA_G :begin 420. if(sta_g2sta_y_start_sn) 421. sn_state_n = STA_Y ; 422. else 423. sn_state_n = sn_state_c ; 424. end 425. STA_Y :begin 426. if(sta_y2sta_r_start_sn) 427. sn_state_n = STA_R ; 428. else 429. sn_state_n = sn_state_c ; 430. end 431. default : sn_state_n = STA_R ; 432. endcase 433. end 434. 435. assign sta_r2sta_g_start_sn = sn_state_c==STA_R && add_second && second == 15-1; 436. assign sta_g2sta_y_start_sn = sn_state_c==STA_G && add_second && second == 25-1; 437. assign sta_y2sta_r_start_sn = sn_state_c==STA_Y && add_second && second == 30-1; 438. 439. always @(posedge clk or negedge rst_n)begin 440. if(rst_n==1'b0)begin 441. led_south<=RED; 442. end 443. else if(sn_state_c==STA_G)begin 444. led_south<=GREEN; 445. end 446. else if(sn_state_c==STA_Y)begin 447. led_south<=YELLOW; 448. end 449. else if(sn_state_c==STA_R)begin 450. led_south<=RED; 451. end 452. else begin 453. led_south<=GREEN; 454. end 455. end 456. 457. always @(posedge clk or negedge rst_n)begin 458. if(rst_n==1'b0)begin 459. led_north<=RED; 460. end 461. else if(sn_state_c==STA_G)begin 462. led_north<=GREEN; 463. end 464. else if(sn_state_c==STA_Y)begin 465. led_north<=YELLOW; 466. end 467. else if(sn_state_c==STA_R)begin 468. led_north<=RED; 469. end 470. else begin 471. led_north<=GREEN; 472. end 473. end 474. 475. endmodule 476.
1.2 效果和总结
点拨板:
1. 复位,东西绿灯亮,南北红灯亮
2. 10秒后,东西黄灯亮,南北还是红灯亮
3. 15秒后,东西红灯亮,南北绿灯亮
4. 20秒后。东西还是红灯亮,南北黄灯亮
5. 30秒后,东西绿灯亮,南北红灯亮
Mp801:
1. 复位,东西绿灯亮,南北红灯亮
2. 10秒后,东西黄灯亮,南北还是红灯亮
3. 15秒后,东西红灯亮,南北绿灯亮
4. 20秒后。东西还是红灯亮,南北黄灯亮
5. 30秒后,东西绿灯亮,南北红灯亮
实验箱:
1. 复位,东西绿灯亮,南北红灯亮
2. 10秒后,东西黄灯亮,南北还是红灯亮
3. 15秒后,东西红灯亮,南北绿灯亮
4. 20秒后。东西还是红灯亮,南北黄灯亮
5. 30秒后,东西绿灯亮,南北红灯亮
观看上面的现象,可以发现,各项功能正常:开发板上有红黄绿三色 LED 灯各四个,在东西南北方向各有一组。参考交通灯的情况,即每个方向都是绿灯亮 10 秒,然后黄灯亮 5 秒,然后红灯亮 15 秒。绿灯按照东西和南北的顺序依次亮。成功完成设计目标。
设计视频教程、工程源代码请移步明德扬论坛观看下载。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也欢迎大家在评论与我进行讨论!
也可以看一下我们往期的文章:
《基于FPGA的密码锁设计》
《波形相位频率可调DDS信号发生器》
《基于FPGA的曼彻斯特编码解码设计》
《基于FPGA的出租车计费系统》
《数电基础与Verilog设计》
《基于FPGA的频率、电压测量》
《基于FPGA的汉明码编码解码设计》
《关于锁存器问题的讨论》
《阻塞赋值与非阻塞赋值》
《参数例化时自动计算位宽的解决办法》
明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。
网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。
周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。
就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。
专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。
项目承接——承接企业FPGA研发项目。
人才服务——提供人才推荐、人才代培、人才派遣等服务。