07 按键控制 LED

按键简介

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

弹出来。

硬件原理图

LED原理图

LED0 到 LED3 这 4 个发光二极管的阴极分别连到 S8050(NPN 三极管)的集电极上,阳极都与 3.3V 电压相连,三极管的基极分别与 FPGA 相连,这是由于 FPGA 的 IO 口的电压只有 1.5V,电压较低,所以此处连接三极管是为了起到放大电压的作用。这样就可以通过改变三极管的状态来控制 LED 的亮灭。当 FPGA 输出到为高电平时,三极管导通,LED 灯亮;当 FPGA 输出到为低电平时,三极管截止,LED 灯灭。

按键原理图

每个按键都连接了一个 1K 电阻(起到限流的作用,以防止按键被按下时电源直接接地造成电路短路),当按键未按下时,为高电平,按下后,为低电平。

程序功能

使用 4 个按键来控制 4 颗 LED 灯。没有按键被按下时,4 颗 LED 保持常灭;如果按键 KEY0 被按下, LED 灯从低位到高位流水;如果按键 KEY1 被按下,LED 灯从高位到低位流水;如果按键 KEY2 被按下,LED 灯交替闪烁;KEY3 被按下,LED 灯常亮。

系统框图

代码编写

c 复制代码
`timescale 1ns / 1ns	//仿真单位/仿真精度

module key_led #(
	//参数列表
	parameter COUNT_WIDTH = 25,				//内部计数器宽度
	parameter COUNT_PERIOD = 25_000_000		//计数器最大周期,决定LED多久变化依次
)
(
	input sys_clk,							//时钟
	input sys_rst_n,						//复位
	input [3:0] key,						//按键
	
	output reg [3:0] led					//led
);

//周期计数器,当计数到COUNT_PERIOD-1时LED进行输出状态切换
reg [COUNT_WIDTH-1:0] count;
//LED等控制标志
//流水灯模式时记录当前点亮的是那个LED
//闪烁模式时led_flag[0]用于亮灭控制
reg [1:0] led_flag;

//周期计数器,当计数到COUNT_PERIOD-1时LED进行输出状态切换
always @(posedge sys_clk) begin
	if(!sys_rst_n)
		count <= 0;
	else if(count < (COUNT_PERIOD - 1))
		count <= count+ 1;
	else
		count <= 0;
end

//LED等控制标志
//流水灯模式时记录当前点亮的是那个LED
//闪烁模式时led_flag[0]用于亮灭控制
always @(posedge sys_clk) begin
	if(!sys_rst_n)
		led_flag <= 2'h0;
	else if(count == (COUNT_PERIOD - 1))
		led_flag <= led_flag + 2'h1;
end

//根据按键状态和流水灯控制标志控制LED输出
always @(posedge sys_clk) begin
	if(!sys_rst_n)
		led <= 4'b0000;
	else if(key == 4'b1111)				//全灭
		led <= 4'b0000;
	else if(key == 4'b1110) begin		//从led0到led3流水
		if(led_flag == 2'd0)
			led <= 4'b0001;
		else if(led_flag == 2'd1)
			led <= 4'b0010;
		else if(led_flag == 2'd2)
			led <= 4'b0100;
		else
			led <= 4'b1000;
	end
	else if(key == 4'b1101) begin		//从led3到led0流水
		if(led_flag == 2'd0)
			led <= 4'b1000;
		else if(led_flag == 2'd1)
			led <= 4'b0100;
		else if(led_flag == 2'd2)
			led <= 4'b0010;
		else
			led <= 4'b0001;
	end
	else if(key == 4'b1011) begin		//闪烁
		if(led_flag[0] == 1'b0)
			led <= 4'b1111;
		else
			led <= 4'b0000;
	end
	else if(key == 4'b0111)				//全亮
		led <= 4'b1111;
end

endmodule

仿真验证激励程序

c 复制代码
`timescale 1ns/1ns //仿真的单位/仿真的精度

module tb_key_led();

reg sys_clk;					//时钟
reg sys_rst_n;					//复位
reg [3:0] key;					//按键

wire [3:0] led;					//led

initial begin
	sys_clk = 1'b0;
	sys_rst_n = 1'b0;
	key <= 4'b1111;
	#180
	sys_rst_n = 1'b1;
	
	#2000
	key <= 4'b1110; //按下 KEY0
	#2000
	key <= 4'b1111; //释放 KEY0
	
	#2000
	key <= 4'b1101; //按下 KEY1
	#2000
	key <= 4'b1111; //释放 KEY1
	
	#2000
	key <= 4'b1011; //按下 KEY2
	#2000
	key <= 4'b1111; //释放 KEY2
	
	#2000
	key <= 4'b0111; //按下 KEY3
	#2000
	key <= 4'b1111; //释放 KEY3
end

always #10 sys_clk = ~sys_clk;

key_led #(
	//参数列表
	.COUNT_WIDTH(25),
	.COUNT_PERIOD(25)
)
tb_key_led_inst(
	.sys_clk(sys_clk),				//时钟
	.sys_rst_n(sys_rst_n),			//复位
	.key(key),						//按键
	
	.led(led)						//led
);
endmodule

时序约束

时序约束(Timing Constraints)用来描述设计人员对时序的要求,比如时钟频率,输入输出的延时等,以满足设计的时序要求。对时序约束最简单的理解就是,设计者告诉 EDA 工具设计中所使用的时钟信号的参数(如频率等),然后 EDA 工具按照所要求的时钟参数去优化布局布线,使设计能够在要求的时钟下正常工作

一般情况下对于简单的设计,即使不对工程做时序约束,也不影响最终的功能。但是当设计变得复杂起来,或者输入的时钟频率比较高的时候,如果不添加时序约束,那么就有可能导致功能异常。

添加时序约束

  1. 对时钟进行约束前,需要先对代码进行综合,即点击 Vivado 左侧"Flow Navigator"窗口下"Run Synthesis"按钮来对代码进行综合

    在弹出的窗口中我们直接点击"OK"

  2. 综合完成后,在弹出的"Synthesis Completed"窗口中我们直接点击"X"即可

  3. 点击 Vivado 左侧"Flow Navigator"窗口下的"Edit Timing Constraints"按钮了

  4. 点击"Edit Timing Constraints"按钮后,Vivado 就会打开"Timing Constraints"界面,我们点击该界面下的"+"号按键就可以添加时钟约束了

  5. 点击"+"号后,弹出的"Create Clock"界面

    "Create Clock"界面中各参数定义如下:

    Clock name:时钟名称,用于为所创建的时钟约束命名。为了可以一眼看出该时钟约束的约束源,通常情况下其命名与被约束的时钟信号名相同

    Source objects:源对象,用于指定被约束的时钟对象

    Waveform:波形,用于设置时钟的周期(Period),上升沿(Rise at)、下降沿(Fall at)以及勾选 Add this clock to the existing clock(是否将该时钟添加到现有时钟中)

    Command:命令,即通过我们上述的配置后,vivado 自动生成的约束命令,该命令是可以直接复制粘贴到 XDC 文件中使用的

  6. 点击"Source objects"框后的"..."指定时钟源对象

  7. 点击"..."后弹出"Specify Clock Source Objects",在"Specify Clock Source Objects"界面中"Find"之上的各个选项主要用于设置筛选条件,因为系统时钟来自于 IO 口,所以"Find names of type(寻找名称类型)"选择"I/O Port",其余选项保持默认。

  8. 在找到的信号中,我们选择需要约束的系统时钟信号(sys_clk)然后点击"→"按钮将其添加到右侧窗口。

  9. 之后点击"Set"即可将该信号定义为源对象

  10. 点击 "Create Clock" 的"OK"即可添加约束并关闭"Create Clock"界面

  11. 此时的 "Timing Constraints"界面如下

  12. 点击 Vivado 软件左上方的保存图标来将其保存到 XDC 文件中,如果弹出"Out of Date Design"则点击 "OK"

  13. 在弹出的"Save Constraints"界面中会默认保存到现有的 XDC 文件中,所以直接点击"OK"

管脚约束

管脚约束方法1:

直接在约束文件中增加如下内容,用于约束IO引脚

c 复制代码
#IO 引脚约束
#------------------------------系统时钟和复位-----------------------------------
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports sys_rst_n]
#----------------------------------按键-----------------------------------------
set_property -dict {PACKAGE_PIN T4 IOSTANDARD LVCMOS15} [get_ports {key[0]}]
set_property -dict {PACKAGE_PIN T3 IOSTANDARD LVCMOS15} [get_ports {key[1]}]
set_property -dict {PACKAGE_PIN R6 IOSTANDARD LVCMOS15} [get_ports {key[2]}]
set_property -dict {PACKAGE_PIN T6 IOSTANDARD LVCMOS15} [get_ports {key[3]}]
#-----------------------------------LED-----------------------------------------
set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS15} [get_ports {led[0]}]
set_property -dict {PACKAGE_PIN Y8 IOSTANDARD LVCMOS15} [get_ports {led[1]}]
set_property -dict {PACKAGE_PIN Y7 IOSTANDARD LVCMOS15} [get_ports {led[2]}]
set_property -dict {PACKAGE_PIN W7 IOSTANDARD LVCMOS15} [get_ports {led[3]}]

管脚约束方法2:

  1. 需要先对代码进行综合,即点击 Vivado 左侧"Flow Navigator"窗口下"Run Synthesis"按钮来对代码进行综合
  2. 综合完成后选择 Vivado 左侧"Flow Navigator"窗口下"SYNTHESIS"
  3. 在右上角的窗口布局中选择"I/O Planning"
  4. 选中下面的"I/O Ports"页面

    Name:工程中顶层端口的名称。
    Direction:说明管脚是输入还是输出。
    Neg Diff Pair:负差分对,差分信号在 I/O Ports 窗口中只显示在一行里(只会显示 P 端信号,N 端信号显示在 Neg Diff Pair 属性栏中)。
    Package Pin:配置管脚封装。
    Fixed:每一个端口都有 Fixed 属性,表明该逻辑端口是由用户赋值的。端口必须保持锁定状态,才能避免生成比特流时不会发生错误。
    Bank:显示管脚所在的 Bank。
    I/O Std:配置管脚的电平标准,常用电平标准有 LVTTL 和 LVCMOS、SSTL、LVDS 与 HSTL 等。
    Vcco:选择的管脚的电压值。
    Vref:在我们的设计中,硬件上 VREF 引脚悬空。
    Drive Strength:驱动强度,默认 12mA。
    Slew Type:指上升下降沿的快慢,设置快功耗会高一点,默认设置慢(slow)。
    Pull Type:管脚上下拉设置,有上拉、下拉、保持与不设置。
    Off-Chip Termination:终端阻抗,默认 50Ω。
    IN-TERM:是用于 input 的串联电阻。
    提示
    如果页面被不小心关掉了可以选择右上角的"Reset Layout"
  5. 在I/O Ports中根据原理图完成引脚约束
  6. 点击 Vivado 软件左上方的保存图标来将其保存到 XDC 文件中,如果弹出"Out of Date Design"则点击 "OK"

  7. 在弹出的"Save Constraints"界面中会默认保存到现有的 XDC 文件中,所以直接点击"OK"
相关推荐
fei_sun16 小时前
【Verilog】第一章作业
fpga开发·verilog
深圳市雷龙发展有限公司longsto17 小时前
基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面
fpga开发
9527华安21 小时前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
able陈21 小时前
为什么verilog中递归函数需要定义为automatic?
fpga开发
fei_sun1 天前
【Verilog】第二章作业
fpga开发·verilog
碎碎思1 天前
如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目
fpga开发·接口隔离原则
江山如画,佳人北望1 天前
fpga-状态机的设计及应用
fpga开发
晓晓暮雨潇潇1 天前
Xilinx IP核(3)XADC IP核
fpga开发·vivado·xadc·ip核
CWNULT1 天前
AMD(Xilinx) FPGA配置Flash大小选择
fpga开发
碎碎思2 天前
很能体现FPGA硬件思维的一道面试题
fpga开发