超级版主
- 积分
- 4671
- 金钱
- 4671
- 注册时间
- 2019-5-8
- 在线时间
- 1224 小时
|
本帖最后由 正点原子运营 于 2021-10-30 10:07 编辑
1)实验平台:正点原子新起点V2FPGA开发板
2) 章节摘自【正点原子】《新起点之FPGA开发指南 V2.1》
3)购买链接:https://detail.tmall.com/item.htm?id=609758951113
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_xinqidian(V2).html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流QQ群:712557122
第十六章 交通灯实验
交通信号灯是一种生活中非常常见的公共设施,它在马路上的路口、斑马线等位置起着疏导交通的作用,本章我们将使用交通信号灯模块来再现它的功能。
本章包括以下几个部分:
16.1简介
16.2实验任务
16.3硬件设计
16.4程序设计
16.5下载验证
16.1简介
交通信号灯往往由红、绿、黄三种颜色的灯组成。红灯亮的时候,禁止通行;绿灯亮的时候,可以通行;黄灯亮的时候,提示通行时间已经结束,马上要转换为红灯。
本次实验要再现的交通信号灯为十字路口处的信号灯,它由两对信号灯组成。信号灯实物如图 16.1.1所示:
图 16.1.1 交通信号灯
图 16.1.2为十字路口交通信号灯简化示意图:
图 16.1.2 十字路口信号灯示意图
单独一个方向上的信号灯点亮顺序是:红灯熄灭后绿灯亮,绿灯熄灭后黄灯亮,黄灯熄灭后红灯亮,这样一直循环下去。另外,同一方向上的一对信号灯亮的颜色一致,且显示的时间是一样的。
为了模拟交通信号灯的功能,制作了表 16.1.1信号灯状态转换表:
表 16.1.1 交通信号灯状态转换表
表 16.1.1中,设定一个周期内,红灯发光30s,绿灯发光27s,黄灯发光3s。以东西方向信号灯状态为例,红灯发光的时间等于黄灯与绿灯发光的时间和。所以一个完整的状态转换周期是红灯发光的时间的两倍,也就是60s。
在东西方向红色信号灯发光的30s内,南北方向由绿灯切换到了黄灯;在南北方向红色信号灯发光的30s内,东西方向也由绿灯切换到了黄灯。因此我们将东西和南北方向信号灯同时保持在固定状态的时间段,划为一个状态。由此产生了循环往复的4个状态:
1、东西方向红灯亮27s,南北方向绿灯亮27s,然后切换到状态2;
2、东西方向红灯亮3s,南北方向黄灯亮3s,然后切换到状态3;
3、东西方向绿灯亮27s,南北方向红灯亮27s,然后切换到状态4;
4、东西方向黄灯亮3s,南北方向红灯亮3s,然后切换到状态1。
如图 16.1.3为交通信号灯的状态转换图:
图 16.1.3 交通信号灯状态转换图
另外,以东西方向为例:一个周期内,红灯发光30s,绿灯发光27s,黄灯发光3s。那么在红灯发光期间,数码管上显示的数字要从29递减到0;同理,绿灯发光期间,数码管上显示的数字要从26递减到0;黄灯发光的时候,数码管上显示的数字要从2递减到0。
16.2实验任务
本节实验任务是通过新起点开发板和外接的交通信号灯扩展模块,再现交通信号灯的功能。
16.3硬件设计
我们的新起点FPGA开发板上左边的P6扩展口可以用来外接交通信号灯扩展模块,交通信号灯扩展模块的原理图如图 16.3.1所示。
图 16.3.1 交通信号灯原理图
由上图可知,交通信号灯扩展模块四个方向共12个LED灯,而我们使用6个LED控制信号来驱动12个LED灯,这是因为东西方向或者南北方向LED灯的亮灭状态总是一致的,所以我们将东西方向或者南北方向颜色相同的LED灯并联在一起,这样设计的好处是减少了交通信号灯扩展模块LED控制信号的引脚。
上图中四个共阳型数码管分别对应四个路口,每个路口用两位数码管显示当前状态的剩余时间。我们知道,在十字路口中,东西方向或者南北方向数码管显示的时间总是一样的。以东西方向为例,正因为这两个方向显示的时间一致,所以这两个方向的数码管,它们的十位可以用同一个位选信号来控制,个位用另一个位选信号来控制,这样的话,就可以实现两个位选信号控制东西方向共4位数码管的亮灭,南北方向的数码管同理。这样设计的好处是减少了交通信号灯扩展模块位选信号的引脚。
需要注意的是,数码管由PNP型三极管驱动,当三极管的基极为低电平时,数码管相应的位被选通,所以交通信号灯扩展模块的位选信号是低电平有效的。
交通信号灯实物图如图 16.3.2所示,我们本章实验设定交通信号灯的方向为上北下南,左西右东。
图 16.3.2 交通信号灯实物图
本实验中,交通信号灯管脚分配如下表所示。
表 16.3.1 交通信号灯实验管脚分配
对应的TCL约束文件如下所示:
- set_location_assignment PIN_M2 -to sys_clk
- set_location_assignment PIN_M1 -to sys_rst_n
- set_location_assignment PIN_R12 -to sel[0]
- set_location_assignment PIN_T13 -to sel[1]
- set_location_assignment PIN_R11 -to sel[2]
- set_location_assignment PIN_G2 -to sel[3]
- set_location_assignment PIN_N8 -to seg_led[0]
- set_location_assignment PIN_P8 -to seg_led[1]
- set_location_assignment PIN_P6 -to seg_led[2]
- set_location_assignment PIN_M8 -to seg_led[3]
- set_location_assignment PIN_R14 -to seg_led[4]
- set_location_assignment PIN_N6 -to seg_led[5]
- set_location_assignment PIN_R13 -to seg_led[6]
- set_location_assignment PIN_T14 -to seg_led[7]
- set_location_assignment PIN_L9 -to led[0]
- set_location_assignment PIN_M9 -to led[1]
- set_location_assignment PIN_P9 -to led[2]
- set_location_assignment PIN_K9 -to led[3]
- set_location_assignment PIN_L10 -to led[4]
- set_location_assignment PIN_N9 -to led[5]
复制代码
16.4程序设计
根据实验任务,我们可以大致规划出系统的控制流程:交通灯控制模块将需要显示的时间数据连接到数码管显示模块,同时将状态信号连接到led灯控制模块,然后数码管显示模块和led灯控制模块驱动交通信号灯外设工作。系统框图如图 16.4.1所示,
图 16.4.1 交通灯实验系统框图
各模块端口及信号连接如下图所示:
图 16.4.2 交通信号灯顶层模块原理图
由上图可知,FPGA部分包括四个模块,顶层模块(top_traffic)、交通灯控制模块(traffic_light)、数码管显示模块(seg_led)、led灯控制模块(led)。在顶层模块中完成对其它三个模块的例化,并实现各模块之间的数据传递。
顶层模块(top_traffic):顶层模块完成了对其它三个子模块的例化、实现了子模块间的信号连接、并将led灯和数码管的驱动信号输出给外接设备(交通信号灯外设)。
交通灯控制模块(traffic_light):交通灯控制模块是本次实验的核心代码,这个模块控制信号灯的状态转换,将实时的状态信号state[1:0]输出给led灯控制模块(led),同时将东西和南北方向的实时时间数据ew_time[5:0]和sn_time[5:0]输出给数码管显示模块(seg_led)。
数码管显示模块(seg_led):接收交通灯控制模块传递过来的东西和南北方向的实时时间数据ew_time[5:0]和sn_time[5:0],并以此驱动对应的数码管,将数据显示出来。
led灯控制模块(led):根据接收到的实时状态信号state[1:0],驱动东西和南北方向的led发光。
顶层模块的代码如下:
- 1 module top_traffic(
- 2 input sys_clk , //系统时钟信号
- 3 input sys_rst_n , //系统复位信号
- 4
- 5 output [3:0] sel , //数码管位选信号
- 6 output [7:0] seg_led , //数码管段选信号
- 7 output [5:0] led //LED使能信号
- 8 );
- 9
- 10 //wire define
- 11 wire [5:0] ew_time; //东西方向状态剩余时间数据
- 12 wire [5:0] sn_time; //南北方向状态剩余时间数据
- 13 wire [1:0] state ; //交通灯的状态,用于控制LED灯的点亮
- 14
- 15 //*****************************************************
- 16 //** main code
- 17 //*****************************************************
- 18 //交通灯控制模块
- 19 traffic_light u0_traffic_light(
- 20 .sys_clk (sys_clk),
- 21 .sys_rst_n (sys_rst_n),
- 22 .ew_time (ew_time),
- 23 .sn_time (sn_time),
- 24 .state (state)
- 25 );
- 26
- 27 //数码管显示模块
- 28 seg_led u1_seg_led(
- 29 .sys_clk (sys_clk) ,
- 30 .sys_rst_n (sys_rst_n),
- 31 .ew_time (ew_time),
- 32 .sn_time (sn_time),
- 33 .en (1'b1),
- 34 .sel (sel),
- 35 .seg_led (seg_led)
- 36 );
- 37
- 38 //led灯控制模块
- 39 led u2_led(
- 40 .sys_clk (sys_clk ),
- 41 .sys_rst_n (sys_rst_n),
- 42 .state (state ),
- 43 .led (led )
- 44 );
- 45
- 46 endmodule
复制代码
在代码第22行和第23行,将交通灯控制模块输出的ew_time和sn_time实时时间数据信号连接到数码管显示模块;在代码的第24行,将交通灯控制模块输出的state状态连接到led灯控制模块。
交通灯控制模块的代码如下:
因为交通灯控制模块是以秒为单位计时的,所以在代码第25行到第43行,我们通过分频产生频率为1HZ、周期为1s的时钟。在前面讲解表 16.3.1的时候,我们提到信号灯有4个工作状态,并且每个状态的持续时间已经作了详细的说明。所以在代码第52行至第100行,通过time_cnt计数器来切换这4个工作状态,并且在每个状态里,将东西和南北方向信号灯的状态剩余时间,分别赋值给ew_time、和sn_time寄存器。在讲解表 16.3.1的时候,提到状态1里东西向红灯和南北向绿灯一起发光27s,但是实际上,红灯一共要发光30s,绿灯一共要发光27s。那么,在状态1开始的时候,东西方向红灯对应的数码管显示的初始值应该为29,南北方向绿灯对应的数码管显示的初始值应该为26,然后每秒各个方向数码管显示的值都递减1;状态1结束时,东西方向红灯对应的数码管显示3,南北方向绿灯对应的数码管显示0。在状态2开始的时候,东西方向红灯对应的数码管显示2,南北方向黄灯对应的数码管也显示2。状态结束的时候,两个方向的数码管都显示0。其他状态数码管的显示原理也是类似的。
需要注意的是,在代码第56行、68行、80行、92行,当time_cnt等于1的时候跳转状态,否则下一秒有的数码管会显示错误,比如显示63。这是因为当time_cnt等于0的时候跳转,由于time_cnt是6位寄存器,time_cnt-1则为6’b111111,也就是63。
如图 16.4.3为交通灯控制模块的仿真图。state信号标示当前信号灯所处的工作状态。通过仿真图我们可以看出,在state等于00的时候(表 16.1.1处提到的状态1),ew_time和sn_time的信号变化情况符合设计要求。
图 16.4.3 交通灯控制模块仿真图
数码管显示模块的代码如下:
由动态数码管实验可知,数码管显示刷新速度在毫秒级是比较合适。所以在代码第33行至第41行,产生一个计时周期为1ms的计数器。为了产生数码管位选控制信号,在代码第16行设置了一个2位计数器cnt_state。每经过1ms,计数器累加1次。总共能计数0,1,2,3这四个10进制数。在代码第60行至80行,依据cnt_state的值,通过给sel寄存器赋值来驱动数码管不同的位选,并将选中位要显示的数值赋值给num寄存器。在代码第94行至代码第104行,根据num的值可以产生相应的段选控制信号seg_led。
如图 16.4.4为数码管显示模块的仿真图。在ew_time和sn_time的值分别为24和21时,在这1s期间,数码管位选信号sel循环切换数码管对应的位选信号,然后依据num寄存器产生对应的段选信号seg_led。
图 16.4.4 数码管显示模块仿真图
led灯控制模块的代码如下:
- 1 module led (
- 2 input sys_clk , //系统时钟
- 3 input sys_rst_n , //系统复位
- 4 input [1:0] state , //交通灯的状态
- 5 output reg [5:0] led //红黄绿LED灯发光使能
- 6 );
- 7
- 8 //parameter define
- 9 parameter TWINKLE_CNT = 20_000_000; //让黄灯闪烁的计数次数
- 10
- 11 //reg define
- 12 reg [24:0] cnt; //让黄灯产生闪烁效果的计数器
- 13
- 14 //计数时间为0.2s的计数器,用于让黄灯闪烁
- 15 always @(posedge sys_clk or negedge sys_rst_n)begin
- 16 if(!sys_rst_n)
- 17 cnt <= 25'b0;
- 18 else if (cnt < TWINKLE_CNT - 1'b1)
- 19 cnt <= cnt + 1'b1;
- 20 else
- 21 cnt <= 25'b0;
- 22 end
- 23
- 24 //在交通灯的四个状态里,使相应的led灯发光
- 25 always @(posedge sys_clk or negedge sys_rst_n)begin
- 26 if(!sys_rst_n)
- 27 led <= 6'b100100;
- 28 else begin
- 29 case(state)
- 30 2'b00:led<=6'b100010; //led寄存器从高到低分别驱动:东西向
- 31 //红绿黄灯,南北向红绿黄灯
- 32 2'b01: begin
- 33 led[5:1]<=5'b10000;
- 34 if(cnt == TWINKLE_CNT - 1'b1) //计数满0.2秒让黄灯的亮灭状况切换一次
- 35 //产生闪烁的效果
- 36 led[0] <= ~led[0];
- 37 else
- 38 led[0] <= led[0];
- 39 end
- 40 2'b10:led<=6'b010100;
- 41 2'b11: begin
- 42 led[5:4]<=2'b00;
- 43 led[2:0]<=3'b100;
- 44 if(cnt == TWINKLE_CNT - 1'b1)
- 45 led[3] <= ~led[3];
- 46 else
- 47 led[3] <= led[3];
- 48 end
- 49 default:led<=6'b100100;
- 50 endcase
- 51 end
- 52 end
- 53
- 54 endmodule
复制代码
在代码的第25行至第48行,根据所处的状态值state,驱动相应的led灯发光。输出的高3位led[5:3]从高到低分别驱动东西方向的红、黄、绿三个LED灯,输出的低3位led[2:0]分别驱动南北方向红、黄、绿灯三个LED灯。
在黄灯亮的状态期间,为了产生闪烁的效果,需要在一段时间内使其发光,在另一段时间内使其熄灭,这样循环直到黄灯发光的状态结束。在代码第14行至代码第22行,产生一个计数周期为0.2s的计数器,每0.2s让黄灯的亮灭状态切换一次,实现闪烁的效果。
图 16.4.5为led控制模块的仿真图。可以看到,在state的值为01和11时(东西方向或者南北方向有黄灯发光的状态),led寄存器的值在一直在变化。
图 16.4.5 led控制模块仿真图
仿真文件代码如下所示:
- 1 `timescale 1ns/1ns
- 2 module tb_top_traffic ;
- 3 reg sys_clk ; //系统时钟
- 4 reg sys_rst_n ; //系统复位
- 5
- 6 wire [3:0] sel ; //数码管位选信号
- 7 wire [7:0] seg_led ; //数码管段选信号
- 8 wire [5:0] led ; //led灯控制信号
- 9
- 10 initial begin
- 11 sys_clk <= 1'b0;
- 12 sys_rst_n <= 1'b0;
- 13 # 20 sys_rst_n <= 1'b1;
- 14 end
- 15
- 16 always # 10 sys_clk = ~sys_clk; //产生频率为50Mhz的时钟
- 17
- 18 //例化交通灯顶层模块
- 19 top_traffic u_top_traffic(
- 20 .sys_clk (sys_clk ),
- 21 .sys_rst_n (sys_rst_n ),
- 22 .sel (sel ),
- 23 .seg_led (seg_led ),
- 24 .led (led )
- 25 );
- 26
- 27 endmodule
复制代码
16.5下载验证
首先我们将下载器与新起点开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后将交通灯模块插到板子上去,最后连接开发板的电源,打开电源开关,如下图所示:
图 16.5.1硬件连接图
之后就可以下载sof文件了,可以看到交通灯模块开始运行,现象如下所示:
图 16.5.2模拟交通灯功能 |
|