FPGA学习分享--01 led流水灯的实现


需求

  1. 博宸电子的ZYNQ7020DEV开发板
  2. Vivado 2018.3
  3. 一定的verilog语言基础

一.任务剖析

1.1 实验目的

在1s内实现开发板上四个led灯的轮流闪烁,每个灯亮起0.25s.

1.2原理图及其分析

博主目前学习的是博宸电子的ZYNQ7020DEV开发板,下面是关于本次分享led流水灯的原理图


图1 涉及本次led流水灯的电路
图1  涉及本次led流水灯的电路

首先,从图1来看,每个led端口都和一个阻值为4.7k的电阻和一个发光二极管相连,并且发光二极管负端接地。要使得led发光,那么就要令发光二极管导通,即led端口应拉高。在编写代码中,当led输入端口输入为“1”时,代表拉高。


图2 涉及本次led流水灯的引脚
在这里插入图片描述
开发板上共有六个led灯。LED1,LED2上电即点亮,故不考虑。
对于剩下的四个led灯,其引脚分配如下:

灯号引脚
LED3Y14
LED4AA13
LED5AB14
LED6AB15

清楚了上述背景知识,接下来我们就可以编写代码啦。

二.总代码

2.1 敲写代码

2.1.1 代码内容

module led_stream(
  input			     clk,
  input 		     rst_n,
  
  output reg [3:0]   led       //设置为寄存器类型,否则第二个always模块会报错
  );
 
//定义一个1s的计时器,时钟频率为50MHz
reg [25:0] cnt;

always @(posedge clk or negedge rst_n) begin
		if (!rst_n)
		cnt <= 26'd0;
		else if(cnt < 26'd5000_0000)
		cnt <= cnt + 1'b1;
		else cnt <= 26'd0;
end

//通过计时器控制led交替闪烁,1s内闪烁4个灯	
always @(cnt) begin
		if(cnt<26'd1250_0000) led <= 4'b0001;
		if(cnt<26'd2500_0000 && cnt >= 26'd1250_0000) led <= 4'b0010;
		if(cnt<26'd3750_0000 && cnt >= 26'd2500_0000) led <= 4'b0100;
		if(cnt<26'd5000_0000 && cnt >= 26'd3750_0000) led <= 4'b1000;
end

endmodule

2.1.2 代码分析

  1. 时间:
    开发板内部自带有一个50M赫兹的晶振,即系统时钟clk为50M赫兹,也就是1s内时钟clk有50*10的6次方个上升沿。为了能够实现计时1s(实验目的),所以需要定义一个计数器cnt。时钟clk每达到一次上升沿,计数器cnt计数一次。所以当计数器cnt等于50000000(50M)时,时间等于1s。
    0.25s也就是50000000(50M)的四分之一,即12500000.

  2. 计数:
    当系统复位时,cnt赋值为0,准备开始计数计时。
    clk每上升一次,cnt的数值加一。
    当cnt值等于50M时,cnt赋值为0,重新开始计数。

always @(posedge clk or negedge rst_n) begin
		if (!rst_n)
		cnt <= 26'd0;
		else if(cnt < 26'd5000_0000)
		cnt <= cnt + 1'b1;
		else cnt <= 26'd0;
end
  1. 点亮:
    本实验先令LED3亮起0.25s,再让LED4亮起0.25s,随后是LED5,最后是LED6。 所以:
    当cnt的值处于0~12500000时,LED3端口赋值1,其余端口赋值为0.
    当cnt的值处于12500000~25000000时,LED4端口赋值1,其余端口赋值为0.
    当cnt的值处于25000000~37500000时,LED5端口赋值1,其余端口赋值为0.
    当cnt的值处于37500000~5000000时,LED6端口赋值1,其余端口赋值为0.
always @(cnt) begin
		if(cnt<26'd1250_0000) led <= 4'b0001;
		if(cnt<26'd2500_0000 && cnt >= 26'd1250_0000) led <= 4'b0010;
		if(cnt<26'd3750_0000 && cnt >= 26'd2500_0000) led <= 4'b0100;
		if(cnt<26'd5000_0000 && cnt >= 26'd3750_0000) led <= 4'b1000;
end

  1. module:
    定义两个输入:clk,rst-n。
    定义一个的输出:reg [3:0] led
    因为有四个led灯,所以led应该有四位,即[3:0],从3数到0
    并且led在always语句进行赋值,所以应该是reg寄存器类型
module led_stream(
  input			     clk,
  input 		     rst_n,
  
  output reg [3:0]   led       //设置为寄存器类型,否则第二个always模块会报错
  );

2.2 引脚与电压分配

  1. 方式一

在这里插入图片描述
按照上图箭头所指的四个部分,依次完成分配。最终结果应与上图一致。

  1. 方式二
    点任选两个个方框中任意一个,左键点击在这里插入图片描述
    选择第一项,加入一个新的约束文档在这里插入图片描述
    进行命名,这里与项目名保持一致
    在这里插入图片描述
    点击方框,直到看见xdc文件,在右侧屏幕进行手动引脚即电压分配
    在这里插入图片描述
    这里为了大家编写方便,我将代码复制到下边了
set_property PACKAGE_PIN Y14 [get_ports {led[0]}]
set_property PACKAGE_PIN AA13 [get_ports {led[1]}]
set_property PACKAGE_PIN AB14 [get_ports {led[2]}]
set_property PACKAGE_PIN AB15 [get_ports {led[3]}]
set_property PACKAGE_PIN W17 [get_ports clk]
set_property PACKAGE_PIN V4 [get_ports rst_n]

set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]

当然大家也不必担心,代码这么多自己该如何编写。它的格式都很固定,只需要根据引脚及电压更改一些参数而已。并且,当你采用方式一进行引脚分配后,Vivado会自动为你生成你的分配代码。

三. 仿真

因为有的同学手上可能没有板子,不过没有关系,Vivado上自带有仿真系统,为了让大家不受板子限制,博主在此也是做出了本次实验的相应仿真部分,供大家参考。仿真所涉及到的知识点会在以后详细讲解,这里大家先看看最后效果就好。

3.1 建立仿真

老样子,二选一
在这里插入图片描述
按照图片,选择第三个,建立仿真文件,下一步
在这里插入图片描述
命名,这里多加入一个tb下划线,用来警醒自己这应该作为头部文件
在这里插入图片描述
然后就可以开始编写代码啦
在这里插入图片描述

3.2 仿真代码

大家直接复制粘贴到代码编写区域即可

`timescale 1ns / 1ps   

module tb_led_stream();

reg         sys_clk;       
reg         sys_rst_n;    

wire  [3:0] led;
              
initial begin         //初始化                        
	sys_clk = 1'b0;
	sys_rst_n = 1'b0;
	#1 sys_rst_n = 1'b1;
end 

always #10 sys_clk = ~sys_clk;    //定义一个50M赫兹的时钟

led_stream u_led_stream(      //例化
.clk                  (sys_clk),        
.rst_n                (sys_rst_n),      

.led                  (led)   
);

endmodule

然后点击下图


在这里插入图片描述


进行仿真,仿真结果如下:
在这里插入图片描述
可以看见,每隔0.25s(250ms)不同的led灯依次点亮,满足实验要求。所以本次实验成功!

四.知识点

4.1 Verilog语法

  1. 时序always模块
    此模块的代码框架基本如下:
always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
		//初始化你的参数
		end
		else 
		//编写代码逻辑
end

并且大多数采用阻塞赋值,即“<=”

  1. 电平always模块
    此模块的代码框架基本如下:
always @(*) begin
		//代码逻辑部分	
end

并且大多数采用非阻塞赋值,即“=”

  1. 数值的定义
    对寄存器类型的变量而言,其赋值需要根据参数的位数进行。
reg [4:0] num;   //5位,所以num最大是 2*2*2*2*2 - 1 = 31
num = 5'd30;     //十进制d下赋值30;
num = 5'b01011;   //二进制b下赋值01011;

//同理,还有八进制,十六进制赋值,但最大不能超过十进制下的31;
  • 36
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值