基于VHDL设计的交通灯系统

本文作者:admin       点击: 2008-01-11 00:00
前言:
一. 系统功能要求

设计一个能够显示倒计时间并可人为调整时间的交通灯系统。南北方向为主干道,东西方向为支干道。比如,南北方向为直行绿灯亮 20秒,黄灯亮 5秒,左拐绿灯亮为15秒,黄灯亮5秒,红灯亮 65秒(为东西方向直行绿灯、左拐绿灯亮及黄灯亮的时间的总合),东西方向为直行绿灯亮 40秒,直行绿黄灯为 5秒,左拐绿灯亮15秒,黄灯亮5秒;红灯45亮秒(为东西方向直行绿灯、左拐绿灯亮及黄灯亮的时间的总合),其中南北方向和东西方向的直行绿灯和左拐绿灯在最后3秒会闪烁,以提示行驶中在远处无法看清时间显示的司机通行时间快要结束了。以上红绿灯时间为预设时间。能够通过键盘操作实现时间的快速更改,更改完成后系统可立即执行更改后的时间进行运行。系统还具有深夜两方向黄灯一直闪亮警告状态(在凌晨转为正常工作)和紧急情况下的红灯常亮状态,这两种状态通过人为设置。发生紧急情况时计时停止,解除后可立即继续当前的工作。

二. 系统功能实现

整个系统控制由FPGA芯片来完成。时间显示采用数码管动态扫描方式,时间更改通过数字键盘实现,正常工作状态到时间更改状态和红黄灯常亮状态的改变通过拨码开关来实现。采用芯片板上9个发光二极管的其中8个模拟交通灯,由于二极管数量有限,故两个方向各只用一套交通灯。

系统描述采用多个进程并行处理的方式来实现,进程与进程之间通过内部信号关联。进程P1将20MHz的主时钟分频为2MHz。进程P2用于再分频和按键扫描,将2MHz分频为供数码管扫描用的128Hz和倒计时用的1Hz以及绿灯左拐灯闪烁的2Hz,同时产生用于按键行扫描的两位二进制码。进程P3用于键值获取,通过将按键扫描码和去抖后的键值合并且译码来得到正确的键值。
P4进程为码值转换和调时时间设置电路,通过计数器adjust的自加进入相应的时间设置语句。当adjust=000时给东西方向的红灯设置倒计时间,adjust=001时给东西方向的黄灯设置时间,以此类推直到adjust=111,8个交通灯的时间设置完成。内部信号time保存的值为普通二进制码,此码是通过转换函数BCD_to_binar将键盘输入的8位BCD码转换为普通二进制码得到的。进程中设置标志位pre是为了在pre= 0时将预设时间值赋给各灯。Pre的赋值由拨码开关来实现。

P5进程是东西方向工作状态时间转换电路。每来一个clk1hz时钟,通过判断time_en,emer和yel的值来决定东西方向系统工作在正常指挥状态,紧急状态还是深夜状态。正常指挥状态中内部不同状态的转换是通过counta的自加来实现的。当东西方向时间计数器atime减为0时counta 自加。并产生相应的状态提示码checka。time_en,emer和yel的赋值由拨码开关来实现。
P6为东西方向工作状态亮灯转换电路。每来一个clk2hz时钟,判断time_en,emer和yel的值来决定东西方向系统工作在正常指挥状态,紧急状态还是深夜状态。P6和P5分开进行的主要原因是考虑到绿灯和左拐灯在最后的3秒时刻要进行闪烁,其状态为0.5秒亮0.5秒灭,这比P5的1hz时钟快一倍,故灯状态的转变只能单独用另一个2hz的时钟控制。在进入3秒区时,通过将绿灯及左拐灯的码值取非来达到闪烁的效果。灯状态转换的真值表如下表。
P7、P8为南北方向的工作状态时间转换电路及亮灯转换电路,其原理与P5、P6相同,这里就不再赘述。
P9、P10、P11构成数码管动态显示电路。P9是动态扫描计数器产生电路。P10为码值转换和显示电路,通过调用转换函数binar_to_BCD将普通二进制码atime和btime转换为8位BCD码。显示电路分为工作显示和时间设置显示两部分。工作显示时通过计数器cnt4进行数码管位码的赋值,从而不断循环点亮4位数码管分别显示东西和南北方向的时间。时间设置显示时通过计数器cnt3进行位码赋值,循环点亮三位数码管显示设置的时间值和设置提示。P9为数码管段码译码电路。P11为移位寄存器电路,用来保存通过数字键盘输入得到的8位BCD码。

转换函数的原理很简单。7位二进制码与8位BCD码从10进制角度来看个位都是相同的,仅10位不同。故可以将7位二进制码减去与此码十位相同个位为0 的二进制码,从而保留了个位,再补上10位相应的二进制码即可。比如说95的二进制为1101111,将其减去90的二进制码1011010,得个位为101,即5,再补上10位1001,变为10010101,即为8位BCD码。反转换函数原理可以此类推。此外,程序中还通过去抖动模块debunce来处理数字键盘人为输入时产生的不稳定的抖动信号。

三. 系统功能框图

系统由按键输入及去抖电路、移位寄存电器电路、分频电路、码值转换与调时电路、码值转换与显示电路和东西、南北工作状态转换电路组成。

四. 关键控制的源程序

I.码值转换与调时电路
    P4:process (enter,ewadjust)
     begin
       if (enter'event and enter='1') then            --每按一次按键
          time<=BCD_to_binar(D);
          case adjust is          --按键计数器
          when"000"=>ayt<=time(3 downto 0);     --东西黄灯时间赋值
          when"001"=>agt<=time(5 downto 0);     --东西绿灯时间赋值
          when"010"=>alt<=time(5 downto 0);     --东西左拐灯时间赋值
          when"011"=>byt<=time(3 downto 0);     --南北黄灯时间赋值
          when"100"=>bgt<=time(5 downto 0);     --南北绿灯时间赋值
          when"101"=>blt<=time(5 downto 0);art<=byt+bgt+blt; brt<=agt+alt+ayt;  --南北左拐灯时间赋值,东西红灯时间自动赋值,南北红灯时间自动赋值              
when others=>null;
       end case;
        if (adjust="101") then adjust<="000";
        else adjust<=adjust+1;
        end if;
  end if;
if pre='0' then       --如果pre为0,采用预设值
art<="0101000"; ayt<="0101"; agt<="101101"; alt<="001111";
brt<="1000001"; byt<="0101"; bgt<="010100";  blt<="001111";
    end if;
  end process p4;
  
2.东西方向工作状态时间转换程序
P5: process(time_en,clk)
     variable counta:std_logic_vector(2 downto 0);
     begin
     if (clk'event and clk ='1')then
if (time_en='1') then         --工作状态
if(emer = '1')then            --紧急状态 
atime(6 downto 0)<= "0000000";  --工作状态回0
counta := "000";
elsif(yel='1')then            --警告状态 
atime(6 downto 0)<= "0000000"; --工作状态回0;
counta := "000";
else
if(atime="0000000") then
case counta is
   when"000"=>atime(5 downto 0)<=agt-1; checka<="000";  --东西绿灯时间
   when"001"=>atime(3 downto 0)<=ayt-1; checka<="001"--东西黄灯时间
   when"010"=>atime(5 downto 0)<=alt-1; checka<="010"--东西左拐灯时间
   when"011"=>atime(3 downto 0)<=ayt-1; checka<="011"--东西黄灯时间
   when"100"=>atime<=art-1; checka<="100";         --东西红灯时间
   when"101"=>atime(3 downto 0)<=ayt-1; checka<="101";  --东西黄灯时间
   when others=>null;
    end case;
    if (counta="101")then 
counta:="000";
       else  
counta:=counta+1;     --状态转换
       end if; 
else
atime<=atime-1;  --倒计时
     end if;   
end if;             
end if;
  end if;
end process p5;

3.东西方向工作状态亮灯转换程序
       P6: process(clk)
       begin
       ewled<=tempa;
        if(clk'event and clk ='1')then
        if (time_en='1') then
        if(emer = '1')then --紧急情况
        tempa<="1011";
        elsif(yel='1')then --深夜情况
        tempa<="1111";
        tempa(1)<=not tempa(1);  --黄灯闪烁
        else
        case checka is
        when "000" => --东西绿灯状态
        if (atime>"0000011") then --时间>3秒
        tempa<="1110"; --绿灯长亮
        elsif(atime>="0000000") then --时间<=3秒
        tempa(0)<=not tempa(0); --绿灯闪烁
        end if;
        when "001" | "011" | "101" => tempa<="1101"; --东西黄灯状态
        when "010" => --东西左拐灯
        if (atime>"0000011") then --时间>3秒
        tempa<="0111"; --左拐灯长亮
        elsif(atime>="0000000") then --时间<=3秒
        tempa(3)<=not tempa(3); --左拐灯闪烁
        end if;
        when "100" => tempa<="1011"; --东西红灯
        when others =>null;
        end case;
        end if;
        end if;
        end if;
       end process;
       
4.南北方向上的工作状态时间转换电路和工作状态亮灯转换程序与东西方向差不多一样,故省略。

5.数码管动态显示电路
P9: process(clk128hz)
     begin
       if clk128hz'event and clk128hz= '1' then 
          cnt4<=cnt4+1;             --工作显示位码扫描
          cnt3<=cnt3+1;             --调时显示位码扫描
       if cnt3="10" then
         cnt3<="00";   
       end if;
      end if;
 end process p7;

P10: process( time_en,atime,btime,time,cnt4,cnt3)
    begin
      if (time_en='1') then
       atimedisp<=h_d(atime);       --将7位二进制转换为8位BCD码
       btimedisp<=h_d(btime);
       case  cnt4 is
          when "00" =>  BT <= "0000001" ;db<=atimedisp(3 downto 0);  --扫描显示4位数码管
          when "01" =>  BT <= "0000010" ;db<=atimedisp(7 downto 4); 
          when "10" =>  BT <= "0100000" ;db<=btimedisp(3 downto 0); 
          when "11" =>  BT <= "1000000" ;db<=btimedisp(7 downto 4);
          when others =>null;
        end case;
       else
        case cnt3 is
          when "00" => BT <="0000100";db<=D(3 downto 0);        --扫描显示3位数码管
          when "01" => BT <="0001000";db<=D(7 downto 4);
          when "10" => BT <="0010000";db<= ewadjust+1;         --调时提示数码管
          when others =>null;
        end case;
       end if;
  end process p8;
  
  P11: process(db)
     begin 
     case db is --段码编码               
       when "0000" => SEGOUT<="0111111"; -- 0         
when "0001" => SEGOUT<="0000110"; -- 1
when "0010" => SEGOUT<="1011011"; -- 2
when "0011" => SEGOUT<="1001111"; -- 3
when "0100" => SEGOUT<="1100110"; -- 4
when "0101" => SEGOUT<="1101101"; -- 5
when "0110" => SEGOUT<="1111101"; -- 6
when "0111" => SEGOUT<="0000111"; -- 7
when "1000" => SEGOUT<="1111111"; -- 8
when "1001" => SEGOUT<="1101111"; -- 9
when others => SEGOUT<="XXXXXXX";       
  end case;

五. 波形仿真

由于系统较为复杂,若直接用源程序进行仿真,无法从波形图中反映其工作状态。故仿真时简化了电路,用clkout表示1Hz的脉冲,clk表示2Hz的时钟。ewcounter和sncounter显示当前的时间状态。

如图2,开始时系统处于正常工作状态,ewled为1110,即东西方向绿灯亮,snled为01111,即南北方向红灯亮。当进入深夜情况,即yel为1时,ewled轮流变为1101和1111,snled轮流变为10111,即东西和南北方向黄灯闪烁,同时时间回0。深夜情况结束后,系统重新回到正常工作状态。紧急情况类似,不同的是红灯进入长亮状态。

绿灯闪烁状态如图3。可见,进入3秒区后,ewled轮流变为1111和1110,即为绿灯闪烁。此两波形图出现的灯与时间变化不同步是由于为便于波形仿真而简化程序导致的。clkout时钟是每来一个clk上升沿产生的,如果如完整程序那样分频产生就不会出现这种状况。调试时工作状态的波形仿真从图上很难看出其是否运行正常,在此不作讨论。

6. 结束语

由以上设计可以看到,采用VHDL语言进行实用交通灯的设计方便快捷,效率非常高。这种设计方式已经广泛应用于各种数字系统的设计开发当中。EDA技术的产生和发展为电子工业的繁荣提供了强大的动力。