|
本帖最后由 正点原子 于 2021-1-25 18:38 编辑
1)实验平台:正点原子达芬奇FPGA开发板
2)购买链接:https://detail.tmall.com/item.htm?id=624335496505
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:905624739 点击加入:
第八章流水灯实验
流水灯是指多个LED灯按照一定的时间间隔,顺序点亮并熄灭,周而复始形成流水效果。本章我们同样通过达芬奇开发板来实现流水灯实验。
本章包括以下几个部分:
88.1简介
8.2实验任务
8.3硬件设计
8.4程序设计
8.5下载验证
8.1简介
我们在“LED灯闪烁实验”中对LED灯作了详细的介绍,如果大家对这部分内容不是很熟悉的话,请参考“LED灯闪烁实验”中的简介部分。
8.2实验任务
本节实验任务是使达芬奇开发板上的4个LED灯顺序点亮并熄灭,循环往复产生流水的现象。
8.3硬件设计
发光二极管的原理图如图 8.3.1所示, LED0到LED3这4个发光二极管的阴极都连到地( GND)上, 阳极分别与FPGA相应的管脚相连。原理图中LED与地之间的电阻起到限流作用。
图 8.3.1 LED灯硬件原理图
本实验中,系统时钟、按键复位以及LED端口的管脚分配如下表 8.3.1所示:
表 8.3.1 流水灯实验管脚分配
对应的XDC约束语句如下:
- set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
- set_property PACKAGE_PIN R2 [get_ports {led[0]}]
- set_property PACKAGE_PIN R3 [get_ports {led[1]}]
- set_property PACKAGE_PIN V2 [get_ports {led[2]}]
- set_property PACKAGE_PIN Y2 [get_ports {led[3]}]
- set_property PACKAGE_PIN R4 [get_ports sys_clk]
- set_property PACKAGE_PIN U2 [get_ports sys_rst_n]
- set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
- set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n]
复制代码
8.4程序设计
本次设计的模块端口及信号连接如图 8.4.1所示:
图 8.4.1 流水灯模块原理图
由于人眼的视觉暂留效应,流水灯状态变换间隔时间最好不要低于0.1s,否则就不能清晰地观察到流水效果。这里我们让流水灯每间隔0.2s变化一次。在程序中需要用一个计数器累加计数来计时,计时达0.2s后计数器清零并重新开始计数,这样就得到了固定的时间间隔。每当计数器计数满0.2s就让led灯发光状态变化一次。
流水灯模块的代码如下:
- 1 module flow_led(
- 2 input sys_clk , //系统时钟
- 3 input sys_rst_n, //系统复位,低电平有效
- 4
- 5 output reg [3:0] led //4个LED灯
- 6 );
- 7
- 8 //reg define
- 9 reg [23:0] counter;
- 10
- 11 //*****************************************************
- 12 //** main code
- 13 //*****************************************************
- 14
- 15 //计数器对系统时钟计数,计时0.2秒,仿真时设为10
- 16 always @(posedge sys_clk or negedge sys_rst_n) begin
- 17 if (!sys_rst_n)
- 18 counter <= 24'd0;
- 19 else if (counter < 24'd1000_0000)
- 20 //else if (counter < 24'd9) //仅用于仿真
- 21 counter <= counter + 1'b1;
- 22 else
- 23 counter <= 24'd0;
- 24 end
- 25
- 26 //通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态
- 27 always @(posedge sys_clk or negedge sys_rst_n) begin
- 28 if (!sys_rst_n)
- 29 led <= 4'b0001;
- 30 else if(counter == 24'd1000_0000)
- 31 //else if(counter == 24'd9) //仅用于仿真
- 32 led[3:0] <= {led[2:0],led[3]};
- 33 else
- 34 led <= led;
- 35 end
- 36
- 37 endmodule
复制代码
本程序中输入时钟为50MHz,所以一个时钟周期为20ns(1/50MHz)。因此计数器counter通过对50MHz系统时钟计数,计时到0.2s,需要累加0.2s/20ns=10000000次。在代码第23行,每当计时到0.2s计数器清零一次。
同时,每当计数器计数到10000000时,将各个LED灯的状态左移一位,并将最高位的值移动到最低位,循环往复。其他时间,LED灯的状态不变。如代码中第30至34行所示。
需要说明的是,led的初始值必须是一位为1,其它位为0,在循环左移的过程中才会呈现流水灯的效果;而如果led的初始值为0,则左移后led的状态仍然为0。代码中led的初始值是由复位信号(sys_rst_n)控制的,如代码中第28行和第29行所示。这里的复位信号对应的就是板载的复位按键,尽管在上电后没有按下复位按键,由于FPGA芯片内部有一个上电检测模块,一旦检测到电源电压超过检测门限后,就产生一个上电复位脉冲(Power On Reset)送给所有的寄存器,led的初始值就是在这个时候复位成4’b0001的。
我们在Vivado中对流水灯程序进行仿真,为了减少仿真过程所需要的时间,将流水灯状态变化的间隔时间修改为10个时钟周期。仿真得到的波形图如图 8.4.2所示,led端口寄存器的值按照0001→0010→0100→1000→0001的顺序变化,对应的各个LED灯的接口电平依次改变。
图 8.4.2仿真波形图
仿真过程用到的测试程序如下所示:
- 1 `timescale 1ns/1ns // 定义仿真时间单位1ns和仿真时间精度为1ns
- 2
- 3 module flow_led_tb; // 测试模块
- 4
- 5 //parameter define
- 6 parameter T = 20; // 时钟周期为20ns
- 7
- 8 //reg define
- 9 reg sys_clk; // 时钟信号
- 10 reg sys_rst_n; // 复位信号
- 11
- 12 //wire define
- 13 wire [3:0] led;
- 14
- 15 //*****************************************************
- 16 //** main code
- 17 //*****************************************************
- 18
- 19 //给输入信号初始值
- 20 initial begin
- 21 sys_clk = 1'b0;
- 22 sys_rst_n = 1'b0; // 复位
- 23 #(T+1) sys_rst_n = 1'b1; // 在第21ns的时候复位信号拉高
- 24 end
- 25
- 26 //50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
- 27 always #(T/2) sys_clk = ~sys_clk;
- 28
- 29 //例化led模块
- 30 flow_led u0_flow_led (
- 31 .sys_clk (sys_clk ),
- 32 .sys_rst_n (sys_rst_n),
- 33 .led (led )
- 34 );
- 35
- 36 endmodule
复制代码
8.5下载验证
编译工程并生成比特流.bit文件后,接下来我们下载比特流.bit文件,验证LED灯流水的功能。程序下载完成后,就能在开发板上看到流水灯的效果了。如下图所示:
图 8.5.1 流水灯实验图
|
|