• 【小月电子】ALTERA FPGA开发板(Spirit_V4)系统学习教程-LESSON3 LED流水灯


    LED流水灯例程讲解

    若要观看该博客配套的视频教程,可点击此链接

    开发板实物图

    在这里插入图片描述

    在这里插入图片描述

    图1.FPGA设计流程
    根据多年工作经验,总结出的FPGA的设计流程,概括起来总共有以上12步,其中根据项目难易度可省去其中一些步骤。比如非常简单的项目,我们可以省去虚线框里面的步骤,但是我们的入门级课程,即使再简单,也按照这12个步骤来进行讲解。

    1.需求解读

    1.1 需求

    实现8个LED灯依次点亮,每100ms点亮1个,直到8个LED灯被全部点亮,然后全部熄灭,再依次点亮。

    1.2 知识背景

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

    在这里插入图片描述

    图2.插件LED灯
    这种二极管长的一端是阳极,短的那端是阴极。 开发板上板载的是贴片LED灯,实物 如下图所示:

    在这里插入图片描述

    图3.贴片LED灯
    贴片发光二极管的正面一般都有颜色标记,有标记的那端就是阴极。发光二极管与普通二极管一样具有单向导电性。 给它加上阳极正向电压后,通过 5mA左右的电流就可以使二极管发光。 通过二极管的电流越大, 发出的光亮度越强。 不过我们一般将电流限定在3~20mA之间,否则电流过大就会烧坏二极管。

    1.3 硬件设计

    在这里插入图片描述

    有源晶振,与E1管脚相连

    在这里插入图片描述

    独立按键,与M1管脚相连

    在这里插入图片描述
    在这里插入图片描述

    LED灯原理图及PCB
    发光二极管的原理图如上图所示, LED0到LED7这8个发光二极管的阳极都连到3.3V上, 阴极分别与FPGA相应的管脚相连。原理图中LED与地 之间的电阻起到限流作用。通过原理图我们可以看出,LED0与FPGA的管脚 G16相连。在PCB图或实物上我们都标注出了管脚号,所以在绑管脚的时候 可以不用看原理图,直接看板上的丝印就可以,如上图PCB图所示。我们只 点亮一个LED灯(LED0),那我们只用关心LED0的管脚号(G16)。当管脚G16 输出低电平时,LED灯便有电流流过,驱动LED灯发光,如果G16输出高电 平,LED灯没有电流流过,LED不发光。

    1.4 接口说明

    信号名方向FPGA管脚号说明
    CLK_50M输入E1系统时钟,50Mhz
    KEY1输入M1独立按键,按下低电平,当作复位使用
    LED0输出G16与LED灯相连,低电平LED灯亮
    LED1输出G15与LED灯相连,低电平LED灯亮
    LED2输出J15与LED灯相连,低电平LED灯亮
    LED3输出J16与LED灯相连,低电平LED灯亮
    LED4输出K15与LED灯相连,低电平LED灯亮
    LED5输出K16与LED灯相连,低电平LED灯亮
    LED6输出L15与LED灯相连,低电平LED灯亮
    LED7输出L16与LED灯相连,低电平LED灯亮

        总结:通过上述说明,可以将需求解读成,先点亮LED0(100ms),接着点亮LED1(100ms)…最后点亮LED7,然后全部熄灭。再重复上述操作。

    2. 绘制理论波形图

    在这里插入图片描述

    逻辑框图

    在这里插入图片描述

    理论波形图

    3.新建QuartusII 工程

    为了让工程看起来整洁,同时方便工程移植。我们新建4个文件夹,分别是Project,Source,Sim,Doc。
    Project — 工程文件夹,里面放的QuartusII工程
    Source — 源代码文件夹,里面放的工程源码(.v文件或.vhd文件)
    Sim — 仿真文件夹,里面放的仿真相关的文件
    Doc — 存放相关资料,比如数据手册,需求文档等

    4.编写代码

    ///
    //QQ:3181961725
    //TEL/WX:13540738439
    //工程师:Mr Wang
    //模块介绍:实现LED流水灯
    ///
    module led_flow(
       input				rst_n	,//复位信号,低电平有效
       input				clk		,//时钟信号,50MHZ
       output	reg [7:0] 	led		 //LED灯控制信号
       );
       parameter	time_100ms=5000000;//100mS
       //parameter	time_100ms=2500;
       parameter	IDLE	=4'd0;//初始状态
       parameter	S0      =4'd1;//点亮第1个LED灯					
       parameter	S1      =4'd2;//点亮第1.2两个LED灯				
       parameter	S2      =4'd3;//点亮第1.2.3三个LED灯			
       parameter	S3      =4'd4;//点亮第1.2.3.4四个LED灯			
       parameter	S4      =4'd5;//点亮第1.2.3.4.5四个LED灯		
       parameter	S5      =4'd6;//点亮第1.2.3.4.5.6四个LED灯		
       parameter	S6      =4'd7;//点亮第1.2.3.4.5.6.7四个LED灯	
       parameter	S7      =4'd8;//点亮第1.2.3.4.5.6.7.8四个LED灯	
       parameter	S8		=4'd9;//LED灯全部熄灭					
       reg [3:0]	curr_st;
       reg	[24:0]	led0_cnt,led1_cnt,led2_cnt,led3_cnt;
       reg	[24:0]	led4_cnt,led5_cnt,led6_cnt,led7_cnt;
       reg	[24:0]	all_off_cnt;
    //状态机跳转
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	curr_st<=IDLE;
       else case(curr_st)
       	IDLE:curr_st<=S0;
       	S0:begin
       		if(led0_cnt==time_100ms-1)//当led0_cnt等于time_500ms-1时,跳转到S1状态
       			curr_st<=S1;
       		else;
       	end
       	S1:begin
       		if(led1_cnt==time_100ms-1)
       			curr_st<=S2;
       		else;
       	end
       	S2:begin
       		if(led2_cnt==time_100ms-1)
       			curr_st<=S3;
       		else;
       	end
       	S3:begin
       		if(led3_cnt==time_100ms-1)
       			curr_st<=S4;
       		else;
       	end
       	S4:begin
       		if(led4_cnt==time_100ms-1)
       			curr_st<=S5;
       		else;
       	end
       	S5:begin
       		if(led5_cnt==time_100ms-1)
       			curr_st<=S6;
       		else;
       	end
       	S6:begin
       		if(led6_cnt==time_100ms-1)
       			curr_st<=S7;
       		else;
       	end
       	S7:begin
       		if(led7_cnt==time_100ms-1)
       			curr_st<=S8;
       		else;
       	end
       	S8:begin
       		if(all_off_cnt==time_100ms-1)
       			curr_st<=S0;
       		else;
       	end
       	default:;
       endcase
    end
    //led0_cnt计数寄存器
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led0_cnt<=0;
       else if(curr_st==S0)
       	led0_cnt<=led0_cnt+1;
       else 
       	led0_cnt<=0;
    end
    //led1_cnt计数寄存器
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led1_cnt<=0;
       else if(curr_st==S1)
       	led1_cnt<=led1_cnt+1;
       else 
       	led1_cnt<=0;
    end
    //led2_cnt计数寄存器
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led2_cnt<=0;
       else if(curr_st==S2)
       	led2_cnt<=led2_cnt+1;
       else 
       	led2_cnt<=0;
    end
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led3_cnt<=0;
       else if(curr_st==S3)
       	led3_cnt<=led3_cnt+1;
       else 
       	led3_cnt<=0;
    end
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led4_cnt<=0;
       else if(curr_st==S4)
       	led4_cnt<=led4_cnt+1;
       else 
       	led4_cnt<=0;
    end
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led5_cnt<=0;
       else if(curr_st==S5)
       	led5_cnt<=led5_cnt+1;
       else 
       	led5_cnt<=0;
    end
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led6_cnt<=0;
       else if(curr_st==S6)
       	led6_cnt<=led6_cnt+1;
       else 
       	led6_cnt<=0;
    end
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led7_cnt<=0;
       else if(curr_st==S7)
       	led7_cnt<=led7_cnt+1;
       else 
       	led7_cnt<=0;
    end
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	all_off_cnt<=0;
       else if(curr_st==S8)
       	all_off_cnt<=all_off_cnt+1;
       else 
       	all_off_cnt<=0;
    end
    //LED灯接口赋值操作
    always@(posedge clk or negedge rst_n)begin
       if(!rst_n)
       	led<=8'hff;
       else case(curr_st)
       	IDLE	:led<=8'hff;
       	S0		:led<=8'hfe;
       	S1		:led<=8'hfc;
       	S2		:led<=8'hf8;
       	S3		:led<=8'hf0;
       	S4		:led<=8'he0;
       	S5		:led<=8'hc0;
       	S6		:led<=8'h80;
       	S7		:led<=8'h00;
       	S8	:led<=8'hff;
       	default:;
       endcase
    end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175

    5.编写仿真测试激励文件

    在这里插入图片描述

    仿真逻辑框图
    仿真时,我们主要关心的是输入端口,在led_flow这个模块中,有两个输入端口, rst_n,clk,所以,我们在仿真测试文件编写时,就是给这两个端口灌入激励信号。
    `timescale 1ns/1ns
    module led_flow_tb;
    	reg	rst_n;
    	reg	clk	;
    	initial	
    		begin
    			clk=0;
    			rst_n=0;//产生复位激励信号
    			#1000
    			rst_n=1;//产生复位激励信号
    		end
    	//产生时钟激励信号
    	always #10 clk<=~clk;
    	//例化被仿真模块
    	led_flow Uled_flow(
    	.rst_n	(rst_n),
    	.clk	(clk),
    	.led    ()
    	);
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6.Modelsim仿真

    这个例程非常简单,只用了一条语句,所以不需要仿真验证。但是为了给大家演示一个完整的开发流程,这个实验我们也新建一个仿真工程,从最简单的一个代码开始教大家如何编写仿真激励文件以及如何使用Modelsim软件进行仿真。将第三步编写的源码和第四步编写的仿真测试激励文件一起加入到Modelsim仿真工程中,即可进行仿真观察波形。
    Modelsim仿真一般有两种方法:

    1. 图形化界面仿真,即所有的操作都是在Modelsim软件界面上来完成,该方式的优点是,简单易学,适用于简单的项目,缺点是操作步骤繁琐。

    2. 批处理仿真,这种方式在仿真前需要编写相应的脚本文件,该方式的优点是,一键即可完成仿真,省时省力,缺点是前期需要编写脚本文件。为了让大家所学的能够很快的应用到工程实践,仅仅第一个实验和第二个实验,采用图形化界面仿真,后面的实验均采用批处理方式仿真。为了更贴近工程实际,从这一讲开始,我们就采用批处理方式仿真。仿真出的波形如下图所示:
      在这里插入图片描述

    7.对比波形图

    将第二步绘制的理论波形图与第六步Modelsim仿真出来的波形图进行对比,结果一致,说明我们的逻辑设计是正确的。如果发现比对结果不一致,就需要找到不一致的原因,最终要保证对比结果一致。
    通过对比,理论波形与仿真波形一致,说明功能符合设计要求。

    8.编译综合

    在这里插入图片描述

    9.绑定管脚

    当工程编译成功后,即可进行管脚分配(需要参考开发板的原理图)。我们店铺的开发板和模块在PCB板上均标注了信号名,在绑定管脚时也可以直接参照实物的连接关系。
    在这里插入图片描述
    管脚映射关系如下所示:
    在这里插入图片描述

    10.再次编译综合

    在这里插入图片描述

    11 下载SOF文件

    再次编译综合成功后便可以将生成的SOF文件下载到开发板
    在这里插入图片描述
    在这里插入图片描述
    下载成功后,便可以观察到开发板上的实验现象,如果实验现象与设计需求相符,那说明我们的设计是没有问题的,即可进行下一步固化JIC文件操作

    12 生成并固化JIC文件

    FPGA有一个特性,就是掉电后配置信息会丢失,所以我们需要将配置信息存储在配置芯片(FLASH)中,待开发板上电后,FPGA便会读取配置芯片中的配置信息,这样开发板掉电再上电后同样可正常工作。要将程序固化到配置芯片,需要先生成JIC文件。SOF文件转换成JIC文件步骤如下:

    12.1 file–>Convert Programming File…

    在这里插入图片描述

    12.2 选择JIC文件以及配置芯片的型号,FPGA的型号在这里插入图片描述

    标号1:选择生成文件的格式,我们选择JIC文件
    标号2:选择配置芯片的型号,我们选择EPCS16
    标号3:修改生成JIC文件的名字以及存放路径
    标号4:鼠标左键点击Flash Loader
    标号5:选择FPGA的型号,我们开发板用的是EP4CE6F17C8这款FPGA,所以我们也应该选这个型号,如下图所示:
    在这里插入图片描述

    12.3 选择SOF文件

    在这里插入图片描述
    标号1:鼠标左键点击SOF Data
    标号2:添加SOF文件,选中我们工程生成的SOF文件,如下图:
    在这里插入图片描述

    12.3 生成JIC文件

    在这里插入图片描述
    到此,JIC文件生成好,可以进行固化操作了。

    12.4 固化JIC文件

    在这里插入图片描述
    固化好以后,掉电程序也不会丢失了!

    实验现象

    与设计需求吻合,设计完成!

  • 相关阅读:
    ubuntu启动模式介绍以及如何进入单用户模式和恢复模式
    c#winform根据邮箱地址和密码一键发送email
    java编程基础总结——32.UDP网络编程
    从输入URL到页面展示发生了什么?
    policy gradient详解(附代码)
    Windows CMD常用命令大全(所见即所得)
    linux串口设备配置方法(固定ID)
    Java如何读取OS环境变量吗?
    目标检测YOLO实战应用案例100讲-基于无人机图像的房屋目标检测
    微服务开发系列 第五篇:Redis
  • 原文地址:https://blog.csdn.net/Moon_3181961725/article/details/126748592