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"
相关推荐
DS小龙哥1 小时前
基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
fpga开发·sd nand·雷龙·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
上理考研周导师10 小时前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战12 小时前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩1 天前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技1 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪1 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望1 天前
EDA技术简介
fpga开发
淘晶驰AK1 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发
最好有梦想~1 天前
FPGA时序分析和约束学习笔记(4、IO传输模型)
笔记·学习·fpga开发