📄 light.vhd
字号:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
--****************路口红绿灯的控制程序*****************--
--a向灯的状态变化为:红(10")-黄(2")-绿(4")-黄(2")-左转(4")-黄(2")
--同时,b向变化为:绿(4")-黄(2")-左转(4")-黄(2")-红(10")-黄(2")
--当阻断某方向时,该方向为红灯,另一方向为绿灯。倒计时10秒后,恢复原来的状态继续执行
entity light is
Port ( GCLK : in std_logic;
set1: in std_logic;
set2: in std_logic;
N : out std_logic_vector(7 downto 0);
P : out std_logic_vector(3 downto 0)
);
end light;
architecture Behavioral of light is
SIGNAL divcounter: std_logic_vector(27 downto 0);
SIGNAL divclk:std_logic;
SIGNAL sec_a,sec_b:std_logic_vector(3 downto 0);
SIGNAL state_a,state_b:std_logic_vector(3 downto 0);
SIGNAL temp:std_logic_vector(4 downto 0);
SIGNAL restore_statea,restore_stateb,restore_seca,restore_secb:std_logic_vector(3 downto 0);
SIGNAL divtenmsa,divtenmsb: std_logic_vector(19 downto 0);
SIGNAL blocka,blockb:std_logic;
SIGNAL stopcountera,stopcounterb:std_logic_vector(3 downto 0);
SIGNAL delaya,delayb:std_logic_vector(31 downto 0);
SIGNAL scan:std_logic_vector(8 downto 0);
SIGNAL scan_clk:std_logic_vector(1 downto 0);
SIGNAL SecSega,StaSega,SecSegb,StaSegb : std_logic_vector(7 downto 0);
begin
--将原始信号分频得到1s为周期的时钟信号divclk
DIV_CLOCK:process(GCLK)
begin
if GCLK='1' and GCLK'event then
if(divcounter>=X"1312D00") then -- 1312D00为十进制的20 000 000,晶震频率的一半,
divcounter<=X"0000000";
divclk<=not divclk; --故divclk的周期即为1s
else
divcounter<=divcounter+'1';
end if;
end if;
end process;
--产生扫描信号
SCAN_COUNTER:process(GCLK)
begin
if (GCLK'event and GCLK='1') then
scan<=scan+1;
end if;
end process;
scan_clk<=scan(8 downto 7); --恰为3.2微妙
--在扫描信号的作用下,轮流选通各个数码管
OUT_PUT:process(scan_clk)
begin
case scan_clk is
when "00"=>
N<=SecSega; --a向的倒计时,显示在第一个数码管
P<="1000";
when "01"=> --a向的灯状态,显示在第二个数码管
N<=StaSega;
P<="0100";
when "10"=> --b向的灯状态,显示在第三个数码管
N<=StaSegb;
P<="0010";
when "11"=> --b向的倒计时,显示在第四个数码管
N<=SecSegb;
P<="0001";
when others=>N<="11111111";P<="0000";
end case;
end process;
--a向秒计数驱动数码管
SECOND_A:process(sec_a)
begin
case sec_a is
when "0000" =>SecSega<="10001000";
when "0001" =>SecSega<="11011011";
when "0010" =>SecSega<="10100010";
when "0011" =>SecSega<="10010010";
when "0100" =>SecSega<="11010001";
when "0101" =>SecSega<="10010100";
when "0110" =>SecSega<="10000100";
when "0111" =>SecSega<="11011010";
when "1000" =>SecSega<="10000000";
when "1001" =>SecSega<="10010000";
when others =>SecSega<="11111111";
end case;
end process;
--b向秒计数驱动数码管
SECOND_B:process(sec_b)
begin
case sec_b is
when "0000" =>SecSegb<="10001000";
when "0001" =>SecSegb<="11011011";
when "0010" =>SecSegb<="10100010";
when "0011" =>SecSegb<="10010010";
when "0100" =>SecSegb<="11010001";
when "0101" =>SecSegb<="10010100";
when "0110" =>SecSegb<="10000100";
when "0111" =>SecSegb<="11011010";
when "1000" =>SecSegb<="10000000";
when "1001" =>SecSegb<="10010000";
when others =>SecSegb<="11111111";
end case;
end process;
--a向灯状态驱动数码管,state_a记录a向的灯颜色
STATEA:process(state_a)
begin
case state_a is
when "0000" =>StaSega<="11110111"; --状态为"0000"时,红色,管子中间一横亮
when "0001" =>StaSega<="10111111"; --"0001",表绿色,管子下面一横亮
when "0010" =>StaSega<="01111111"; --“0010”黄色,右下方的小点亮
when "0011" =>StaSega<="10101101"; --“0011”左转,L型亮
when others =>StaSega<="11111111"; --其它状态都灭
end case;
end process;
--b向灯状态驱动数码管
STATEB:process(state_b)
begin
case state_b is
when "0000" =>StaSegb<="11110111"; --状态为"0000"时,红色,管子中间一横亮
when "0001" =>StaSegb<="10111111"; --"0001",表绿色,管子下面一横亮
when "0010" =>StaSegb<="01111111"; --“0010”黄色,右下方的小点亮
when "0011" =>StaSegb<="10101101"; --“0011”左转,L型亮
when others =>StaSegb<="11111111";
end case;
end process;
--灯状态的转换,divclk是1"为周期的时钟,
--blocka=1,阻断a方向车辆,即a向为红灯,b向为绿灯
--blockb=1,阻断b方向车辆,即b向为红灯,a向为绿灯
--正常情况下,
--a向灯的状态变化为:红(10")-黄(2")-绿(4")-黄(2")-左转(4")-黄(2")
--同时,b向变化为:绿(4")-黄(2")-左转(4")-黄(2")-红(10")-黄(2")
--当阻断某方向时,先保存原状态,阻断完成后,再恢复,继续执行以前的。
SHIFT_STATE:process(divclk, blocka,blockb)
begin
if(divclk'event and rising_edge(divclk)) then
if(blocka = '1') then --阻断a向时
if(stopcountera = "0000") then --如果是第一次扫描到阻断信号
restore_statea <= state_a; --保存a、b方向原状态,秒的计数
restore_seca <= sec_a;
restore_stateb <= state_b;
restore_secb <= sec_b;
--初始化a、b方向的两个参数:
state_a <= "0000"; --a向灯设为红
sec_a <= X"9"; --设置秒数为9,即都从9开始倒计时
state_b <= "0001"; --b向灯设为绿
sec_b <= X"9";
else --如果不是第一次扫描到信号,
sec_a <= sec_a - '1'; --a、b秒数减1,即倒计时
sec_b <= sec_b - '1';
end if;
stopcountera <= stopcountera + '1'; --计数增加
elsif(blockb = '1') then --阻断b方向时,基本上同上面一样
if(stopcounterb = "0000") then
restore_statea <= state_a;
restore_seca <= sec_a;
restore_stateb <= state_b;
restore_secb <= sec_b;
state_a <= "0001"; --a向灯设为绿
sec_a <= X"9";
state_b <= "0000"; --b向灯设为红
sec_b <= X"9";
else
sec_a <= sec_a - '1';
sec_b <= sec_b - '1';
end if;
stopcounterb <= stopcounterb + '1';
else --当没有阻断信号时,执行下面的
if(stopcountera > "0010") then --如果刚从阻断a状态恢复过来,则先恢复原状态
state_a <= restore_statea ;
sec_a <= restore_seca ;
state_b <= restore_stateb;
sec_b <= restore_secb;
stopcountera <= "0000";
elsif(stopcounterb > "0010") then--如果刚从阻断b状态恢复过来,也要先恢复原状态
state_a <= restore_statea ;
sec_a <= restore_seca ;
state_b <= restore_stateb;
sec_b <= restore_secb;
stopcounterb <= "0000";
else --正常执行时,
temp <= temp+'1'; --temp是循环计数的变量,一个循环就是灯从
if(temp >= "10111") then--红(10")-黄(2")-绿(4")-黄(2")-左转(4")-黄(2")
temp <= "00000"; --的变化过程,刚好23秒,10111-00000
end if;
case temp is --检查temp的值,由temp控制灯的状态变化
when "00000" => --当temp=0000时,将A灯变为绿,B灯变为红,分别设置倒计时时间
state_a <= "0001"; --a改为绿灯, 等待4"
sec_a <= X"3"; --倒计时开始值为3
state_b <= "0000"; --b红,等待10"
sec_b <= X"9";
when "00100" => --4″之后,
state_a <= "0010"; --a改为黄灯,等待2" ,此时b依然为红
sec_a <= X"1";
sec_b <= sec_b - '1'; --b向倒计时一次
when "00110" =>
state_a <= "0011"; --2″过后,a改为左转灯,等待4"
sec_a <= X"3";
sec_b <= sec_b - '1';
when "01010" =>
state_a <= "0010"; --a,b 都改为黄灯 2 "
sec_a <= X"1";
state_b <= "0010";
sec_b <= X"1";
when "01100" =>
state_a <= "0000"; --a变为红,b变为绿 4"
sec_a <= X"9";
state_b <= "0001";
sec_b <= X"3";
when "10000" =>
sec_a <= sec_a - '1'; --a为红,b为黄 2"
state_b <= "0010";
sec_b <= X"1";
when "10010" => --a为红,b为左拐 4"
sec_a <= sec_a - '1';
state_b <= "0011";
sec_b <= X"3";
when "10110" => --a,b都为黄 2"
state_a <= "0010";
sec_a <= X"1";
state_b <= "0010";
sec_b <= X"1";
when others => --temp为中间值的时候,只做倒数
sec_a <= sec_a - '1';
sec_b <= sec_b - '1';
end case;
end if;
end if;
end if;
end process;
--产生阻断a向的信号,blocka=‘1’表示阻断a向,需要消除抖动
CHECKK1:process(set1,blockb,GCLK)
begin
if(blockb = '1') then --先判断是否已经阻断b向,如果已经阻断b向,则不处理阻断a向的请求
else
if(GCLK'event and GCLK = '1') then
if(blocka = '1') then --阻断a成功,保持10秒
delaya <= delaya +'1';
if(delaya >= X"17D78400") then --17D78400为十进制的400M,记录到该值时,刚好为10秒时间
blocka <= '0'; --10秒一过,恢复阻断信号
end if;
else --没有任何阻断时,
if(set1='1') then --按钮没按下,set1='1'
divtenmsa <= X"00000";
elsif(set1 = '0') then -- 如果按钮按下,需要等待10ms,如果还是按下的,则产生阻断信号,这样就实现了按钮的消抖
divtenmsa <= divtenmsa + '1'; --按钮按下时间的计数,一个GCLK周期加1次
end if;
if(divtenmsa >= X"61A80") then --如果按钮按下超过10毫秒
if(set1 = '0') then
blocka <= '1'; --blocka 设为 1, 表示阻止A向的车辆。
delaya <= X"00000000";
elsif(set1 = '1') then
blocka <= '0';
end if;
divtenmsa <= X"00000"; --计数重新置0
end if;
end if;
end if;
end if;
end process;
--产生阻断b向的信号,blockb=‘1’表示阻断b向,操作原理基本同上面
CHECKK2:process(set2,blocka,GCLK)
begin
if(blocka = '1') then
else
if(GCLK'event and GCLK = '1') then
if(blockb = '1') then
delayb <= delayb +'1';
if(delayb >= X"17D78400") then
blockb <= '0';
end if;
else
if(set2='1') then
divtenmsb <= X"00000";
elsif(set2 = '0') then
divtenmsb <= divtenmsb + '1';
end if;
if(divtenmsb >= X"61A80") then
if(set2 = '0') then
blockb <= '1'; --blockb = 1 表示阻止B向的车辆。
delayb <= X"00000000";
elsif(set2 = '1') then
blockb <= '0';
end if;
divtenmsb <= X"00000";
end if;
end if;
end if;
end if;
end process;
end Behavioral;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -