实验一:交通灯的设计及FPGA实现
作者: Saint
掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a
微博:https://weibo.com/5458277467/profile?topnav=1&wvr=6&is_all=1
GitHub:github.com/saint-000
一、实验目的:
熟练使用VHDL进行RTL级代码的编写;熟练运用EDA工具(modelsim、ISE)和 FPGA 开发板,完成较复杂的系统设计;锻炼系统级的设计能力及原型验证能力。完成模拟交通灯的设计及 FPGA 实验验证。
二、实验原理:
(1)交通信号灯的基本转换原理
本实验要求模拟的交通信号灯的转换原理如下图所示:
默认的初始状态为红灯状态,做 30 秒减计时。当时间从 30 秒递减到 0 秒后,红灯状态变为绿灯状态;重新 30 秒减计时,递减到 0 秒后,绿灯状态变为黄灯状态;做 5 秒减计时,递减到 0 秒后,黄灯状态变为红灯状态。如此循环进行转换。
(2)共阳数码管
实验主板上的 8 个数码管为共阳极型,即数码管的公共端接正极,显示段码信号接负极。8个数码管的显示段码信号复用,公共端则相互独立。数码管和 LCD 模块共用一组 IO 控制接口,用户做这俩实验时,注意数码管和 LCD 不要同时工作。通过数码管旁边的J1 跳线控制。
(3)拨码开关
实验主板上设计了一组8位的拨码开关,ON为低电平,OFF为高电平。拨码开关可供用户 输入一些高低电平。
(4)LED灯电路(启用需短接J4)
实验主板上 LED 灯电路属于板内资源,要使用它,需把 J4 短接。LED 灯的阳极接电源, 阴级接 FPGA、CPLD 的 I/0 口,因此是低电平驱动,只要由 FPGA、CPLD 在相应引脚给出 低电平,LED 就会发光。LED 灯电路原理图如图 1-6 所示,对应 FPGA 管脚如表 1-3 所示。
三、实验器材(设备、元器件):
计算机、FPGA 开发板、开发板附件
四、实验内容:
编写 VHDL 代码、生成下载文件,下载设计到 FPGA 实现如下功能:模拟路口交通信号灯的红、黄、绿灯的变化过程,分别用三个 LED 灯表示,并在数码管上动态显示当前状态剩余时间。要求红灯持续时间为 30 秒,黄灯 5 秒,绿灯 30秒。
五、实验步骤:
(1)创建工程
1.打开 Xilinx ISE 7.1i。选择 File-New Project 新建工程,根据需要设定工程名 称和工程路径。
2.点击下一步配置工程属性, Device Family 选择 Spartan2,Device 选择 XC2S50, Package 选择 TQ144,Speed Grade 选择-6。
3.直到完成工程的建立。
(2)建立和编写源文件
1.在 Sources in Project 中点右键,选择 New Source。在弹出的对话框中选择 VHDL Module,自己设定 File Name,一路下一步,建立一个新的源文件。
2.在新的源文件中录入设计,完成数字时钟的功能。
(3)设计的仿真
编写 test_bench,用 Modelsim 对设计进行仿真,验证功能正确性。
(4)上板调试
将设计下载到板上进行调试,验证设计的正确性。
六、实验数据及结果分析:
一.分频模块(将40MHZ的时钟频率变成1HZ的频率):
process(clr,clk_40Mhz)
begin
if(clr='0')then
count_25<="0000000000000000000000000";
elsif(rising_edge(clk_40Mhz))then
if(count_25="1111111111111111111111111")then
count_25<="0000000000000000000000000";
else
count_25<=count_25+'1';
end if;
end if;
end process;
clk_1hz<=count_25(24);(计数分频1hz)
通过分频模块可以得到一秒钟的时钟信号。
二.计数模块(红灯30秒,黄灯5秒,绿灯30秒):
1.绿灯30秒:
when s0 => --
if(high = "0000" and low = "0000") then --黄灯时间结束
current_state <= s1; --
green <= '0';
red <= '1';
yellow <= '1';
high <= "0010"; low <= "1001"; --绿灯显示时间为30
end if;
2.黄灯5秒
when s1 => --
if(high = "0000" and low = "0000") then --绿灯时间结束
current_state <= s2; --
yellow <= '0';
green <= '1';
red <= '1';
high <= "0000"; low <= "0100"; --黄灯时间为5
end if;
3.红灯30秒
when s2 =>
if(high = "0000" and low = "0000") then --黄灯时间结束
current_state <= s3;
red <= '0';
green <= '1';
yellow <= '1';
high <= "0010"; low <= "1001"; --红灯时间为30
end if;
三.动态显示模块
begin
case he is
when "0000"=>xianshi_z<="11000000";
when "0001"=>xianshi_z<="11111001";
when "0010"=>xianshi_z<="10100100";
when "0011"=>xianshi_z<="10110000";
when "0100"=>xianshi_z<="10011001";
when "0101"=>xianshi_z<="10010010";
when "0110"=>xianshi_z<="10000010";
when "0111"=>xianshi_z<="11111000";
when "1000"=>xianshi_z<="10000000";
when "1001"=>xianshi_z<="10010000";
when "1011"=>xianshi_z<="11000001";
when "1100"=>xianshi_z<="01100011";
when "1101"=>xianshi_z<="10000101";
when "1110"=>xianshi_z<="01100001";
when "1111"=>xianshi_z<="01110001";
when others=>xianshi_z<="00000011";
end case;
end process;
xianshi<=xianshi_z;
end BEHAVIORAL;
四.源码:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity TrafficLight is
Port(reset: in std_logic;
led_r,led_y,led_g:out std_logic;
xianshi:out std_logic_vector(7 downto 0);
clr:in std_logic;
clk_40Mhz:in std_logic;
EN:out std_logic_vector(7 downto 0)
);
end TrafficLight;
architecture BEHAVIORAL of TrafficLight is
type mmp is(s0,s1, s2,s3);
signal current_state: mmp;
signal red, yellow, green: std_logic;
signal high: std_logic_vector(3 downto 0); --倒计时显示的高位数
signal low: std_logic_vector(3 downto 0); --倒计时显示的低位数
signal xianshi_z,xianshi_h_z:std_logic_vector(7 downto 0);
signal he:std_logic_vector(3 downto 0);
signal count_25:std_logic_vector(24 downto 0);
signal s:std_logic_vector(7 downto 0);
signal clk_1hz,clk_sm:std_logic;
begin
p1:
process(clr,clk_40Mhz)
begin
if(clr='0')then
count_25<="0000000000000000000000000";
elsif(rising_edge(clk_40Mhz))then
if(count_25="1111111111111111111111111")then
count_25<="0000000000000000000000000";
else
count_25<=count_25+'1';
end if;
end if;
end process;
clk_1hz<=count_25(24);
clk_sm<=count_25(14); --分频
p2:
process(clk_sm,clr)
begin
if(clr='0')then
s<="11111110";
elsif(rising_edge(clk_sm))then
s(1)<= not s(1);
s(0)<= not s(0);
end if;
EN<=s;
end process;
p3:
process(s,he,low,high)
begin
if(s="11111110")then
he<=low;
else
he<=high;
end if;
end process; --位选
p4: process(reset, clk_1hz, current_state)
begin
if(clk_1hz = '1' and clk_1hz'event) then --当clk_1hz处于上升沿
if reset = '0' then
current_state <= s0; --复位时强行进入s0状态
low <= "0000";
high <= "0000";
else
if(low = "0000" and not(high = "0000")) then --如果低位不够减,则高位减一,低位置9
low <= "1001";
high <= high - 1;
else --每扫描一次,时间减一,如果低位够减,则只需低位减一,高位不变
low <= low - 1;
high <= high;
end if;
case current_state is --检测当前状态
when s0 => --
if(high = "0000" and low = "0000") then --黄灯时间结束
current_state <= s1; --
green <= '0';
red <= '1';
yellow <= '1';
high <= "0010"; low <= "1001"; --绿灯显示时间为30
end if;
when s1 => --
if(high = "0000" and low = "0000") then --绿灯时间结束
current_state <= s2; --
yellow <= '0';
green <= '1';
red <= '1';
high <= "0000"; low <= "0100"; --黄灯时间为5
end if;
when s2 => --
if(high = "0000" and low = "0000") then --黄灯时间结束
current_state <= s3; --
red <= '0';
green <= '1';
yellow <= '1';
high <= "0010"; low <= "1001"; --红灯时间为30
end if;
when s3 => --
if(high = "0000" and low = "0000") then --绿灯时间结束
current_state <= s0; --
yellow <= '0';
green <= '1';
red <= '1';
high <= "0000"; low <= "0100"; --黄灯时间为5
end if;
end case;
end if;
end if;
end process;
led_r<=red;
led_g<=green;
led_y<=yellow;
p5: process(he)
begin
case he is
when "0000"=>xianshi_z<="11000000";
when "0001"=>xianshi_z<="11111001";
when "0010"=>xianshi_z<="10100100";
when "0011"=>xianshi_z<="10110000";
when "0100"=>xianshi_z<="10011001";
when "0101"=>xianshi_z<="10010010";
when "0110"=>xianshi_z<="10000010";
when "0111"=>xianshi_z<="11111000";
when "1000"=>xianshi_z<="10000000";
when "1001"=>xianshi_z<="10010000";
when "1011"=>xianshi_z<="11000001";
when "1100"=>xianshi_z<="01100011";
when "1101"=>xianshi_z<="10000101";
when "1110"=>xianshi_z<="01100001";
when "1111"=>xianshi_z<="01110001";
when others=>xianshi_z<="00000011";
end case;
end process;
xianshi<=xianshi_z;
end BEHAVIORAL;
五.板上的显示结果:
七、实验结论:
起始状态黄灯熄灭,绿灯点亮,数码管数值显示跳变到30并每一秒钟递减一位。当数码管数值显示变为零0的时候,绿灯熄灭,红灯点亮,数码管数值显示跳变到30并每一秒钟递减一位。当数码管数值显示变为零0的时候,红灯熄灭,黄灯点亮,数码管数值显示跳变到5并每一秒钟递减一位,当数码管数值显示变为零0的时候回到起始状态。
八、总结及心得体会:
1.此次实验一开始我们想用状态机来控制红黄绿灯的显示,用一个总时间为75秒的计数器来显示红黄绿灯的秒数,并在其基础上分出两个30秒和一个5秒的计数器,但最后编写程序的时候发现程序太过复杂,程序编译时也存在许多错误,并且修改难度太大于是我们就换了另外一种方法。
2.第二种方法:我们把状态机取消,同时也把75秒的计数器给取消掉,在此基础上的两个30秒和一个5秒的计数器保持不变,然后把控制不同LED灯亮灭的代码放在计数器清零的末端,从而实现了数码管显示变为零的时候,一个LED灯亮,一个LED灯灭。
3.实验过程中最大的问题就是数码管的动态扫描模块(因为不太了解相关的代码及原理);我们把代码下载到板子时,LED的显示正常但是数码管的显示是乱码,然后反复修改动态扫描模块,直到最后得正确的数码管显示。
4.通过此次实验我们掌握了用VHDL进行RTL级代码的编写,并学会了如何用汇编语言编写分屏模块,计数模块,动态扫描模块的代码,同时也学会了FPGA实验验证和调试。
九、对本实验过程及方法、手段的改进建议:
1.外部可以设计一个中断源,当出现紧急情况时可以暂停交通灯的运作;
2.可以将1HZ的分频信号放加法器内(在交通灯灯的基础上添加一数字时钟显示功能)