搜索
bottom↓
回复: 0

《领航者ZYNQ之FPGA开发指南_V2》第三十一章 交通灯实验

[复制链接]

出0入234汤圆

发表于 2021-11-13 16:21:47 | 显示全部楼层 |阅读模式
1)实验平台:正点原子领航者V2 ZYNQ开发板
2)  章节摘自【正点原子】《领航者ZYNQ之FPGA开发指南_V2 》
3)购买链接:https://detail.tmall.com/item.htm?id=609032204975
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-329957-1-1.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流QQ群:90562473 1.png


2.jpg


3.png


第三十一章 交通灯实验
       交通信号灯是一种生活中非常常见的公共设施,它在马路上的路口、斑马线等位置起着疏导交通的作用,本章我们将使用交通信号灯模块来再现它的功能。
       本章包括以下几个部分:
       1.1简介
       1.2实验任务
       1.3硬件设计
       1.4程序设计
       1.5下载验证

1.1简介
       交通信号灯往往由红、绿、黄三种颜色的灯组成。红灯亮的时候,禁止通行;绿灯亮的时候,可以通行;黄灯亮的时候,提示通行时间已经结束,马上要转换为红灯。
       本次实验要再现的交通信号灯为十字路口处的信号灯,它由两对信号灯组成。信号灯实物如图 7.5.13.1所示:
第三十一章 交通灯实验291.png
图 7.5.13.1 交通信号灯
         图 7.5.13.2为十字路口交通信号灯简化示意图:
第三十一章 交通灯实验421.png
图 7.5.13.2 十字路口信号灯示意图
       单独一个方向上的信号灯点亮顺序是:红灯熄灭后绿灯亮,绿灯熄灭后黄灯亮,黄灯熄灭后红灯亮,这样一直循环下去。另外,同一方向上的一对信号灯亮的颜色一致,且显示的时间是一样的。
       为了模拟交通信号灯的功能,制作了表 31.1.1信号灯状态转换表:
第三十一章 交通灯实验3.png
表 31.1.1 交通信号灯状态转换表

       表 31.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。
       如图 7.5.13.3为交通信号灯的状态转换图:
第三十一章 交通灯实验1218.png
图 7.5.13.3 交通信号灯状态转换图
       另外,以东西方向为例:一个周期内,红灯发光30s,绿灯发光27s,黄灯发光3s。那么在红灯发光期间,数码管上显示的数字要从29递减到0;同理,绿灯发光期间,数码管上显示的数字要从26递减到0;黄灯发光的时候,数码管上显示的数字要从2递减到0。
1.2实验任务
       本节实验任务是通过领航者ZYNQ开发板和外接的交通信号灯扩展模块,再现交通信号灯的功能。
1.3硬件设计
       我们的领航者ZYNQ开发板上左边的J3扩展口可以用来外接交通信号灯扩展模块,交通信号灯扩展模块的原理图如图 7.5.13.1所示。
第三十一章 交通灯实验1569.png
图 7.5.13.1 交通信号灯原理图
       由上图可知,交通信号灯扩展模块四个方向共12个LED灯,而我们使用6个LED控制信号来驱动12个LED灯,这是因为东西方向或者南北方向LED灯的亮灭状态总是一致的,所以我们将东西方向或者南北方向颜色相同的LED灯并联在一起,这样设计的好处是减少了交通信号灯扩展模块LED控制信号的引脚。
       上图中四个共阳型数码管分别对应四个路口,每个路口用两位数码管显示当前状态的剩余时间。我们知道,在十字路口中,东西方向或者南北方向数码管显示的时间总是一样的。以东西方向为例,正因为这两个方向显示的时间一致,所以这两个方向的数码管,它们的十位可以用同一个位选信号来控制,个位用另一个位选信号来控制,这样的话,就可以实现两个位选信号控制东西方向共4位数码管的亮灭,南北方向的数码管同理。这样设计的好处是减少了交通信号灯扩展模块位选信号的引脚。
       需要注意的是,数码管由PNP型三极管驱动,当三极管的基极为低电平时,数码管相应的位被选通,所以交通信号灯扩展模块的位选信号是低电平有效的。
       交通信号灯实物图如图 7.5.13.2所示,我们本章实验设定交通信号灯的方向为上北下南,左西右东。
第三十一章 交通灯实验2184.png
图 7.5.13.2 交通信号灯实物图
       本实验中,交通信号灯管脚分配如下表所示。

3.png
表 31.3.1 交通信号灯实验管脚分配

       对应的XDC约束文件如下所示:
  1. set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
  2. set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]

  3. set_property -dict {PACKAGE_PIN T16 IOSTANDARD LVCMOS33} [get_ports {seg_led[0]}]
  4. set_property -dict {PACKAGE_PIN U17 IOSTANDARD LVCMOS33} [get_ports {seg_led[1]}]
  5. set_property -dict {PACKAGE_PIN V17 IOSTANDARD LVCMOS33} [get_ports {seg_led[2]}]
  6. set_property -dict {PACKAGE_PIN V18 IOSTANDARD LVCMOS33} [get_ports {seg_led[3]}]
  7. set_property -dict {PACKAGE_PIN T17 IOSTANDARD LVCMOS33} [get_ports {seg_led[4]}]
  8. set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports {seg_led[5]}]
  9. set_property -dict {PACKAGE_PIN Y18 IOSTANDARD LVCMOS33} [get_ports {seg_led[6]}]
  10. set_property -dict {PACKAGE_PIN Y19 IOSTANDARD LVCMOS33} [get_ports {seg_led[7]}]

  11. set_property -dict {PACKAGE_PIN Y17 IOSTANDARD LVCMOS33} [get_ports {led[0]}]  
  12. set_property -dict {PACKAGE_PIN T14 IOSTANDARD LVCMOS33} [get_ports {led[1]}]
  13. set_property -dict {PACKAGE_PIN V16 IOSTANDARD LVCMOS33} [get_ports {led[2]}]
  14. set_property -dict {PACKAGE_PIN Y16 IOSTANDARD LVCMOS33} [get_ports {led[3]}]
  15. set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports {led[4]}]
  16. set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports {led[5]}]

  17. set_property -dict {PACKAGE_PIN P15 IOSTANDARD LVCMOS33} [get_ports {sel[0]}]
  18. set_property -dict {PACKAGE_PIN P16 IOSTANDARD LVCMOS33} [get_ports {sel[1]}]
  19. set_property -dict {PACKAGE_PIN W18 IOSTANDARD LVCMOS33} [get_ports {sel[2]}]
  20. set_property -dict {PACKAGE_PIN W19 IOSTANDARD LVCMOS33} [get_ports {sel[3]}]
复制代码
1.4程序设计
       根据实验任务,我们可以大致规划出系统的控制流程:交通灯控制模块将需要显示的时间数据连接到数码管显示模块,同时将状态信号连接到led灯控制模块,然后数码管显示模块和led灯控制模块驱动交通信号灯外设工作。系统框图如图 7.5.13.1所示,
第三十一章 交通灯实验4844.png
图 7.5.13.1 交通灯实验系统框图
       各模块端口及信号连接如下图所示:
第三十一章 交通灯实验4927.png
图 7.5.13.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. 1   module top_traffic(
  2. 2       input                  sys_clk   ,    //系统时钟信号
  3. 3       input                  sys_rst_n ,    //系统复位信号
  4. 4      
  5. 5       output       [3:0]     sel       ,    //数码管位选信号
  6. 6       output       [7:0]     seg_led   ,    //数码管段选信号
  7. 7       output       [5:0]     led            //LED使能信号,红绿黄灯
  8. 8   );
  9. 9   
  10. 10  //wire define   
  11. 11  wire   [5:0]  ew_time;                    //东西方向状态剩余时间数据
  12. 12  wire   [5:0]  sn_time;                    //南北方向状态剩余时间数据
  13. 13  wire   [1:0]  state  ;                    //交通灯的状态,用于控制LED灯的点亮
  14. 14  
  15. 15  //*****************************************************
  16. 16  //**                    main code                     
  17. 17  //*****************************************************
  18. 18  //交通灯控制模块   
  19. 19  traffic_light u0_traffic_light(
  20. 20      .sys_clk                (sys_clk),   
  21. 21      .sys_rst_n              (sys_rst_n),      
  22. 22      .ew_time                (ew_time),
  23. 23      .sn_time                (sn_time),
  24. 24      .state                  (state)
  25. 25  );
  26. 26  
  27. 27  //数码管显示模块
  28. 28  seg_led    u1_seg_led(
  29. 29      .sys_clk                (sys_clk)  ,
  30. 30      .sys_rst_n              (sys_rst_n),
  31. 31      .ew_time                (ew_time),
  32. 32      .sn_time                (sn_time),
  33. 33      .en                     (1'b1),   
  34. 34      .sel                    (sel),
  35. 35      .seg_led                (seg_led)
  36. 36  );
  37. 37  
  38. 38  //led灯控制模块
  39. 39  led   u2_led(
  40. 40      .sys_clk                (sys_clk  ),
  41. 41      .sys_rst_n              (sys_rst_n),
  42. 42      .state                  (state    ),
  43. 43      .led                    (led      )
  44. 44  );
  45. 45  
  46. 46  endmodule        
复制代码
       在代码第22行和第23行,将交通灯控制模块输出的ew_time和sn_time实时时间数据信号连接到数码管显示模块;在代码的第24行,将交通灯控制模块输出的state状态连接到led灯控制模块。
       交通灯控制模块的代码如下:
  1. 1   module  traffic_light(
  2. 2       //input
  3. 3       input               sys_clk   ,        //系统时钟
  4. 4       input               sys_rst_n ,        //系统复位
  5. 5   
  6. 6       output  reg  [1:0]  state     ,        //交通灯的状态,用于控制LED灯的点亮
  7. 7       output  reg  [5:0]  ew_time   ,        //交通灯东西向数码管要显示的时间数据
  8. 8       output  reg  [5:0]  sn_time            //交通灯南北向数码管要显示的时间数据
  9. 9       );
  10. 10  
  11. 11  //parameter define
  12. 12  parameter  TIME_LED_Y    = 3;              //黄灯发光的时间
  13. 13  parameter  TIME_LED_R    = 30;             //红灯发光的时间
  14. 14  parameter  TIME_LED_G    = 27;             //绿灯发光的时间
  15. 15  parameter  WIDTH         = 25_000_000;     //产生频率为1hz的时钟
  16. 16  
  17. 17  //reg define
  18. 18  reg    [5:0]     time_cnt;                 //产生数码管显示时间的计数器   
  19. 19  reg    [24:0]    clk_cnt;                  //用于产生clk_1hz的计数器
  20. 20  reg              clk_1hz;                  //1hz时钟
  21. 21  
  22. 22  //*****************************************************
  23. 23  //**                    main code                     
  24. 24  //*****************************************************
  25. 25  //计数周期为0.5s的计数器  
  26. 26  always @ (posedge sys_clk or negedge sys_rst_n)begin
  27. 27      if(!sys_rst_n)
  28. 28          clk_cnt <= 25'b0;
  29. 29      else if (clk_cnt < WIDTH - 1'b1)
  30. 30          clk_cnt <= clk_cnt + 1'b1;
  31. 31      else
  32. 32          clk_cnt <= 25'b0;
  33. 33  end
  34. 34  
  35. 35  //产生频率为1hz的时钟
  36. 36  always @(posedge sys_clk or negedge sys_rst_n)begin
  37. 37      if(!sys_rst_n)
  38. 38          clk_1hz <= 1'b0;
  39. 39      else  if(clk_cnt == WIDTH - 1'b1)
  40. 40          clk_1hz <= ~ clk_1hz;
  41. 41      else  
  42. 42          clk_1hz <=  clk_1hz;
  43. 43  end
  44. 44  
  45. 45  //切换交通信号灯工作的4个状态,并产生数码管要显示的时间数据
  46. 46  always @(posedge clk_1hz or negedge sys_rst_n)begin
  47. 47      if(!sys_rst_n)begin        
  48. 48          state <= 2'd0;
  49. 49          time_cnt <= TIME_LED_G ;            //状态1持续的时间
  50. 50      end
  51. 51      else begin
  52. 52          case (state)
  53. 53              2'b0:  begin                    //状态1
  54. 54                  ew_time <= time_cnt + TIME_LED_Y - 1'b1;//东西方向数码管要显示的时间数据
  55. 55                  sn_time <= time_cnt - 1'b1;             //南北方向数码管要显示的时间数据
  56. 56                  if (time_cnt > 1)begin      //time_cnt等于1的时候切换状态
  57. 57                      time_cnt <= time_cnt - 1'b1;
  58. 58                      state <= state;
  59. 59                  end
  60. 60                  else begin
  61. 61                      time_cnt <= TIME_LED_Y; //状态2持续的时间
  62. 62                      state <= 2'b01;         //切换到状态2
  63. 63                  end
  64. 64              end
  65. 65              2'b01:  begin                   //状态2
  66. 66                  ew_time <= time_cnt  - 1'b1;
  67. 67                  sn_time <= time_cnt  - 1'b1;
  68. 68                  if (time_cnt > 1)begin
  69. 69                      time_cnt <= time_cnt - 1'b1;
  70. 70                      state <= state;
  71. 71                  end
  72. 72                  else begin
  73. 73                      time_cnt <= TIME_LED_G; //状态3持续的时间
  74. 74                      state <= 2'b10;         //切换到状态3
  75. 75                  end
  76. 76              end
  77. 77              2'b10:  begin                   //状态3
  78. 78                  ew_time <= time_cnt  - 1'b1;
  79. 79                  sn_time <= time_cnt + TIME_LED_Y - 1'b1;
  80. 80                  if (time_cnt > 1)begin
  81. 81                      time_cnt <= time_cnt - 1'b1;
  82. 82                      state <= state;
  83. 83                  end
  84. 84                  else begin
  85. 85                      time_cnt <= TIME_LED_Y; //状态4持续的时间
  86. 86                      state <= 2'b11;         //切换到转态4
  87. 87                  end
  88. 88              end
  89. 89              2'b11:  begin                   //状态4
  90. 90                  ew_time <= time_cnt  - 1'b1;
  91. 91                  sn_time <= time_cnt  - 1'b1;
  92. 92                  if (time_cnt > 1)begin
  93. 93                      time_cnt <= time_cnt - 1'b1;
  94. 94                      state <= state;
  95. 95                  end
  96. 96                  else begin
  97. 97                      time_cnt <= TIME_LED_G;
  98. 98                      state <= 2'b0;          //切换到状态1
  99. 99                  end
  100. 100             end         
  101. 101             default: begin
  102. 102                 state <= 2'b0;
  103. 103                 time_cnt <= TIME_LED_G;  
  104. 104             end
  105. 105         endcase
  106. 106     end
  107. 107 end                 
  108. 108
  109. 109 endmodule
复制代码
       因为交通灯控制模块是以秒为单位计时的,所以在代码第25行到第43行,我们通过分频产生频率为1HZ、周期为1s的时钟。在前面讲解表 31.3.1的时候,我们提到信号灯有4个工作状态,并且每个状态的持续时间已经作了详细的说明。所以在代码第52行至第100行,通过time_cnt计数器来切换这4个工作状态,并且在每个状态里,将东西和南北方向信号灯的状态剩余时间,分别赋值给ew_time、和sn_time寄存器。在讲解表 31.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。
       如图 7.5.13.3为交通灯控制模块的仿真图。state信号标示当前信号灯所处的工作状态。通过仿真图我们可以看出,在state等于00的时候(表 31.1.1处提到的状态1),ew_time和sn_time的信号变化情况符合设计要求。
第三十一章 交通灯实验12300.png
图 7.5.13.3 交通灯控制模块仿真图
        数码管显示模块的代码如下:
  1. 1   module seg_led(
  2. 2       input                  sys_clk     ,     //系统时钟
  3. 3       input                  sys_rst_n   ,     //系统复位
  4. 4       input        [5:0]     ew_time     ,     //东西方向数码管要显示的数值
  5. 5       input        [5:0]     sn_time     ,     //南北方向数码管要显示数值
  6. 6       input                  en          ,     //数码管使能信号                                                            
  7. 7       output  reg  [3:0]     sel         ,     //数码管位选信号
  8. 8       output  reg  [7:0]     seg_led           //数码管段选信号,包含小数点
  9. 9   );
  10. 10  
  11. 11  //parameter define
  12. 12  parameter  WIDTH = 50_000;                   //计数1ms的计数深度
  13. 13  
  14. 14  //reg define
  15. 15  reg    [15:0]             cnt_1ms;           //计数1ms的计数器
  16. 16  reg    [1:0]              cnt_state;         //用于切换要点亮数码管
  17. 17  reg    [3:0]              num;               //数码管要显示的数据
  18. 18  
  19. 19  //wire define
  20. 20  wire   [3:0]              data_ew_0;         //东西方向数码管的十位
  21. 21  wire   [3:0]              data_ew_1;         //东西方向数码管的各位
  22. 22  wire   [3:0]              data_sn_0;         //南北方向数码管的十位
  23. 23  wire   [3:0]              data_sn_1;         //南北方向数码管的各位
  24. 24  
  25. 25  //*****************************************************
  26. 26  //**                    main code                     
  27. 27  //*****************************************************
  28. 28  assign  data_ew_0   = ew_time / 10;          //取出东西向时间数据的十位
  29. 29  assign  data_ew_1   = ew_time % 10;          //取出东西向时间数据的个位
  30. 30  assign  data_sn_0   = sn_time / 10;          //取出南北向时间数据的十位
  31. 31  assign  data_sn_1   = sn_time % 10;          //取出南北向时间数据的个位
  32. 32  
  33. 33  //计数1ms
  34. 34  always @ (posedge sys_clk or negedge sys_rst_n) begin
  35. 35      if (!sys_rst_n)
  36. 36          cnt_1ms <= 15'b0;
  37. 37      else if (cnt_1ms < WIDTH - 1'b1)
  38. 38          cnt_1ms <= cnt_1ms + 1'b1;
  39. 39      else
  40. 40          cnt_1ms <= 15'b0;
  41. 41  end
  42. 42  
  43. 43  //计数器,用来切换数码管点亮的4个状态
  44. 44  always @ (posedge sys_clk or negedge sys_rst_n) begin
  45. 45      if (!sys_rst_n)
  46. 46          cnt_state <= 2'd0;
  47. 47      else  if (cnt_1ms == WIDTH - 1'b1)
  48. 48          cnt_state <= cnt_state + 1'b1;
  49. 49      else
  50. 50          cnt_state <= cnt_state;
  51. 51  end
  52. 52  
  53. 53  //先显示东西方向数码管的十位,然后是个位。再显示南北方向数码管的十位,然后个位
  54. 54  always @ (posedge sys_clk or negedge sys_rst_n) begin
  55. 55      if(!sys_rst_n) begin
  56. 56          sel  <= 4'b1111;
  57. 57          num  <= 4'b0;
  58. 58      end
  59. 59      else if(en) begin      
  60. 60          case (cnt_state)
  61. 61              3'd0 : begin     
  62. 62                  sel <= 4'b1110;              //驱动东西方向数码管的十位  
  63. 63                  num <= data_ew_0;
  64. 64              end      
  65. 65              3'd1 : begin     
  66. 66                  sel <= 4'b1101;              //驱动东西方向数码管的个位
  67. 67                  num <= data_ew_1;
  68. 68              end
  69. 69              3'd2 : begin
  70. 70                  sel <= 4'b1011;              //驱动南北方向数码管的十位
  71. 71                  num  <= data_sn_0;
  72. 72              end
  73. 73              3'd3 : begin
  74. 74                  sel <= 4'b0111;              //驱动南北方向数码管的个位
  75. 75                  num  <= data_sn_1 ;   
  76. 76              end
  77. 77              default : begin     
  78. 78                  sel <= 4'b1111;                     
  79. 79                  num <= 4'b0;
  80. 80              end
  81. 81          endcase
  82. 82      end
  83. 83      else  begin
  84. 84          sel <= 4'b1111;
  85. 85          num <= 4'b0;   
  86. 86      end
  87. 87  end
  88. 88  
  89. 89  //数码管要显示的数值所对应的段选信号      
  90. 90  always @ (posedge sys_clk or negedge sys_rst_n) begin
  91. 91      if (!sys_rst_n)
  92. 92          seg_led <= 8'b0;
  93. 93      else begin
  94. 94          case (num)              
  95. 95              4'd0 :     seg_led <= 8'b1100_0000;                                                        
  96. 96              4'd1 :     seg_led <= 8'b1111_1001;                           
  97. 97              4'd2 :     seg_led <= 8'b1010_0100;                           
  98. 98              4'd3 :     seg_led <= 8'b1011_0000;                           
  99. 99              4'd4 :     seg_led <= 8'b1001_1001;                           
  100. 100             4'd5 :     seg_led <= 8'b1001_0010;                           
  101. 101             4'd6 :     seg_led <= 8'b1000_0010;                           
  102. 102             4'd7 :     seg_led <= 8'b1111_1000;      
  103. 103             4'd8 :     seg_led <= 8'b1000_0000;      
  104. 104             4'd9 :     seg_led <= 8'b1001_0000;   
  105. 105             default :  seg_led <= 8'b1100_0000;
  106. 106         endcase
  107. 107     end
  108. 108 end
  109. 109
  110. 110 endmodule
复制代码
       由动态数码管实验可知,数码管显示刷新速度在毫秒级是比较合适。所以在代码第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。
       如图 7.5.13.4为数码管显示模块的仿真图。在ew_time和sn_time的值分别为24和21时,在这1s期间,数码管位选信号sel循环切换数码管对应的位选信号,然后依据num寄存器产生对应的段选信号seg_led。
第三十一章 交通灯实验17114.png
图 7.5.13.4 数码管显示模块仿真图
         led灯控制模块的代码如下:
  1. 1   module led (
  2. 2       input              sys_clk   ,       //系统时钟
  3. 3       input              sys_rst_n ,       //系统复位
  4. 4       input       [1:0]  state     ,       //交通灯的状态
  5. 5       output reg  [5:0]  led               //红黄绿LED灯发光使能
  6. 6   );
  7. 7   
  8. 8   //parameter define
  9. 9   parameter  TWINKLE_CNT = 20_000_000;     //让黄灯闪烁的计数次数
  10. 10  
  11. 11  //reg define
  12. 12  reg    [24:0]     cnt;                   //让黄灯产生闪烁效果的计数器
  13. 13  
  14. 14  //计数时间为0.2s的计数器,用于让黄灯闪烁                                                              
  15. 15  always @(posedge sys_clk or negedge sys_rst_n)begin                                 
  16. 16      if(!sys_rst_n)                                                                  
  17. 17          cnt <= 25'b0;                                                               
  18. 18      else if (cnt < TWINKLE_CNT - 1'b1)                                                                                                        
  19. 19          cnt <= cnt + 1'b1;                                                                                                                                                                                                                                                            
  20. 20      else                                                                             
  21. 21          cnt <= 25'b0;                                                               
  22. 22  end                                                                                 
  23. 23                                                                                      
  24. 24  //在交通灯的四个状态里,使相应的led灯发光                                                              
  25. 25  always @(posedge sys_clk or negedge sys_rst_n)begin                                 
  26. 26      if(!sys_rst_n)                                                                  
  27. 27          led <= 6'b100100;                                                            
  28. 28      else begin                                                                       
  29. 29          case(state)                                                                  
  30. 30              2'b00:led<=6'b100010;        //led寄存器从高到低分别驱动:东西向                        
  31. 31                                          //红绿黄灯,南北向红绿黄灯                                            
  32. 32              2'b01: begin                                                            
  33. 33                  led[5:1]<=5'b10000;                                                  
  34. 34                  if(cnt == TWINKLE_CNT - 1'b1)  //计数满0.2秒让黄灯的亮灭状况切换一次
  35. 35                                              //产生闪烁的效果                                 
  36. 36                      led[0] <= ~led[0];                                               
  37. 37                  else                                                                 
  38. 38                      led[0] <= led[0];                                                
  39. 39              end                                                                     
  40. 40              2'b10:led<=6'b010100;                                                   
  41. 41              2'b11: begin                                                            
  42. 42                  led[5:4]<=2'b00;                                                     
  43. 43                  led[2:0]<=3'b100;                                                   
  44. 44                  if(cnt == TWINKLE_CNT - 1'b1)                                            
  45. 45                      led[3] <= ~led[3];                                               
  46. 46                  else                                                                 
  47. 47                      led[3] <= led[3];                                               
  48. 48              end                                                                     
  49. 49              default:led<=6'b100100;                                                  
  50. 50          endcase                                                                     
  51. 51      end                                                                              
  52. 52  end  
  53. 53  
  54. 54  endmodule      
复制代码
       在代码的第25行至第48行,根据所处的状态值state,驱动相应的led灯发光。输出的高3位led[5:3]从高到低分别驱动东西方向的红、黄、绿三个LED灯,输出的低3位led[2:0]分别驱动南北方向红、黄、绿灯三个LED灯。
       在黄灯亮的状态期间,为了产生闪烁的效果,需要在一段时间内使其发光,在另一段时间内使其熄灭,这样循环直到黄灯发光的状态结束。在代码第14行至代码第22行,产生一个计数周期为0.2s的计数器,每0.2s让黄灯的亮灭状态切换一次,实现闪烁的效果。
       图 7.5.13.5为led控制模块的仿真图。可以看到,在state的值为01和11时(东西方向或者南北方向有黄灯发光的状态),led寄存器的值在一直在变化。
第三十一章 交通灯实验21676.png
图 7.5.13.5 led控制模块仿真图
       仿真文件代码如下所示:
  1. 1   `timescale 1ns/1ns
  2. 2   module tb_top_traffic     ;
  3. 3   reg            sys_clk    ;      //系统时钟
  4. 4   reg            sys_rst_n  ;      //系统复位
  5. 5                           
  6. 6   wire  [3:0]    sel        ;      //数码管位选信号
  7. 7   wire  [7:0]    seg_led    ;      //数码管段选信号
  8. 8   wire  [5:0]    led        ;      //led灯控制信号
  9. 9   
  10. 10  initial begin
  11. 11  sys_clk        <= 1'b0;
  12. 12  sys_rst_n      <= 1'b0;
  13. 13  # 20 sys_rst_n <= 1'b1;
  14. 14  end
  15. 15  
  16. 16  always # 10 sys_clk = ~sys_clk;  //产生频率为50Mhz的时钟
  17. 17  
  18. 18  //例化交通灯顶层模块
  19. 19  top_traffic  u_top_traffic(
  20. 20  .sys_clk       (sys_clk   ),
  21. 21  .sys_rst_n     (sys_rst_n ),                        
  22. 22  .sel           (sel       ),
  23. 23  .seg_led       (seg_led   ),
  24. 24  .led           (led       )   
  25. 25  );
  26. 26  
  27. 27  endmodule
复制代码
       本次仿真所用的文件在领航者FPGA开发板资料盘(A盘) → 4_SourceCode→1_Verilog→top_traffic→digital_recognition.sim的目录下。
1.5下载验证
       首先我们将下载器与领航者开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后将交通灯模块插到板子上去,最后连接开发板的电源,打开电源开关,如下图所示:
第三十一章 交通灯实验22690.png
图 7.5.13.1硬件连接图
        之后就可以下载bit文件了,可以看到交通灯模块开始运行,现象如下所示:
第三十一章 交通灯实验22787.png
图 7.5.13.2 交通灯实验现象
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子论坛 ( 公安交互式论坛备案:44190002001997 粤ICP备09047143号 )

GMT+8, 2022-7-7 03:25

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表