基于Lemon ZYNQ的FPGA实验三 PL部分按键功能演示(IO输入功能)

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

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

一、硬件介绍

先看原理图,由原理图可以看出板子上接了四个按键,并且默认是通过下拉电阻下拉到GND的,也就是当按键没有按下的时候 按键KEY 的信号脚是0V ,当按键按下后,该信号脚被拉高到3.3V 。

为了演示按键的功能,这里再在工程中引入LED指示灯,指示灯之前章节的例子里测试过,将LED的驱动脚拉高,指示灯亮,拉低指示灯熄灭。

为了方便代码的演示,这里仅用两个按键,和两个LED进行测试

二、工程创建

工程创建的过程可以参考试验一中的内容,这里不详细描述了。

三、代码的编写

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

1 )不带消抖的程序演示

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

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

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

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

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

reg LED1_r=0;
reg LED2_r=0;

always@(posedge clk)begin
if(KEY1==1)LED1_r<=1'b1;
else LED1_r<=1'B0;

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

assign LED1=LED1_r;
assign LED2=LED2_r;

endmodule

约束文件如下

set_property PACKAGE_PIN R14 [get_ports LED1]
set_property PACKAGE_PIN P14 [get_ports LED2]
set_property PACKAGE_PIN D19 [get_ports KEY1]
set_property PACKAGE_PIN D20 [get_ports KEY2]
set_property PACKAGE_PIN H16 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports LED1]
set_property IOSTANDARD LVCMOS33 [get_ports LED2]
set_property IOSTANDARD LVCMOS33 [get_ports KEY1]
set_property IOSTANDARD LVCMOS33 [get_ports KEY2]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

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

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

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

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

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

2)带消抖的程序演示

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

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

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

按键消抖的思路

a.初始状态 计数器为0,跳转到状态b。

b.检测到按键按下(即电平为1)计数器开始计时 ,跳转到状态c。

c.计数器计数到20ms前不停的检测,检测到电压值不为1,即认为是抖动信号,则系统回到a初始状态,如果20ms内电压值时钟为1,则认为按键有效,跳转到状态d。

d.按键有效信号置位 , 当检测到按键电平不为1时, 按键有效信号清0。

(以上是判断按键按下的消抖处理思路, 如果释放按键不参与决策,则不需要做按键释放消抖,否则 按键释放也需要做消抖处理)。

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

/////////////////////
//www.hellofpga.com//
/////////////////////
`timescale 1ns / 1ps
module KEY_TEST(
input clk,
output LED,
input KEY
);

reg [1:0]debounce_mode=2'd0;
reg [21:0]debounce_count=22'd0;

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


reg KEY_value=0;
always@(posedge clk)begin
if(debounce_mode==0)begin
debounce_count<=22'd0;
debounce_mode<=2'd1;
KEY_value<=0;
end
else if(debounce_mode==1)begin
if(KEY_POSEDGE==1)begin
debounce_count<=22'd0;
debounce_mode<=2'd2;
end
end
else if(debounce_mode==2)begin
if(debounce_count>=22'd2_500_000)debounce_mode<=2'd3;
else begin
if(KEY==0)debounce_mode<=2'd0;
debounce_count<=debounce_count+1'b1;
end
end
else begin
if(KEY==0)debounce_mode<=2'd0;
KEY_value<=1'b1;
end
end

assign LED=KEY_value;

endmodule

代码简单解读

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

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

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

debounce_count是一个22位的计数器,从按键的上升沿开始 计数,直到20ms停止(20ms 对应125mhz时钟的第2500000个脉冲)。

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

增加约束文件:

set_property PACKAGE_PIN R14 [get_ports LED]
set_property PACKAGE_PIN D19 [get_ports KEY]
set_property PACKAGE_PIN H16 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports LED]
set_property IOSTANDARD LVCMOS33 [get_ports KEY]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

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

  • 本文的完整工程下载:03_PL_KEY
  • VIVADO的版本:2018.3
  • 工程创建目录: E:\Lemon_ZYNQ\FPGA\03_PL_KEY 其中不带消抖的是KEY_TEST目录,带消抖的是KEY_TEST_DEBOUNCE目录

发表回复

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