按键开关是一种电子开关,属于电子元器件类。常见的按键开关有两种,第一种是轻触式按键开关(简称轻触开关),使用时以向开关的操作方向施加压力使内部电路闭合接通,当撤销压力时开关断开,其内部结构是靠金属弹片受力后发生形变来实现通断的;第二种是自锁按键,自锁按键第一次按下后保持接通,即自锁,第二次按下后,开关断开,同时开关按钮
弹出来。
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 灯常亮。
`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
`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 工具按照所要求的时钟参数去优化布局布线,使设计能够在要求的时钟下正常工作
一般情况下对于简单的设计,即使不对工程做时序约束,也不影响最终的功能。但是当设计变得复杂起来,或者输入的时钟频率比较高的时候,如果不添加时序约束,那么就有可能导致功能异常。
对时钟进行约束前,需要先对代码进行综合,即点击 Vivado 左侧“Flow Navigator”窗口下“Run Synthesis”按钮来对代码进行综合
在弹出的窗口中我们直接点击“OK”
点击 Vivado 左侧“Flow Navigator”窗口下的“Edit Timing Constraints”按钮了
点击“Edit Timing Constraints”按钮后,Vivado 就会打开“Timing Constraints”界面,我们点击该界面下的“+”号按键就可以添加时钟约束了
点击“+”号后,弹出的“Create Clock”界面
“Create Clock”界面中各参数定义如下:
Clock name:时钟名称,用于为所创建的时钟约束命名。为了可以一眼看出该时钟约束的约束源,通常情况下其命名与被约束的时钟信号名相同
Source objects:源对象,用于指定被约束的时钟对象
Waveform:波形,用于设置时钟的周期(Period),上升沿(Rise at)、下降沿(Fall at)以及勾选 Add this clock to the existing clock(是否将该时钟添加到现有时钟中)
Command:命令,即通过我们上述的配置后,vivado 自动生成的约束命令,该命令是可以直接复制粘贴到 XDC 文件中使用的
点击“…”后弹出“Specify Clock Source Objects”,在“Specify Clock Source Objects”界面中“Find”之上的各个选项主要用于设置筛选条件,因为系统时钟来自于 IO 口,所以“Find names of type(寻找名称类型)”选择“I/O Port”,其余选项保持默认。
点击 Vivado 软件左上方的保存图标来将其保存到 XDC 文件中,如果弹出“Out of Date Design”则点击 “OK”
管脚约束方法1:
直接在约束文件中增加如下内容,用于约束IO引脚
#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: