基于Smart Artix 的FPGA实验三 基于FPGA的按键功能演示(IO输入功能)

本章介绍如果读取按键的操作,来演示FPGA的IO输入功能

  • 此章节内容适用于Smart Artix 的主板 ,如是本站其他板子请看对应板子目录
  • 本文在 vivado2018.3版本上演示

一、硬件介绍

先看原理图,由原理图可以看出板子上接了三个按键(其中一个是复位RESET_n)本例使用KEY1和KEY2,并且默认是通过上拉电阻上拉到3.3V的,也就是当按键没有按下的时候 按键KEY 的信号脚是3.3V ,当按键按下后,该信号脚被拉低到GND也就是0V 。

为了演示两个按键的功能,这里再引入两个指示灯从下面电路图可以看出两个个LED灯的阴极接的GND, 则FPGA接LED的管脚输出为高电平LED灯点亮,FPGA接LED的管脚输出为低电平 LED 熄灭。

二、工程创建

工程创建的过程可以参考实验一中的内容,这里不详细描述了。基于Smart Artix 的FPGA实验一 用FPGA资源点亮一个LED(完整图文) (芯片型号选XC7A50TFGG484-2)

三、代码的编写

正常的按键操作,在按下瞬间 电平会产生抖动,虽说只是一个按下的操作,但是实际可能电压跳变了无数次,所以正常的按键代码是需要消抖的,消抖会稍微麻烦点 这里放到本文的最后再介绍。

1 )不带消抖的程序演示

为了方便理解输入输出的概念这里先简单写一个不带消抖的按键,去控制LED灯的程序(一般芯片间的通讯是不需要做消抖功能的,只有按键才需要做消抖)

首先 原先 LED 的驱动程序,我们用的是output ,output代表输出,所以这里我们的按键要设置成input ,代表输入。

完整代码比较简单 就不详细描述了,详细见下面

程序是通过时钟信号同步的时序逻辑的方式来运行,其中LED灯的变化 和 KEY的变化仅在clk 的上升沿完成,受时钟影响。

`timescale 1ns / 1ps
module KEY_TEST(
input CLK,
input RST_n,
output LED1,
output LED2,
input KEY1,
input KEY2
);

reg LED1_r=0;
reg LED2_r=0;

always@(posedge CLK or negedge RST_n)begin
if(!RST_n)begin
LED1_r <= 0;
LED2_r <= 0;
end
else begin
if(KEY1==0) LED1_r <= 1'b1;
else LED1_r <= 1'b0;

if(KEY2==0) LED2_r <= 1'b1;
else LED2_r <= 1'b0;
end
end

assign LED1 = LED1_r;
assign LED2 = LED2_r;

endmodule

约束文件如下(包含管脚约束和时钟约束):

# Clock Constraints
set_property PACKAGE_PIN Y18 [get_ports CLK]
set_property IOSTANDARD LVCMOS33 [get_ports CLK]

# Reset Constraints (active-low reset)
set_property PACKAGE_PIN T20 [get_ports RSTn]
set_property IOSTANDARD LVCMOS33 [get_ports RSTn]

# LED Constraints
set_property PACKAGE_PIN R17 [get_ports LED1]
set_property IOSTANDARD LVCMOS33 [get_ports LED1]
set_property PACKAGE_PIN P16 [get_ports LED2]
set_property IOSTANDARD LVCMOS33 [get_ports LED2]

# KEY Constraints
set_property PACKAGE_PIN E22 [get_ports KEY1]
set_property IOSTANDARD LVCMOS33 [get_ports KEY1]
set_property PACKAGE_PIN G22 [get_ports KEY2]
set_property IOSTANDARD LVCMOS33 [get_ports KEY2]

# Define the clock period for 50 MHz
create_clock -period 20.000 -name CLK -waveform {0.000 10.000} [get_ports CLK]

保存后进行编译和综合,最后下载到板子上进行运行,可以看到当按下左边的按键后,左边的灯被点亮,当按下右边的按键后,右边的灯被点亮了

到这里简单的输入功能介绍完毕了。

按键控制也可以是组合逻辑 ,LED灯的状态仅和按键有关和时钟等没有关系,这种方式就相当于FPGA内部拉了个导线并串了个非门到LED和按键之间

LED1=(KEY1==0)?1’b1:1’b0;

这种方式 也不需要创建LED对应的 REG寄存器(LED_r)缺点是各个信号间不同步 不适用于大型系统中

2)带消抖的程序演示

以上介绍的方法不带按键消抖,适用于不同芯片间传输,和简单的按键应用, 如果要让系统中按键的稳定性更大,那需要在系统中额外增加按键消抖功能

按键消抖的原由:通常在一个按键开关在闭合时由于机械特性的原因,按键不会立马接通,在断开时也不会一下子断开,而是在闭合及断开的瞬间均伴随有多次抖动,抖动的产生会对按键的信号本身产生干扰。

一般正常按键 抖动过程是小于20ms的, 而正常的一次按键操作,即使是短按时间也都是超过50ms的。 所以我们的按键消抖时间可以以20ms作为界限。

按键消抖的思路

  • a.初始状态 计数器为0,跳转到状态b
  • b.检测到按键按下(即电平为0)计数器开始计时 ,跳转到状态c
  • c.计数器计数到20ms前不停的检测,检测到电压值不为0,即认为是抖动信号,则系统回到a初始状态,如果20ms内电压值时钟为0,则认为按键有效,跳转到状态d
  • d.按键有效信号置位 , 当检测到按键电平不为0时, 按键有效信号清0
  • (以上是判断按键按下的消抖处理思路, 如果释放按键不参与决策,则不需要做按键释放消抖,否则 按键释放也需要做消抖处理)。

接下来开始写消抖的程序:

`timescale 1ns / 1ps
module KEY_TEST(
input CLK,
input RSTn,
output LED,
input KEY
);

reg [1:0]debounce_mode=2'd0;
reg [19:0]debounce_count=20'd0;

reg [1:0]KEY_r=0;
always@(posedge CLK or negedge RSTn)begin
if(!RSTn)begin
KEY_r[1] <= 0;
KEY_r[0] <= 0;
end
else begin
KEY_r[1] <= KEY_r[0];
KEY_r[0] <= KEY;
end
end
wire KEY_NEGEDGE = (KEY_r[1] & ~KEY_r[0]) ? 1'b1 : 1'b0;

reg KEY_value = 0;
always@(posedge CLK or negedge RSTn)begin
if(!RSTn)begin
debounce_count <= 20'd0;
debounce_mode <= 2'd0;
KEY_value <= 0;
end
else if(debounce_mode == 0)begin
debounce_count <= 20'd0;
debounce_mode <= 2'd1;
KEY_value <= 0;
end
else if(debounce_mode == 1)begin
if(KEY_NEGEDGE == 1)begin
debounce_count <= 20'd0;
debounce_mode <= 2'd2;
end
end
else if(debounce_mode == 2)begin
if(debounce_count >= 20'd1_000_000) debounce_mode <= 2'd3;
else begin
if(KEY == 1) debounce_mode <= 2'd0;
debounce_count <= debounce_count + 1'b1;
end
end
else begin
if(KEY == 1) debounce_mode <= 2'd0;
KEY_value <= 1'b1;
end
end

assign LED = KEY_value;

endmodule

代码简单解读

代码里用了两位寄存器来移位KEY的按键状态,然后通过对比前后的按键变化来找到按键的下降沿(上个时刻是高电平,下个时刻为低电平,代表是下降沿,即(KEY_r[1]&~KEY_r[0])),当系统为下降沿的那个时刻KEY_NEGEDGE输出高电平。(下面代码省去了复位部分)

reg [1:0]KEY_r=0;
always@(posedge CLK)begin
KEY_r[1]<= KEY_r[0];
KEY_r[0]<= KEY;
end
wire KEY_NEGEDGE=(KEY_r[1]&~KEY_r[0])?1'b1:1'b0;

debounce_mode代表当前的状态机所处的状态,其中的0,1,2,3分别代表前面写的按键消抖思路的a,b,c,d4个过程步骤,这里可以对应着看。 0代表初始化,1模式检测下降沿,如果出现下降沿进入到模式2,2开始计数并检测按键状态是否变成1,如果是则认为触发的是抖动干扰,则重新回到模式0等待下一个下降沿,如果计数器超过20ms按键时钟没变化,则认为按键有效进入模式3,模式3下改变按键的输出值KEY_value。

debounce_count是一个20位的计数器,从按键的下降沿开始 计数,直到20ms停止(20ms 对应50mhz时钟的第1000000个脉冲)

KEY_value代表最终消抖后的按键值,KEY_value=1代表按键有效 并且一直持续。 这里将按键的结果通过assign LED=KEY_value; 将KEY_value寄存器的值直接映射到LED指示灯上,可以更好的看到按键按下的结果。

增加约束文件:


# Clock Constraints
set_property PACKAGE_PIN Y18 [get_ports CLK]
set_property IOSTANDARD LVCMOS33 [get_ports CLK]

# Reset Constraints (active-low reset)
set_property PACKAGE_PIN T20 [get_ports RSTn]
set_property IOSTANDARD LVCMOS33 [get_ports RSTn]

# LED Constraints
set_property PACKAGE_PIN R17 [get_ports LED]
set_property IOSTANDARD LVCMOS33 [get_ports LED]

# KEY Constraints
set_property PACKAGE_PIN E22 [get_ports KEY]
set_property IOSTANDARD LVCMOS33 [get_ports KEY]

# Define the clock period for 50 MHz
create_clock -period 20.000 -name CLK -waveform {0.000 10.000} [get_ports CLK]

这里只演示一个按键,如果要做多个按键,可以用模块例化的方式 设计多个按键,这里请自行尝试。如果需要增加按键释放消抖,原理上相通,可自行研究。

本章节的工程:

  • 本文的完整工程下载:03_KEY_TEST(Smart Artix 50T)
  • VIVADO的版本:2018.3
  • 工程创建目录: E:\Smart_Artix\4_Code\03_KEY_TEST

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注