实现8个LED灯依次点亮,每100ms点亮1个,直到8个LED灯被全部点亮,然后全部熄灭,再依次点亮。
LED灯简介
LED,又名发光二极管。 LED灯工作电流很小(有的仅零点几毫安即可发光) , 抗 冲击和抗震性能好,可靠性高,寿命长。由于这些优点, LED灯被广泛用在仪器仪表 中作指示灯、 液晶屏背光源等诸多领域。不同材料的发光二极管可以发出红、 橙、 黄、 绿、 青、蓝、 紫、白这八种颜色的光。 如下图所示:
信号名 | 方向 | 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,然后全部熄灭。再重复上述操作。
为了让工程看起来整洁,同时方便工程移植。我们新建4个文件夹,分别是Project,Source,Sim,Doc。
Project — 工程文件夹,里面放的QuartusII工程
Source — 源代码文件夹,里面放的工程源码(.v文件或.vhd文件)
Sim — 仿真文件夹,里面放的仿真相关的文件
Doc — 存放相关资料,比如数据手册,需求文档等
///
//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
`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
这个例程非常简单,只用了一条语句,所以不需要仿真验证。但是为了给大家演示一个完整的开发流程,这个实验我们也新建一个仿真工程,从最简单的一个代码开始教大家如何编写仿真激励文件以及如何使用Modelsim软件进行仿真。将第三步编写的源码和第四步编写的仿真测试激励文件一起加入到Modelsim仿真工程中,即可进行仿真观察波形。
Modelsim仿真一般有两种方法:
图形化界面仿真,即所有的操作都是在Modelsim软件界面上来完成,该方式的优点是,简单易学,适用于简单的项目,缺点是操作步骤繁琐。
批处理仿真,这种方式在仿真前需要编写相应的脚本文件,该方式的优点是,一键即可完成仿真,省时省力,缺点是前期需要编写脚本文件。为了让大家所学的能够很快的应用到工程实践,仅仅第一个实验和第二个实验,采用图形化界面仿真,后面的实验均采用批处理方式仿真。为了更贴近工程实际,从这一讲开始,我们就采用批处理方式仿真。仿真出的波形如下图所示:
将第二步绘制的理论波形图与第六步Modelsim仿真出来的波形图进行对比,结果一致,说明我们的逻辑设计是正确的。如果发现比对结果不一致,就需要找到不一致的原因,最终要保证对比结果一致。
通过对比,理论波形与仿真波形一致,说明功能符合设计要求。
当工程编译成功后,即可进行管脚分配(需要参考开发板的原理图)。我们店铺的开发板和模块在PCB板上均标注了信号名,在绑定管脚时也可以直接参照实物的连接关系。
管脚映射关系如下所示:
再次编译综合成功后便可以将生成的SOF文件下载到开发板
下载成功后,便可以观察到开发板上的实验现象,如果实验现象与设计需求相符,那说明我们的设计是没有问题的,即可进行下一步固化JIC文件操作
FPGA有一个特性,就是掉电后配置信息会丢失,所以我们需要将配置信息存储在配置芯片(FLASH)中,待开发板上电后,FPGA便会读取配置芯片中的配置信息,这样开发板掉电再上电后同样可正常工作。要将程序固化到配置芯片,需要先生成JIC文件。SOF文件转换成JIC文件步骤如下:
标号1:选择生成文件的格式,我们选择JIC文件
标号2:选择配置芯片的型号,我们选择EPCS16
标号3:修改生成JIC文件的名字以及存放路径
标号4:鼠标左键点击Flash Loader
标号5:选择FPGA的型号,我们开发板用的是EP4CE6F17C8这款FPGA,所以我们也应该选这个型号,如下图所示:
标号1:鼠标左键点击SOF Data
标号2:添加SOF文件,选中我们工程生成的SOF文件,如下图:
到此,JIC文件生成好,可以进行固化操作了。
固化好以后,掉电程序也不会丢失了!
与设计需求吻合,设计完成!