OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1221|回复: 2

[国产FPGA] 《ATK-DFPGL22G 之FPGA开发指南》第八章 LED流水灯实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4666
金钱
4666
注册时间
2019-5-8
在线时间
1224 小时
发表于 2023-11-1 17:53:59 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-10-31 17:24 编辑

第八章 LED流水灯实验


1)实验平台:正点原子 ATK-DFPGL22G开发板

2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)FPGA技术交流QQ群:435699340

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

LED流水灯作为一个经典的入门实验,其地位堪比编程界的“Hello,World!”。对于很多电子工程师来说,LED流水灯都是他们在硬件上观察到的第一个实验现象。本章我们同样通过LED流水灯实验,带你进入FPGA的精彩世界。
本章包括以下几个部分:
1.1          LED灯简介
1.2          实验任务
1.3          硬件设计
1.4          程序设计
1.5          下载验证

1.1 LED灯简介
LED,又名发光二极管。LED灯工作电流很小(有的仅零点几毫安即可发光),抗冲击和抗震性能好,可靠性高,寿命长。由于这些优点,LED灯被广泛用在仪器仪表中作指示灯、液晶屏背光源等诸多领域。

不同材料的发光二极管可以发出红、橙、黄、绿、青、蓝、紫、白这八种颜色的光。图 8.1.1是可以发出黄、红、蓝三种颜色的直插型二极管实物图,这种二极管长的一端是阳极,短的一端是阴极。图 8.1.2是开发板上用的贴片二极管实物图。贴片二极管的正面一般都有颜色标记,有标记的那端就是阴极。                           
image001.jpg
图 8.1.1 发光二极管实物图

image003.jpg
图 8.1.2 贴片发光二极管实物图

发光二极管与普通二极管一样具有单向导电性。给它加上阳极正向电压后,通过5mA左右的电流就可以使二极管发光。通过二极管的电流越大,发出的光亮度越强。不过我们一般将电流限定在3~20mA之间,否则电流过大就会烧坏二极管。

1.2 实验任务
本节实验任务是控制ATK-DFPGL22G开发板上的4个LED灯顺序点亮并熄灭,循环往复产生流水的现象。

1.3 硬件设计
开发板上LED的原理图如下图所示:
image005.png
图 8.3.1  LED灯硬件原理图

在图 8.3.1中,LED0~LED3这4个发光二极管均有连接三极管,这是由于LED0~LED3连接到了FPGA的IO口,但是电压只有1.35V,电压较低,所以此处连接三极管是为了起到放大信号的作用。当FPGA输出LED0到LED3为高电平时,三极管导通,点亮LED灯;当FPGA输出LED0到LED3为低电平时,三极管截止,LED灯熄灭。

本实验的管脚分配如下表所示:
QQ截图20231030152351.png
表 8.3.1  LED闪烁实验管脚分配

对应的FDC约束语句如下所示:
  1. create_clock-name {clk} [get_ports {sys_clk}] -period {20} -waveform {0.000 10.000}
  2. define_attribute{p:led[3]} {PAP_IO_DIRECTION} {OUTPUT}
  3. define_attribute{p:led[3]} {PAP_IO_LOC} {G1}
  4. define_attribute{p:led[3]} {PAP_IO_VCCIO} {1.5}
  5. define_attribute{p:led[3]} {PAP_IO_STANDARD} {LVCMOS15}
  6. define_attribute{p:led[3]} {PAP_IO_DRIVE} {4}
  7. define_attribute{p:led[3]} {PAP_IO_PULLUP} {TRUE}
  8. define_attribute{p:led[3]} {PAP_IO_SLEW} {SLOW}
  9. define_attribute{p:led[2]} {PAP_IO_DIRECTION} {OUTPUT}
  10. define_attribute{p:led[2]} {PAP_IO_LOC} {J7}
  11. define_attribute{p:led[2]} {PAP_IO_VCCIO} {1.5}
  12. define_attribute{p:led[2]} {PAP_IO_STANDARD} {LVCMOS15}
  13. define_attribute{p:led[2]} {PAP_IO_DRIVE} {4}
  14. define_attribute{p:led[2]} {PAP_IO_PULLUP} {TRUE}
  15. define_attribute{p:led[2]} {PAP_IO_SLEW} {SLOW}
  16. define_attribute{p:led[1]} {PAP_IO_DIRECTION} {OUTPUT}
  17. define_attribute{p:led[1]} {PAP_IO_LOC} {J6}
  18. define_attribute{p:led[1]} {PAP_IO_VCCIO} {1.5}
  19. define_attribute{p:led[1]} {PAP_IO_STANDARD} {LVCMOS15}
  20. define_attribute{p:led[1]} {PAP_IO_DRIVE} {4}
  21. define_attribute{p:led[1]} {PAP_IO_PULLUP} {TRUE}
  22. define_attribute{p:led[1]} {PAP_IO_SLEW} {SLOW}
  23. define_attribute{p:led[0]} {PAP_IO_DIRECTION} {OUTPUT}
  24. define_attribute{p:led[0]} {PAP_IO_LOC} {F3}
  25. define_attribute{p:led[0]} {PAP_IO_VCCIO} {1.5}
  26. define_attribute{p:led[0]} {PAP_IO_STANDARD} {LVCMOS15}
  27. define_attribute{p:led[0]} {PAP_IO_DRIVE} {4}
  28. define_attribute{p:led[0]} {PAP_IO_PULLUP} {TRUE}
  29. define_attribute{p:led[0]} {PAP_IO_SLEW} {SLOW}
  30. define_attribute{p:sys_clk} {PAP_IO_DIRECTION} {INPUT}
  31. define_attribute{p:sys_clk} {PAP_IO_LOC} {B5}
  32. define_attribute{p:sys_clk} {PAP_IO_VCCIO} {3.3}
  33. define_attribute{p:sys_clk} {PAP_IO_STANDARD} {LVCMOS33}
  34. define_attribute{p:sys_clk} {PAP_IO_PULLUP} {TRUE}
  35. define_attribute{p:sys_rst_n} {PAP_IO_DIRECTION} {INPUT}
  36. define_attribute{p:sys_rst_n} {PAP_IO_LOC} {G5}
  37. define_attribute{p:sys_rst_n} {PAP_IO_VCCIO} {1.5}
  38. define_attribute{p:sys_rst_n} {PAP_IO_STANDARD} {LVCMOS15}
  39. define_attribute{p:sys_rst_n} {PAP_IO_PULLUP} {TRUE}
复制代码

1.4 程序设计
由于发光二极管的阳极与FPGA的管脚相连,只需要改变与LED灯相连的FPGA管脚的电平,LED灯的亮灭状态就会发生变化。当FPGA管脚为高电平时,LED灯点亮;为低电平时,LED灯熄灭。

本次设计的模块端口及信号连接如下图所示:
image008.png
图 8.4.1 LED灯模块原理图

由于人眼的视觉暂留效应,流水灯状态变换间隔时间最好不要低于0.1s,否则就不能清晰地观察到流水效果。这里我们让流水灯每间隔0.2s变化一次。在程序中需要用一个计数器累加计数来计时,计时达0.2s后计数器清零并重新开始计数,这样就得到了固定的时间间隔。每当计数器计数满0.2s就让led灯发光状态变化一次。

流水灯模块的代码如下:
  1. 1  module flow_led(
  2. 2       input              sys_clk  ,  //系统时钟
  3. 3       input              sys_rst_n,  //系统复位,低电平有效
  4. 4      
  5. 5       output  reg  [3:0  led         //4个LED灯
  6. 6       );
  7. 7  
  8. 8  //reg define
  9. 9  reg [23:0 counter;
  10. 10
  11. 11 //*****************************************************
  12. 12 //**                    main code
  13. 13 //*****************************************************
  14. 14                                                                                                                                                                                                                          
  15. 15 //计数器对系统时钟计数,计时0.2秒
  16. 16 always @(posedge sys_clk or negedge sys_rst_n) begin
  17. 17      if (!sys_rst_n)
  18. 18          counter <= 24'd0;
  19. 19      else if (counter < 24'd1000_0000 – 1’d1)
  20. 20      //else if (counter < 24'd5) //仅用于仿真
  21. 21          counter <= counter +1'b1;
  22. 22      else
  23. 23          counter <= 24'd0;
  24. 24 end
  25. 25
  26. 26 //通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态
  27. 27 always @(posedge sys_clk or negedge sys_rst_n) begin
  28. 28      if (!sys_rst_n)
  29. 29          led <= 4'b0001;
  30. 30      else if(counter == 24'd1000_0000 – 1’d1)
  31. 31      //else if (counter == 24'd5) //仅用于仿真
  32. 32          led[3:0 <= {led[2:0],led[3]};
  33. 33      else
  34. 34          led <= led;
  35. 35 end
  36. 36
  37. 37 endmodule
复制代码
本程序中输入时钟为50MHz,所以一个时钟周期为20ns(1/50MHz)。因此计数器counter通过对50MHz系统时钟计数,计时到0.2s,需要累加0.2s/20ns=10000000次。在代码第23行,每当计时到0.2s计数器清零一次。

大家可以发现计数器(counter)计数到了999_9999,并没有计数到1000_0000。这是计数器是从0开始计数的,从0计数到999_9999,需要1000_0000个时钟周期,而系统时钟为20ns,所以计数的时间为0.2s,而从0计数到1000_0000需要1000_0001个时钟周期,因此其计数时间实际上比0.2s要多出20ns。我们将结合仿真为大家展示不减一和减一的区别,如图 8.4.3和图 8.4.4所示,。

同时,每当计数器计数到999_9999时,将各个LED灯的状态左移一位,并将最高位的值移动到最低位,循环往复。其他时间,LED灯的状态不变。如代码中第30至34行所示。

需要说明的是,led的初始值必须是一位为1,其它位为0,在循环左移的过程中才会呈现流水灯的效果;而如果led的初始值为0,则左移后led的状态仍然为0。代码中led的初始值是由复位信号(sys_rst_n)控制的,如代码中第28行和第29行所示。

为了验证我们的程序,我们在Modelsim内对代码进行仿真。为了减少仿真过程所需要的时间,这里我们将时间间隔计数器(counter)修改为5(即100ns(实际为120ns))。如下图所示:
image009.png
图 8.4.2 仅用于仿真的代码

Testbench模块代码如下:
  1. 1   `timescale  1ns/1ns               // 定义仿真时间单位1ns和仿真时间精度为1ns
  2. 2   
  3. 3   module  tb_flow_led ();               // 测试模块
  4. 4   
  5. 5   //输入
  6. 6   reg  sys_clk;                      //时钟信号
  7. 7   reg  sys_rst_n;                    // 复位信号
  8. 8   
  9. 9   //输出
  10. 10  wire  [3:0]  led;
  11. 11  
  12. 12  //*****************************************************
  13. 13  //**                    maincode
  14. 14  //*****************************************************
  15. 15  
  16. 16  //给输入信号初始值
  17. 17  initial begin
  18. 18     sys_clk            =1'b0;
  19. 19     sys_rst_n          =1'b0;     // 复位
  20. 20      #20 sys_rst_n  =1'b1;     // 在第21ns的时候复位信号信号拉高
  21. 21  end
  22. 22  
  23. 23  //50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
  24. 24  always #10sys_clk = ~sys_clk;
  25. 25  
  26. 26  //例化led模块
  27. 27 flow_led  u0_flow_led (
  28. 28      .sys_clk     (sys_clk  ),
  29. 29      .sys_rst_n   (sys_rst_n),
  30. 30      .led         (led      )
  31. 31  );
  32. 32  
  33. 33  endmodule
复制代码
仿真得到的波形图如下图所示:
image011.png
图 8.4.3 仿真波形图1

从图 8.4.3中可以看到,led端口寄存器的值按照0001→0010→0100→1000→0001的顺序变化,对应的各个LED灯的接口电平依次改变,呈现流水灯效果。

大家可以发现,计数器(counter)设置为5时,仿真时每个led灯亮起的实际时间为120ns,这是因为计数器是从0开始计数的,从0计数到5需要6个时钟周期,而系统时钟为20ns,因此仿真时每个led灯亮起的实际时间为120ns;如果要观察到每个led灯只亮起100ns,则需要减去一个时钟周期(5-1),即将5改为4即可,改完后仿真波形如下所示。
image013.png
图 8.4.4 仿真波形图2

1.5 下载验证
编译工程并生成比特流.sbit文件后,点击PDS工具栏中的“configuration” (图中红框位置),如下图所示。
image015.png
图 8.5.1 打开下载配置界面操作

此时将下载器一端连接电脑,另一端与开发板上的JTAG下载口连接,开发板连接电源线,如下图所示:
image017.png
图 8.5.2 开发板连接实物图

注意!一定要先把下载器的一端连接到了电脑、另一端连接了JTAG接口之后,再给开发板上电!否则,对开发板的JTAG接口进行带电热插拔,有一定概率会损坏JTAG接口!
开发板连接好电源线和下载器后,打开开发板电源开关,点击“Fabric Configuration”窗口中的“Scan Device”图标(图中红框位置)扫描设备,如下图所示:
image019.png
图 8.5.3 “Scan Device”图标

扫描成功后,在弹出的“assign new configuration file”窗口选择需要烧录的sbit文件(本试验中为“flow_led.sbit”)后点击“open”或双击需要烧录的sbit文件皆可,如下图所示:
image021.png
图 8.5.4 “assignnew configuration file”窗口

然后我们鼠标右击“Fabric Configuration”窗口中“PANGO”芯片点击“program”开始下载 ,操作如下图所示:
image023.png
图 8.5.5 下载比特流操作

程序下载完成后,配置完成灯会点亮,此时我们可以看到位于开发板上的四个LED灯在呈现流水灯现象,如下图所示:
image025.png
图 8.5.6 四个LED灯呈现流水灯现象
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

1

主题

7

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2023-11-29
在线时间
4 小时
发表于 2024-1-12 23:51:59 | 显示全部楼层
小白,做这个过程中碰到个问题,请教下:
1、使用独热码作为led灯信号时,为什么是led信号中最低位对应led0,最高位对应led3这样来排序的?(比如:led信号是4’b0001,对应led0亮,led1至led3灭)
     使用独热码时,为什么不需要用代码解释,当led信号处于什么状态时,led灯应该是怎么亮灯的?
回复 支持 反对

使用道具 举报

0

主题

4

帖子

0

精华

初级会员

Rank: 2

积分
62
金钱
62
注册时间
2023-12-5
在线时间
18 小时
发表于 2024-1-15 15:19:23 | 显示全部楼层
muyi80 发表于 2024-1-12 23:51
小白,做这个过程中碰到个问题,请教下:
1、使用独热码作为led灯信号时,为什么是led信号中最低位对应led ...

因为led[3:0]分别约束到了LED0~LED3的引脚上,根据LED电路原理图可以看出当引脚为高电平时,发光二极管导通,即点亮,所以当信号是4'b0001时,led0亮,led1至led3为灭
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 10:33

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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