目录

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 接口!!!


Vivado 下按键控制 LED 实验

       按键是常用的一种控制器件。生活中我们可以见到各种形式的按键,由于其结构简单,成本低廉等特点,在家电、数码产品、玩具等方面有广泛的应用。本章我们将介绍如何使用按键控制多个 LED 的亮灭。

1、简介

       按键开关是一种电子开关,属于电子元器件类。我们的开发板上有两种按键开关:第一种是本实验所使用的轻触式按键开关(如下图1.1 ),简称轻触开关。使用时以向开关的操作方向施加压力使内部电路闭合接通,当撤销压力时开关断开,其内部结构是靠金属弹片受力后发生形变来实现通断的;第二种是自锁按键(如下图1.2 ),自锁按键第一次按下后保持接通,即自锁,第二次按下后,开关断开,同时开关按钮弹出来,开发板上的电源键就是这种开关。

Vivado 下按键控制 LED 实验_fpga开发

2、实验环境

  • Windows 10  64 位
  • vivado 2020.2
  • Xinlinx 黑金 FPGA 开发板(AX7A035t 开发板、AX7A100t 开发板、AX7A200t 开发板)

3、实验任务

       使用 Xinlinx 黑金 FPGA 开发板上的四个按键控制四个 LED 灯。不同按键按下时,四个 LED 灯显示不同效果。

4、硬件设计

如图 3.1 所示,本实验使用四个按键开关控制四个 LED 灯。

Vivado 下按键控制 LED 实验_fpga开发_02

如上图所示,开发板上的 4 个按键未按下时,输出高电平,按下后,输出低电平。

5、程序设计

       我们程序设计最终实现的效果为:无按键按下时,LED 灯全灭;按键 1 按下时,LED 灯显示自右向左的流水效果;按键 2 按下时,LED 灯显示自左向右的流水效果;按键 3 按下时,四个 LED 灯同时闪烁;按键 4 按下时,LED 灯全亮。

       LED 在流水效果和闪烁效果在时间间隔均为 0.2 秒,因此需要在程序中定义一个 0.2s 的计数器,即每隔 0.2s,状态计数器加一。根据当前按键的状态选择不同的显示模式,不同的显示模式下四个 led 灯的亮灭随状态计数器的值改变,从而呈现出不同的显示效果。

Vivado 下按键控制 LED 实验_算法_03

5.1、按键控制 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。

5.2、Vivado 仿真验证

为了验证我们的程序,我们使用 Vivado 对代码进行仿真。

5.2.1、Testbench 模块代码如下:

`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

5.2.2、Vivado 仿真验证

Vivado 下按键控制 LED 实验_dsp开发_04

 仿真波形如下:

Vivado 下按键控制 LED 实验_算法_05

观察代码,结合波形分析可知。43 至 49 行代码为对时钟信号、复位信号、按键信号赋初始值,默认为按键全断开。在第一个周期按下按键 key0(kye[0]由高电平变为低电平),可观察到 led3 至 led0 依次点亮,呈现自右向左的流水效果;按键 key0 断开的同时按下按键 key1,可观察到 led0 至 led3 依次点亮,呈现自左向右的流水效果;按键 key1 断开的同时按下按键 key2,可观察到 led0 至 led3 呈现闪烁效果;按键 key2 断开的同时按下按键 key3,可观察到 led0 至 led3 保持全亮。

6、下载验证

6.1、添加约束文件.xdc

添加约束文件 key_led.xdc,添加约束文件.xdc 的详细步骤见: Vivado 下 LED 流水灯实验_OliverH-yishuihan的博客-博客 中的 “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]

6.2、板上下载验证

       编译工程并生成比特流.bit 文件后,接下来我们下载比特流.bit 文件,验证按键控制 LED 灯的功能。程序下载完成后,我们按 KEY0、KEY1、KEY2 和 KEY3,就可以看到按键对应的实验现象了。

详细步骤见: Vivado 下 LED 流水灯实验_OliverH-yishuihan的博客-博客 中的 “4.6 下载和调试”、“4.7、FLASH 程序固化” 

注意:一定要先把下载器的一端连接到了电脑、另一端连接了 JTAG 接口之后,再给开发板上电!否则,对开发板的 JTAG 接口进行带电热插拔,有一定概率会损坏 JTAG 接口!!!