该工程有两种模式:
模式0下,强制行人模式(强制红灯500ns)
模式1下,正常绿黄红循环(300ns-100ns-300ns)
模式切换利用按键进行,当按键未被按下时,且当复位结束后,模式1正常运行,当按键被按下,长达3个时钟周期的时长,此时进行模式0,强制行人模式。
这里利用了消抖的原理,如果有不正常的抖动,模式0的切换会不正常,本工程里面设计了持续长达3个时钟周期(60ns)才是真正的按键被按下,否则,依旧为模式1的运行模式。
但是消抖的原理讲来讲去,都是对按键按下的时长进行计数,然后利用边沿检测来确定,图像如下:

a_dly为a的延迟信号,当确保a被按下后,以a_dly为准,对信号进行赋值,产生一个高电平脉冲,而这个高电平脉冲需要(a信号取反)与(a_dly)的一个(与门)来产生。
代码如下:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
ENTITY traffic_lights IS
PORT(
clk : IN STD_LOGIC;
rst_n : IN STD_LOGIC;
key_pedestrian : IN STD_LOGIC;
led_r : OUT STD_LOGIC;
led_y : OUT STD_LOGIC;
led_g : OUT STD_LOGIC
);
END traffic_lights;
ARCHITECTURE bh OF traffic_lights IS
–仿真缩小时钟计数:20ns时钟 –15是时钟数,27是这个信号的位宽
CONSTANT GREEN_TIME : UNSIGNED(26 DOWNTO 0) := TO_UNSIGNED(15,27); –绿 300ns
CONSTANT YELLOW_TIME : UNSIGNED(26 DOWNTO 0) := TO_UNSIGNED(5,27); –黄 100ns
CONSTANT RED_TIME : UNSIGNED(26 DOWNTO 0) := TO_UNSIGNED(15,27); –红 300ns
CONSTANT RED_PED_TIME : UNSIGNED(26 DOWNTO 0) := TO_UNSIGNED(25,27); –行人红灯 500ns
CONSTANT C_DEBOUNCE_CNT: UNSIGNED(19 DOWNTO 0) := TO_UNSIGNED(2,20); –消抖2个周期
–内部信号定义
SIGNAL mode : STD_LOGIC;
SIGNAL light_state : STD_LOGIC_VECTOR(1 DOWNTO 0);–3个状态的位宽占据
SIGNAL cnt : UNSIGNED(26 DOWNTO 0);–计数模式0的时间
SIGNAL key0,key1,key2 : STD_LOGIC;
SIGNAL count_debounce_cnt: UNSIGNED(19 DOWNTO 0);–计数消抖所需要的时长,满时为真正消抖,即为真实按键按下
SIGNAL key_trig : STD_LOGIC; –检测按键按下的脉冲
SIGNAL ped_cnt : UNSIGNED(26 DOWNTO 0);–计数模式1的时间
SIGNAL exit_ped : STD_LOGIC; — 退出行人模式标志
BEGIN
–1.按键同步+消抖
PROCESS(clk,rst_n)
BEGIN
IF rst_n='0' THEN
key0 <= '1';
key1 <= '1';
key2 <= '1';
count_debounce_cnt <= (OTHERS=>'0');
ELSIF clk'EVENT AND clk='1' THEN
key0 <= key_pedestrian;
key1 <= key0;
IF(key1 /= key0) THEN
count_debounce_cnt <= C_DEBOUNCE_CNT;–参数传递
ELSIF(count_debounce_cnt > 0) THEN
count_debounce_cnt <= count_debounce_cnt – 1;
IF(count_debounce_cnt = 1) THEN
key2 <= key1;
END IF;
END IF;
END IF;
END PROCESS;
— 边沿检测:检测key2的下降沿
key_trig <= '1' WHEN (key2 = '0' AND key1 = '1') ELSE '0';
–2.行人模式控制
PROCESS(clk,rst_n)
BEGIN
IF rst_n='0' THEN –功能初始化
mode <= '0';
ped_cnt <= (OTHERS=>'0');
exit_ped <= '0';
ELSIF clk'EVENT AND clk='1' THEN
exit_ped <= '0'; — 默认清零 为循环计数做准备,此时模式0下不断循环
IF key_trig='1' AND mode='0' THEN –检测到按键的按下,立刻切换模式1
mode <= '1';
ped_cnt <= (OTHERS=>'0'); –模式1下的计数清零准备计数
ELSIF mode='1' THEN
IF ped_cnt >= RED_PED_TIME – 1 THEN –模式1下红灯的时长,这里是结束了模式1后的清零状态
mode <= '0'; –模式1结束了,进入模式0
ped_cnt <= (OTHERS=>'0'); –模式1的计数清零,为下次做准备
exit_ped <= '1'; — 产生退出脉冲 为进入模式0的循环做准备
ELSE
ped_cnt <= ped_cnt + 1; –如果模式1没有计数结束,就持续加加
END IF;
END IF;
END IF;
END PROCESS;
–3.全局计时计数器
PROCESS(clk,rst_n)
BEGIN
IF rst_n='0' THEN
cnt <= (OTHERS=>'0');
ELSIF clk'EVENT AND clk='1' THEN
IF mode='1' THEN
cnt <= (OTHERS=>'0');
ELSIF key_trig='1' THEN
cnt <= (OTHERS=>'0');
ELSIF exit_ped='1' THEN
cnt <= (OTHERS=>'0'); — 退出时清零,让绿灯从0开始
ELSE
IF (light_state="00" AND cnt >= GREEN_TIME-1) OR
(light_state="01" AND cnt >= YELLOW_TIME-1) OR
(light_state="10" AND cnt >= RED_TIME-1) THEN
cnt <= (OTHERS=>'0');
ELSE
cnt <= cnt + 1; –在红绿黄3个状态下,计数未满,则一直计数加加,直到满了清0
END IF;
END IF;
END IF;
END PROCESS;
–4.红绿灯状态跳转(关键:退出时强制切换到绿灯)
PROCESS(clk,rst_n)
BEGIN
IF rst_n='0' THEN
light_state <= "00"; –初始化绿灯
ELSIF clk'EVENT AND clk='1' THEN
IF mode='1' THEN –模式一状态下为红灯
light_state <= "10";
ELSIF exit_ped='1' THEN –收到模式1结束时候的脉冲,表示要切换到模式0
light_state <= "00"; — 退出行人模式时强制切换到绿灯
ELSE
CASE light_state IS
WHEN "00" =>
IF cnt >= GREEN_TIME-1 THEN –绿灯下计数结束后进入黄灯
light_state <= "01";
END IF;
WHEN "01" =>
IF cnt >= YELLOW_TIME-1 THEN –黄灯下计数结束后进入红灯
light_state <= "10";
END IF;
WHEN "10" =>
IF cnt >= RED_TIME-1 THEN –红灯下计数结束后进入绿灯,达到循环
light_state <= "00";
END IF;
WHEN OTHERS =>
light_state <= "00"; –初始化绿灯状态
END CASE;
END IF;
END IF;
END PROCESS;
–5.组合逻辑输出(0点亮、1熄灭) – 行人模式用 ped_cnt 直接判断
PROCESS(mode, light_state, rst_n, ped_cnt)
BEGIN
IF rst_n='0' THEN
led_r <= '1';
led_y <= '1';
led_g <= '1';
ELSE
— 行人模式:用 ped_cnt 判断,精确控制500ns
IF mode='1' OR (ped_cnt > 0 AND ped_cnt < RED_PED_TIME) THEN –模式1下且计数未满时红灯亮
led_r <= '0';
led_y <= '1';
led_g <= '1';
ELSE
CASE light_state IS
WHEN "00" =>
led_r <= '1';
led_y <= '1';
led_g <= '0';
WHEN "01" =>
led_r <= '1';
led_y <= '0';
led_g <= '1';
WHEN "10" =>
led_r <= '0';
led_y <= '1';
led_g <= '1';
WHEN OTHERS =>
led_r <= '1';
led_y <= '1';
led_g <= '1';
END CASE;
END IF;
END IF;
END PROCESS;
END bh;
此时需要额外编写仿真代码,操作与设计代码一样,请自行编写!!!
这里我用的是modelsim仿真的,需要在Quartus上进行仿真路径配置,如无法配置,请卸载该版本Quartus软件,重新安装,注意勾选并安装必备工具!!!

仿真波形如下所示:


从第一张图片可以看出key按下长达3个时钟周期,此时切换到模式0,由于时序延迟,这里产生key_trig脉冲后,仍需从第二个时钟周期开始计数,才是正常的模式0下的500ns计数时长。
从第二张图片可以明显看出key的按下时长不足3个时钟周期,模式不切换,模式1正常循环运行。
