工程1完整的介绍了一个工程从创建到编译综合,最终到程序运行的全过程,本工程在工程一的基础上进一步深化,以流水灯的形式分时点亮两个LED灯。
- 此章节内容适用于Lemon ZYNQ主板,如是其他板子请看对应板子目录。
- 本文在 vivado2018.3版本上演示。
一、原理图分析
LED灯:从下面电路图可以看出四个个LED灯的阴极接的GND, 则FPGA接LED的管脚输出为高电平,LED灯点亮,FPGA接LED的管脚输出为低电平 LED 熄灭。
按键:板子上有四个按键,我们用板子上四个按键的其中一个(BTN3)充当复位功能(一般正常的工程都需要一个复位键,也可以像工程一那样没有复位键) 注意,这里的按键不按情况下为0,按下后为1。
二、工程创建
工程创建的过程可以参考试验一中的内容,这里不详细描述了。
三、程序编写
LED的驱动方式有很多种
1)直驱方式,比较简单 如下
module LED(
output LED1,
output LED2
);
assign LED1=1'b0;
assign LED2=1'b1;
endmodule
这种方式相当于把LED的输出口直接通过assign 方式内部连接到 VCC,和GND上, 如上面代码中LED1连接到了GND,处于熄灭状态,LED2连接到了内部的VCC上 处于点亮状态
这个代码比较简单 所以不作展开
2) 接下来介绍一种用计数器来分别点亮两个LED灯来实现 流水灯的效果
由于我们板子上焊接的是一个125Mhz的晶振,所以每秒钟晶体振荡 125_000_000次,用计算器看125000000换算成二进制共占用27位,所以这里我们定义一个27bit 的寄存器作计数器用
reg [26:0]time_count; //[26:0]代表27bit
1秒钟计数器模块: 接下来是编写一个完成的1秒钟计数器模块
always@(posedge clk or posedge rst)
if(rst)begin//当复位条件下,计数器赋初值0
time_count<=27'd0;
end
else begin//当不在复位条件下
if(time_count>=T1MS-1'b1)//如果计数器达到 50_000_000则代表一秒钟计数完成
time_count<=27'd0;
else time_count<=time_count+1'b1;//其他情况下计数器自增
end
其中 parameter T1MS = 27’d125_000_000 ; 为定义一个常量,前面所说125M晶振下,1秒钟晶体震荡125_000_000次
always@(posedge clk or posedge rst_n) 代表模块进入的两个条件,1是系统时钟的上升沿(posedge),另一种是复位信号的上升沿(posedge ),也就当系统复位信号按下瞬间或时钟每次上升沿进入并执行always@块中的内容
复位条件下 if(rst_n) 系统对计数器模块进行初始化0的操作
当系统不处于初始化情况,每个时钟上升沿 time_count 都自增1,当 time_count >= T1MS -1’b1时,即计数达到1秒钟的时间的时候, time_count 再次清零
LED灯切换模块
reg [3:0]led_state;
always@(posedge clk or posedge rst)
if(rst)begin//当复位条件下,计数器赋初值0
led_state<=4'd0;
end
else begin
if(time_count==T1MS-1'b1)begin//一秒钟
if(led_state>=4'd3)led_state<=4'd0;//从0-2反复循环
else led_state<=led_state+1'b1;//自增
end
end
assign led1= (led_state==4'd0)?1'b1:1'b0;
assign led2= (led_state==4'd1)?1'b1:1'b0;
assign led3= (led_state==4'd2)?1'b1:1'b0;
assign led4= (led_state==4'd3)?1'b1:1'b0;
这里定义了一个2bit的led_state寄存器,用来控制当前灯的显示状态,从程序上可以看出,每当 time_count==T1MS-1’b1 时, led_state 在0-1-2-3反复循环自增(如果有多个灯,可以在这里进行修改,比方说5个灯可以修改成 0-1-2-3-4-5, 多个灯的情况下需要对led_state位宽进行修改)。
led_state从0-3反复循环,每一种状态对应一个LED灯的显示,如 led_state =0时,LED1亮,当 led_state =1时LED2亮,依次类推。
工程完整代码如下
`timescale 1ns / 1ps
module LED(
input clk,//时钟
input rst,//复位
output led1,
output led2,
output led3,
output led4
);
parameter T1MS = 27'd125_000_000 ; //50M晶振时钟
reg [26:0]time_count;
always@(posedge clk or posedge rst)
if(rst)begin//当复位条件下,计数器赋初值0
time_count<=27'd0;
end
else begin//当不在复位条件下
if(time_count>=T1MS-1'b1)//如果计数器达到 50_000_000则代表一秒钟计数完成
time_count<=27'd0;
else time_count<=time_count+1'b1;//其他情况下计数器自增
end
reg [3:0]led_state;
always@(posedge clk or posedge rst)
if(rst)begin//当复位条件下,计数器赋初值0
led_state<=4'd0;
end
else begin
if(time_count==T1MS-1'b1)begin//一秒钟
if(led_state>=4'd3)led_state<=4'd0;//从0-2反复循环
else led_state<=led_state+1'b1;//自增
end
end
assign led1= (led_state==4'd0)?1'b1:1'b0;
assign led2= (led_state==4'd1)?1'b1:1'b0;
assign led3= (led_state==4'd2)?1'b1:1'b0;
assign led4= (led_state==4'd3)?1'b1:1'b0;
endmodule
三、管脚约束文件创建
前面的部分和第一个实验的创建大致相同,可以参考实验一,约束文件部分实验一通过图形界面设置自动生成, 本实验我们用不同的方式进行约束文件创建,具体操作如下:
1) 新建一个约束文件
2)在弹出的窗口依次点create file 并且在file name中填入约束文件的文件名 ,完成后点ok 和finish
3)如下图方式打开约束文件
4) 在约束文件中填入约束信息(管脚定义,管脚电压等)
set_property IOSTANDARD LVCMOS33 [get_ports led1]
set_property IOSTANDARD LVCMOS33 [get_ports led2]
set_property IOSTANDARD LVCMOS33 [get_ports led3]
set_property IOSTANDARD LVCMOS33 [get_ports led4]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst]
set_property PACKAGE_PIN R14 [get_ports led1]
set_property PACKAGE_PIN P14 [get_ports led2]
set_property PACKAGE_PIN N16 [get_ports led3]
set_property PACKAGE_PIN M14 [get_ports led4]
set_property PACKAGE_PIN H16 [get_ports clk]
set_property PACKAGE_PIN L19 [get_ports rst]
四、工程编译综合和下载调试
1)将程序进行编译综合
2)用TYPE C线连接JTAG 口将程序下载到板子上(步骤可参看实验一), 可以看到 转接板上四个个灯依次按一秒钟的间隔频率闪烁,并且按下BTN3(程序定义的复位键,用按键来替代)灯恢复到初始状态(备注 如果下载的时候bit文件没有出现 可以手动添加bit文件)。
- 本文的完整工程下载:02_PL_LED_WATER
- VIVADO的版本:2018.3
- 工程创建目录: E:\Lemon_ZYNQ\FPGA\02_PL_LED_WATER