您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 
 订阅
FPGA极易入门教程----LED篇-跑马灯(流水灯)跑起来
 
作者:孤独的单刀
 
  488  次浏览      10 次
 2024-11-27
编辑推荐:
本文主要介绍了FPGA极易入门教程----LED篇相关内容。 希望对你的学习有帮助。
本文来自于CSDN,由火龙果软件Linda编辑、推荐。

1、LED的基本原理

LED,又名发光二极管。LED灯工作电流很小(有的仅零点几毫安即可发光),抗冲击和抗震性能好,可靠性高,寿命长。由于这些优点,LED灯被广泛用在仪器仪表中作指示灯、液晶屏背光源等诸多领域。不同材料的发光二极管可以发出红、橙、黄、绿、青、蓝、紫、白这八种颜色的光。下图是可以发出黄、红、蓝三种颜色的直插型二极管实物图,这种二极管长的一端是阳极,短的那端是阴极。

下图是开发板上用的贴片发光二极管实物图。贴片二极管的正面一般都有颜色标记,有标记的那端就是阴极。

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

2、串行与并行实现的区别

大多数开发板的LED都是设计成阴极接地,而阳极接到FPGA(或者单片机)的IO管脚上,所以我们只需要对IO口赋值即可实现LED的亮灭(赋值高电平==LED亮;赋值低电平==LED灭)。相信大多数同学都有C语言和单片机的基础(什么?你没有?那你还不去学),所以我们先来看一下单片机完成流水灯是怎么做的:

  1. #include<reg52.h>
  2. #define uchar unsigned char
  3. #define uint unsigned int
  4. sbit LED1 = P2^0;
  5. sbit LED2 = P2^1;
  6. sbit LED3 = P2^2;
  7. sbit LED4 = P2^3;
  8. sbit LED5 = P2^4;
  9. sbit LED6 = P2^5;
  10. sbit LED7 = P2^6;
  11. sbit LED8 = P2^7;
  12. void delay(uint z)
  13. {
  14. uint x,y;
  15. for(x = 0; x < z; x++)
  16. for(y = 0; y < 113; y++);
  17. }
  18. void main(void)
  19. {
  20. while(1)
  21. {
  22. LED1 = 0; //灯亮
  23. delay(1000); //持续亮1s
  24. LED1 = 1; //灯灭
  25. delay(1000); //持续灭1s
  26. LED2 = 0;
  27. delay(1000);
  28. LED2 = 1;
  29. delay(1000);
  30. LED3 = 0;
  31. delay(1000);
  32. LED3 = 1;
  33. delay(1000);
  34. LED4 = 0; //灯亮
  35. delay(1000); //持续亮1s
  36. LED4 = 1; //灯灭
  37. delay(1000); //持续灭1s
  38. }
  39. }

 

直接看main函数:串行设计的流水灯是怎么实现的呢?

1、首先点亮第1个LED----延时1s----熄灭第1个LED;

2、然后点亮第2个LED----延时1s----熄灭第2个LED;

3、然后点亮第3个LED----延时1s----熄灭第3个LED;

4、最后点亮第4个LED----延时1s----熄灭第4个LED;

可以看到整个结构都是顺序执行下来的,即符合单片的顺序结构,也非常契合人类的思维方式。那么FPGA不是顺序结构的,而是并行结构,无法直接一条一条指令地执行,那么该如何实现流水灯?

FPGA的工作是无法离开时钟的(组合逻辑除外)。我们可以设计一个always块,实现计数(或者说是计时)功能:每来一个时钟,让寄存器cnt的值累加1,直到加到预先设计的阈值再从0开始循环。假设FPGA的工作频率是50M,那么周期就是20ns。要实现计数到1s,则需要计数1s/20ns = 50_000_000次。所以我们的计时模块,就从0计数到50_000_000 – 1,即可实现计数1s。

同时我们再设计一个always块(LED显示),每当计数模块计数到了1s,就让LED的输出变化一次。比如初始显示最右边的LED,第1s到来时切换显示右边数第2个LED;第2s到来时切换显示右边数第3个LED;第3s到来时切换显示最左边的LED。这样就可以实现LED的流水显示了。

从上面的分析,可以通过流程图来直观感受串行设计与并行设计的不同:

3、流水灯的具体实现

第二章实际上已经把流水灯的实现方法阐明了:

构造一个计时器,循环计时1s

构造显示的always块,每当计数器计时到1s,则切换LED显示状态

1s计时器

上面算了在50M的时钟频率下,计数到1s需要计数50000000次,这个数转换成二进制需要26位才能表示,所以寄存器cnt的位宽是26位,如下:

  1. reg [25:0] cnt; //1s计时器
  2. always@(posedge sys_clk or negedge sys_rst_n)begin
  3. if(!sys_rst_n)
  4. cnt <= 26'd0; //复位为0
  5. else if(cnt == 50_000_000 - 1) //计数到1s
  6. cnt <= 26'd0; //清零计数器
  7. else //计数不到1s
  8. cnt <= cnt + 1'd1; //计数器每个周期累加1
  9. end

 

LED显示

每当计数器计时到1s,则切换LED显示状态:第一次只显示最右边的LED1,下一次显示LED2,再下一次显示LED3,下一次显示LED4,然后重复显示LED1,如此循环,如下图所示:

该部分代码如下:

  1. always@(posedge sys_clk or negedge sys_rst_n)begin
  2. if(!sys_rst_n)
  3. led <= 4'b0001; //复位为最右边的点亮
  4. else if(cnt == 50_000_000 - 1) //计数到1s
  5. //拼接运算。把最高位(最左边的LED)移动到最低位,第2、1、0位作为整体向左移动一位
  6. led <= {led[2:0],led[3]};
  7. else //计数不到1s
  8. led <= led; //保持LED状态不变
  9. end

 

需要注意的是:我们是使用移位运算符来实现LED的循环移位:把右边三位【2:0】变成高3位【3:1】,而最高位【3】变成最低位【0】,即可显示LED显示的整体左移。

完整代码如下;

  1. //**************************************************************************
  2. // *** 名称 : led_flow
  3. // *** 作者 : 孤独的单刀
  4. // *** 博客 : https://blog.csdn.net/wuzhikaidetb
  5. // *** 日期 : 2021.12
  6. // *** 描述 : 1s流水灯,循环左移
  7. //**************************************************************************
  8. module led_flow
  9. //============================< 信号 >======================================
  10. (
  11. //时钟和复位 --------------------------------------------
  12. input sys_clk , //系统时钟50M
  13. input sys_rst_n , //低电平有效的异步复位
  14. //DDR3写 ------------------------------------------------
  15. output reg [3:0] led //LED的赋值电平
  16. );
  17. reg [25:0] cnt; //1s计时器
  18. always@(posedge sys_clk or negedge sys_rst_n)begin
  19. if(!sys_rst_n)
  20. cnt <= 26'd0; //复位为0
  21. else if(cnt == 50_000_000 - 1) //计数到1s
  22. cnt <= 26'd0; //清零计数器
  23. else //计数不到1s
  24. cnt <= cnt + 1'd1; //计数器每个周期累加1
  25. end
  26. always@(posedge sys_clk or negedge sys_rst_n)begin
  27. if(!sys_rst_n)
  28. led <= 4'b0001; //复位为最右边的点亮
  29. else if(cnt == 50_000_000 - 1) //计数到1s
  30. //拼接运算。把最高位(最左边的LED)移动到最低位,第210位作为整体向左移动一位
  31. led <= {led[2:0],led[3]};
  32. else //计数不到1s
  33. led <= led; //保持LED状态不变
  34. end
  35. endmodule

 

4、仿真测试

进行仿真测试的时候,我们把代码稍微改下:1s计时模块改成1ms计时模块,即50_000_000改为50_000。原因是若计时1s则仿真需要的时间太长。仿真的testbench比较简单,只需要提供时钟和复位激励即可,如下:

  1. //**************************************************************************
  2. // *** 名称 : tb_led_flow
  3. // *** 作者 : 孤独的单刀
  4. // *** 博客 : https://blog.csdn.net/wuzhikaidetb
  5. // *** 日期 : 2021.12
  6. // *** 描述 : 1s流水灯,循环左移的测试模块
  7. //**************************************************************************
  8. `timescale 1ns/1ns //时间单位/精度
  9. //------------<模块及端口声明>----------------------------------------
  10. module tb_led_flow();
  11. reg sys_clk;
  12. reg sys_rst_n;
  13. wire [3:0] led;
  14. //------------<例化被测试模块>----------------------------------------
  15. led_flow led_flow_inst(
  16. .sys_clk (sys_clk ),
  17. .sys_rst_n (sys_rst_n ),
  18. .led (led )
  19. );
  20. //------------<设置初始测试条件>----------------------------------------
  21. initial begin
  22. sys_clk = 1'b0; //初始时钟为0
  23. sys_rst_n <= 1'b0; //初始复位
  24. #5 //5个时钟周期后
  25. sys_rst_n <= 1'b1; //拉高复位,系统进入工作状态
  26. end
  27. //------------<设置时钟>----------------------------------------------
  28. always #10 sys_clk = ~sys_clk; //系统时钟周期20ns
  29. endmodule

 

使用modelsim执行仿真,仿真结果如下:

可以看到:

4个led的初始结果是0001(1代表LED亮,0代表灭),所以此时最右边的LED1亮,其他LED熄灭

1ms(仿真为节省时间改为1ms,实际上板验证是1s)后led的值变成了0010,代表此时仅LED2亮,其他LED熄灭

再1ms后led的值变成了0100,代表此时仅LED3亮,其他LED熄灭

再1ms后led的值变成了1000,代表此时仅LED4亮,其他LED熄灭

再1ms后led的值变成了0001,代表此时仅LED1亮,其他LED熄灭

一直循环上面的状态,以此来实现LED的流水显示

5、上板测试

绑定对应管脚,全编译整个文件,将sof文件通过JTAG接口下载进FPGA开发板,观察其实验现象。结果如下:

可以看到LED以1S的速度向左循环移位,实现了预期的流水灯效果。

6、其他

创作不易,如果本文对您有帮助,还请多多点赞、评论和收藏。您的支持是我持续更新的最大动力!

关于本文,您有什么想法均可在评论区留言。如果需要整个工程,请在评论留下邮箱或者私信我邮箱(注意保护隐私)。

自身能力不足,如有错误还请多多指出!

版本信息

文件:V1.0

编号:0002

Quartus II:Quartus II 13.1 (64-bit)

Modelsim:Modelsim SE-64 2020.4

 

   
488 次浏览       10
相关文章

一文了解汽车嵌入式AUTOSAR架构
嵌入式Linux系统移植的四大步骤
嵌入式中设计模式的艺术
嵌入式软件架构设计 模块化 & 分层设计
相关文档

企点嵌入式PHP的探索实践
ARM与STM简介
ARM架构详解
华为鸿蒙深度研究
相关课程

嵌入式C高质量编程
嵌入式操作系统组件及BSP裁剪与测试
基于VxWorks的嵌入式开发、调试与测试
嵌入式单元测试最佳实践

最新活动计划
C++高级编程 12-25 [线上]
白盒测试技术与工具实践 12-24[线上]
LLM大模型应用与项目构建 12-26[特惠]
需求分析最佳实践与沙盘演练 1-6[线上]
SysML建模专家 1-16[北京]
UAF架构体系与实践 1-22[北京]
 
 
最新文章
基于FPGA的异构计算在多媒体中的应用
深入Linux内核架构——简介与概述
Linux内核系统架构介绍
浅析嵌入式C优化技巧
进程间通信(IPC)介绍
最新课程
嵌入式Linux驱动开发
代码整洁之道-态度、技艺与习惯
嵌入式软件测试
嵌入式C高质量编程
嵌入式软件可靠性设计
成功案例
某军工所 嵌入式软件架构
中航工业某研究所 嵌入式软件开发指南
某轨道交通 嵌入式软件高级设计实践
深圳 嵌入式软件架构设计—高级实践
某企业 基于IPD的嵌入式软件开发
更多...