FPGA交通信号灯设计报告(VHDL语言)

avatar
作者
猴君
阅读量:0

FPGA的大作业我选择了交通灯控制系统的设计,此课程只有2个学分,因此只需要在相应软件仿真出结果即可。以下是我的设计报告,当时写的匆忙,没有对代码进行优化改进,但仿真结果是正确的,可以给大家提供一下思路。

一、任务分析

请添加图片描述

分析设计要求可以看出,本系统设计数码管动态显示、译码器、计数器分频方面的知识。

1.输入和输出

  1. 输入

    clk:时钟输入(50MHz)。

    special:特殊情况发生输入。

  2. 输出

    l1、l2:数码管位选。

    display_1、display_2:数码管段选。

    out_1、out_2:红绿灯选择。

2.多进程

  1. reg进程:分频计数

    • 红绿灯的闪烁和数码管显示数字的变化都需要计时,计时单位为s(秒)时,设计最简单。因此,我将50MHz频率的clk驱动分频,这部分运用了计数器知识。

    • 从题目中看出,红绿灯50s为一个循环,我使用信号counter来记录时间,时钟每过1秒钟,counter就加1,counter的值不能超过50。

  2. com进程:红绿灯显示

    • 通过counter的值就可以判断出东西方和南北方的灯为什么颜色。我使用自定义枚举类型来表示红绿灯的三种状态。

    • 在判断红绿灯状态的同时,还应该计算出数码管应该显示的数字为多少。

  3. smg进程:数码管显示

    • 将com进程中计算出的数码管数字在数码管上显示出来。计算数码管十位上的显示数字时用10取模,计算数码管个位上的显示数字时用10取余。
    • 由于数码管位选仅有两个,我使用一个变量的0和1分别表示数码管的十位显示和个位显示。
    • 数码管的段选运用了译码器相关知识。

3.特殊情况

  1. 当bit类型信号special为’1’时,进入特殊情况处理。

  2. 在com进程中,只有当special为’0’时,counter才会加1。目的是在特殊情况时,数码管的数字不会发生变化。

  3. 我额外定义了一个bit类型信号change,时钟每过一秒钟,它就会取反。使用它的目的是在特殊情况时,实现数码管闪烁。

4.注意

需要格外注意的是,reg、com、smg三个进程的敏感信号。

reg:process(clk,special)

com:process(counter,special)

红绿灯显示在每隔1秒钟判断或special特殊情况到来时判断即可

reg:process(clk,change,special)

数码管为动态扫描,只有速度够快,才能使人眼无法察觉数码管的交替显示,因此需要clk时钟信号作为敏感信号。

二、quartus ii 仿真

1.VHDL语言描述

library ieee; use ieee.std_logic_1164.all; entity fisrt is --两组数码管(段选+位选),两组灯 	port(clk:in std_logic; 			special:in bit;        		--数码管段选 			l1,l2:out std_logic_vector(1 downto 0);        		--数码管位选 			display_1,display_2:out std_logic_vector(6 downto 0);        		--红绿灯 			out_1,out_2:out std_logic_vector(2 downto 0)); end fisrt; architecture behav of fisrt is 	type FSM is (s0,s1,s2); 	signal LED_1,LED_2:FSM;--自定义枚举类型 	signal counter:integer range 0 to 49;--共50s 	signal smg_1,smg_2:integer range 0 to 24;--数码管显示数字 	signal change:bit;--每一秒钟用于变化的量 begin  --进程reg用于分频 	reg:process(counter,special) 		variable clk1:integer range 0 to 9999;--50MHz分频为1Hz 		variable clk2:integer range 0 to 4999; 	begin 		if clk'event and clk='1' then 			if clk1=9999 then 				clk1:=0; 				if clk2=4999 then 					clk2:=0; 					if special='0' then 						change<=not change; 						if counter=49 then 							counter<=0; 						else counter<=counter+1; 						end if; 					end if; 				else clk2:=clk2+1; 				end if; 			else clk1:=clk1+1; 			end if; 	  end if; 	end process reg;   --进程com用于红绿灯显示 	com:process(clk,counter,special) 	begin 		if special='1' then 			out_1<="100"; 			out_2<="100"; 		else 			if counter<20 then 				LED_1<=s0;smg_1<=20-counter; 				LED_2<=s0;smg_2<=25-counter; 			elsif counter<25 then 				LED_1<=s1;smg_1<=25-counter; 				LED_2<=s0;smg_2<=25-counter; 			elsif counter<45 then 				LED_1<=s2;smg_1<=50-counter; 				LED_2<=s1;smg_2<=45-counter; 			else LED_1<=s2;smg_1<=50-counter; 				   LED_2<=s2;smg_2<=50-counter; 			end if; 			 			case LED_1 is 				when s0=>out_1<="010";--绿灯亮 				when s1=>out_1<="001";--黄灯亮 				when s2=>out_1<="100";--红灯亮 			end case; 			case LED_2 is 				when s0=>out_2<="100";--红灯亮 				when s1=>out_2<="010";--绿灯亮 				when s2=>out_2<="001";--黄灯亮 			end case; 		end if; 	end process com; 	 --进程smg用于数码管显示 	smg:process(clk,change,special) 	variable a,b:bit; 	variable c1,c2:integer range 0 to 10; 	begin     if clk'event and clk='1' then 		a:=not a; 		b:=not b; 		case a is 			when '0'=> l1<="01";c1:=smg_1 rem 10;--取个位数 			when '1'=> l1<="10";c1:=smg_1 mod 10;--取十位数 		end case; 		case b is 			when '0'=> l2<="01";c2:=smg_2 rem 10; 			when '1'=> l2<="10";c2:=smg_2 mod 10; 		end case; 		 		if special='1' and change='1' then --特殊情况时,数码管1s闪烁一次 			c1:=10;c2:=10; 		end if; 		 		case c1 is 			when 0 =>display_1<="0111111"; 			when 1 =>display_1<="0000110"; 			when 2 =>display_1<="1011011"; 			when 3 =>display_1<="1001111"; 			when 4 =>display_1<="1100110"; 			when 5 =>display_1<="1101101"; 			when 6 =>display_1<="1111101"; 			when 7 =>display_1<="0000111"; 			when 8 =>display_1<="1111111"; 			when 9 =>display_1<="1100011"; 			when others=>display_1<="0000000"; 		end case; 		case c2 is 			when 0 =>display_2<="0111111"; 			when 1 =>display_2<="0000110"; 			when 2 =>display_2<="1011011"; 			when 3 =>display_2<="1001111"; 			when 4 =>display_2<="1100110"; 			when 5 =>display_2<="1101101"; 			when 6 =>display_2<="1111101"; 			when 7 =>display_2<="0000111"; 			when 8 =>display_2<="1111111"; 			when 9 =>display_2<="1100011"; 			when others=>display_2<="0000000"; 		end case;     end if; 	end process smg; end behav; 

2.仿真结果

  • RTL Viewer

请添加图片描述

  • 时序仿真

为了方便仿真,我将clk1和clk2去除,将clk设置为1Hz频率的时钟信号,下图为修改后代码。这样仿真的缺点就是,数码管无法显示出完整数字,在每过一秒钟,交替显示十位数码管和个位数码管。
在这里插入图片描述
仿真结果如下:

请添加图片描述

三、总结

1.收获

设计交通信号灯控制系统,我熟悉了计数器、译码器、动态数码管显示的知识,加深对它们的工作原理的理解,更加熟练的使用case、if等语句。同时,也进一步熟悉EDA软件——quartusii,提高代码构思能力。

2.问题与解决

  1. 编译过程中出现很多语法错误,例如process进程忘记加begin,定义的整型类型忘记加range,变量赋值语句的符号为“ := ”,if语句没有加then,忘记写“end if”…

    这些语法错误在编译后的error中查找并改正。

  2. 构思VHDL代码结构时,最大的困难就是数码管显示数字和特殊情况处理。

    • 我将数码管显示数字的计算和判断红绿灯代码放到了一起,利用counter信号50s一循环,红绿灯50s一循环,在根据红、绿、黄灯的持续时间,就可以将数码管数字计算出来。

    • 特殊情况是最难处理的地方。它不仅要求在此时红灯亮,还需要让数码管闪烁。我将数码管设置为1s闪烁,具体实现方法是,在com进程让change信号每过1s取反,在smg进程中,当special=‘1’ 并且change='1’时,数码管不显示数字。

3.改进与优化

  1. 数码管显示数字部分可以看出,这个程序在显示数字小于10时,两位数码管显示为"0X",也就是说十位上的数码管有数据显示,这是不符合现实的,因此,可以更改为以下代码。

    --smg进程部分 case a is 	 when '0'=> l1<="01";c1:=smg_1 rem 10;--取个位数 	 when '1'=> l1<="10";c1:=smg_1 mod 10;--取十位数 			if c1=0 then          		c1:=10;--此时数码管不显示 			end if; end case; case b is 	 when '0'=> l2<="01";c2:=smg_2 rem 10; 	 when '1'=> l2<="10";c2:=smg_2 mod 10; 			if c2=0 then          		c2:=10;--此时数码管不显示 			end if; end case;  
  2. 通过理解题目内容可以看出,东西方和南北方的数码管显示和灯颜色的选择是相似的,可以设计子程序,通过函数调用或过程调用优化代码。但因本人能力有限,没有写出具体实现方法。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!