目录
Vivado 下按键控制 LED 实验
1、简介
2、实验环境
3、实验任务
4、硬件设计
5、程序设计
5.1、按键控制 led 模块代码
5.2、Vivado 仿真验证
5.2.1、Testbench 模块代码如下:
5.2.2、Vivado 仿真验证
6、下载验证
6.1、添加约束文件.xdc
6.2、板上下载验证
注意:一定要先把下载器的一端连接到了电脑、另一端连接了 JTAG 接口之后,再给开发板上电!否则,对开发板的 JTAG 接口进行带电热插拔,有一定概率会损坏 JTAG 接口!!!
按键是常用的一种控制器件。生活中我们可以见到各种形式的按键,由于其结构简单,成本低廉等特点,在家电、数码产品、玩具等方面有广泛的应用。本章我们将介绍如何使用按键控制多个 LED 的亮灭。
按键开关是一种电子开关,属于电子元器件类。我们的开发板上有两种按键开关:第一种是本实验所使用的轻触式按键开关(如下图1.1 ),简称轻触开关。使用时以向开关的操作方向施加压力使内部电路闭合接通,当撤销压力时开关断开,其内部结构是靠金属弹片受力后发生形变来实现通断的;第二种是自锁按键(如下图1.2 ),自锁按键第一次按下后保持接通,即自锁,第二次按下后,开关断开,同时开关按钮弹出来,开发板上的电源键就是这种开关。
使用 Xinlinx 黑金 FPGA 开发板上的四个按键控制四个 LED 灯。不同按键按下时,四个 LED 灯显示不同效果。
如图 3.1 所示,本实验使用四个按键开关控制四个 LED 灯。
如上图所示,开发板上的 4 个按键未按下时,输出高电平,按下后,输出低电平。
我们程序设计最终实现的效果为:无按键按下时,LED 灯全灭;按键 1 按下时,LED 灯显示自右向左的流水效果;按键 2 按下时,LED 灯显示自左向右的流水效果;按键 3 按下时,四个 LED 灯同时闪烁;按键 4 按下时,LED 灯全亮。
LED 在流水效果和闪烁效果在时间间隔均为 0.2 秒,因此需要在程序中定义一个 0.2s 的计数器,即每隔 0.2s,状态计数器加一。根据当前按键的状态选择不同的显示模式,不同的显示模式下四个 led 灯的亮灭随状态计数器的值改变,从而呈现出不同的显示效果。
按键控制 led 模块的代码如下所示:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/05/10 20:25:57
// Design Name:
// Module Name: key_led
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//实验任务:按键控制LED灯
//使用开发板上的四个按键控制四个 LED 灯。不同按键按下时,四个 LED 灯显示不同效果。
module key_led(
// input sys_clk, //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
//differential system clocks //200MHz系统时钟(一个周期是5ns:1/200MHz=0.005us=5ns)
input sys_clk_p, // Differential input clock 200Mhz
input sys_clk_n, // Differential input clock 200Mhz
input sys_rst_n, //reset ,low active //系统复位,低电平有效
input [3:0] key, //按键输入信号
output reg [3:0] led //LED输出信号
);
//define the time counter
reg [26:0] cnt;
reg [1:0] led_control;
//*********差分时钟这么处理***START******************
wire sys_clk;
//差分输入时钟缓冲器-黑金FPGA
IBUFDS sys_clk_ibufgds //generate single end clock
(
.O (sys_clk ), //Differential clock converted to single terminal clock
.I (sys_clk_p ),
.IB (sys_clk_n )
);
//*********差分时钟这么处理*****END*******************
//用于计数0.2s的计数器.
//(1/50MHz=0.02us=20ns)(0.2s需计数:0.2s/20ns=10_000_000次)
//(1/200MHz=0.005us=5ns)(0.2s需计数:0.2s/5ns=40_000_000次)
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
cnt <= 26'd0;
//else if (cnt < 26'd9_999_999)
else if (cnt < 26'd39_999_999)
//else if (cnt < 26'd9) //仅用于仿真
cnt <= cnt + 1;
else
cnt <= 0;
end
//用于led灯状态的选择
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
led_control <= 2'b00;
else if (cnt == 26'd39_999_999)
//else if (cnt == 26'd9) //仅用于仿真
led_control <= led_control + 1'b1;
else
led_control <= led_control;
end
//识别按键,切换显示模式
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
led <= 4'b0000;
end
else if (key[0]==0) //按键1 按下时,从右向左的流水灯效果
case(led_control)
2'b00 : led <= 4'b1000;
2'b01 : led <= 4'b0100;
2'b10 : led <= 4'b0010;
2'b11 : led <= 4'b0001;
default : led <= 4'b0000;
endcase
else if (key[1]==0) //按键2 按下时,从左向右的流水灯效果
case(led_control)
2'b00 : led <= 4'b0001;
2'b01 : led <= 4'b0010;
2'b10 : led <= 4'b0100;
2'b11 : led <= 4'b1000;
default :led <= 4'b0000;
endcase
else if (key[2]==0) //按键3 按下时,LED灯闪烁
case(led_control)
2'b00 : led <= 4'b1111;
2'b01 : led <= 4'b0000;
2'b10 : led <= 4'b1111;
2'b11 : led <= 4'b0000;
default :led <= 4'b0000;
endcase
else if (key[3]==0) //按键4 按下时,LED灯全亮
led <= 4'b1111;
else
led <= 4'b0000; //无按键按下时,LED全熄灭
end
endmodule
代码主要分为三个部分,第 49 至 61 行对系统时钟计数,当计数时间达 0.2s 时,计数器清零,同时使led_control 在四个状态(00,01,10,11)内依次变化。第 74 至 107 行利用 case 语句实现对按键状态的检测,当不同的按键按下时,led 随着 led_control 的变化,被赋予不同的值。仿真时我们设为每 10 个时钟周期,进行一次状态转换一次,如代码第 57 和 68 行。
大家可以发现,本次实验和流水灯实验计数时间都是0.2s,本次实验的计数器最大可以计数到39_999_999,而流水灯实验中计数器的值最大可以计数到 40_000_000。事实上,这两个实验计数器都是从 0 开始计数的,
本次实验从 0 计数到 39_999_999,需要 40_000_000 个时钟周期,而系统时钟为 5ns,所以计数的时间为 0.2s, 而流水灯实验从 0 计数到 40_000_000 需要 40_000_001 个时钟周期,因此其计数时间实际上比 0.2s 要多出 5ns。
为了验证我们的程序,我们使用 Vivado 对代码进行仿真。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/05/10 21:08:42
// Design Name:
// Module Name: tb_key_led
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//实验效果:无按键按下时,LED 灯全灭;按键 1 按下时,LED 灯显示自右向左的流水效果;
//按键 2 按下时,LED 灯显示自左向右的流水效果;
//按键 3 按下时,四个 LED 灯同时闪烁;按键 4 按下时,LED 灯全亮。
module tb_key_led(); //测试模块
//parameter T = 20; //时钟周期为20ns
parameter T = 5; //时钟周期为5ns
// Inputs
reg [3:0] key; //按键信号
//reg sys_clk; //时钟信号
reg sys_clk_p; //时钟信号
wire sys_clk_n;
reg sys_rst_n; //复位信号
// Outputs
wire [3:0] led;
//***************************************
//** main code
//***************************************
initial begin
key <=4'b1111; //按键初始状态全为断开
// sys_clk <=1'b0; //初始时钟为低电平
sys_clk_p <=1'b0; //初始时钟为低电平
sys_rst_n <=1'b0; //复位信号初始为低电平
#50 sys_rst_n <=1'b1; //50个时间单位后后复位信号拉高
key[0] <=0; //50ns时按下按键1
#160 key[0] <=1;
key[1] <=0; //800ns后松开按键1,按下按键2
#160 key[1] <=1;
key[2] <=0; //800ns后松开按键2,按下按键3
#160 key[2] <=1;
key[3] <=0; //800ns后松开按键3,按下按键4
#160 key[3] <=1; //800ns后松开按键4
end
//always #(T/2) sys_clk <= ~sys_clk; //每半个周期后,电平取反一次。
always #(T/2) sys_clk_p <= ~sys_clk_p; //每半个周期后,电平取反一次。
assign sys_clk_n=~sys_clk_p;
//实例化key_led模块
key_led u_key_led(
// .sys_clk (sys_clk),
.sys_clk_p (sys_clk_p),
.sys_clk_n (sys_clk_n),
.sys_rst_n (sys_rst_n),
.key (key),
.led (led)
);
endmodule
仿真波形如下:
观察代码,结合波形分析可知。43 至 49 行代码为对时钟信号、复位信号、按键信号赋初始值,默认为按键全断开。在第一个周期按下按键 key0(kye[0]由高电平变为低电平),可观察到 led3 至 led0 依次点亮,呈现自右向左的流水效果;按键 key0 断开的同时按下按键 key1,可观察到 led0 至 led3 依次点亮,呈现自左向右的流水效果;按键 key1 断开的同时按下按键 key2,可观察到 led0 至 led3 呈现闪烁效果;按键 key2 断开的同时按下按键 key3,可观察到 led0 至 led3 保持全亮。
添加约束文件 key_led.xdc,添加约束文件.xdc 的详细步骤见: Vivado 下 LED 流水灯实验_OliverH-yishuihan的博客-CSDN博客 中的 “4.3、添加 XDC管脚约束文件”
约束文件 key_led.xdc 的具体内容如下:
############## clock define 时钟引脚、电平信号约束#####黑金-FPGA##################
create_clock -period 5.000 [get_ports sys_clk_p]
set_property PACKAGE_PIN R4 [get_ports sys_clk_p]
set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_p]
############## reset key define##########################
set_property -dict {PACKAGE_PIN F15 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
############## LED define ############################
set_property -dict {PACKAGE_PIN L13 IOSTANDARD LVCMOS15} [get_ports {led[0]}]
set_property -dict {PACKAGE_PIN M13 IOSTANDARD LVCMOS15} [get_ports {led[1]}]
set_property -dict {PACKAGE_PIN K14 IOSTANDARD LVCMOS15} [get_ports {led[2]}]
set_property -dict {PACKAGE_PIN K13 IOSTANDARD LVCMOS15} [get_ports {led[3]}]
############## key define ############################
set_property -dict {PACKAGE_PIN L19 IOSTANDARD LVCMOS15} [get_ports {key[0]}]
set_property -dict {PACKAGE_PIN L20 IOSTANDARD LVCMOS15} [get_ports {key[1]}]
set_property -dict {PACKAGE_PIN K17 IOSTANDARD LVCMOS15} [get_ports {key[2]}]
set_property -dict {PACKAGE_PIN J17 IOSTANDARD LVCMOS15} [get_ports {key[3]}]
############ SPI Configurate Setting####固化后,重新一上电就开始工作##############
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
编译工程并生成比特流.bit 文件后,接下来我们下载比特流.bit 文件,验证按键控制 LED 灯的功能。程序下载完成后,我们按 KEY0、KEY1、KEY2 和 KEY3,就可以看到按键对应的实验现象了。
详细步骤见: Vivado 下 LED 流水灯实验_OliverH-yishuihan的博客-CSDN博客 中的 “4.6 下载和调试”、“4.7、FLASH 程序固化”