FPGA按键消抖
简介
按键
按键是输入设备,一般来说,按键在没有按下的时候是高电平;当按键按下的时候,为低电平。
在DE2-70 User Manual中
Each switch provides a high logic level (3.3 volts) when it is not pressed, and provides a low logic level (0 volts) when depressed. Since the pushbutton switches are debounced, they are appropriate for use as clock or reset inputs in a circuit.
这里介绍到了按键抖动(Button Bouncing)和按键消抖(Button Debouncing)。
按键消抖
按键消抖通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。
上图描述的是硬件的按键消抖。可见,消抖后,一次按键,只产生了一次电平变化。
这对我们接下来利用按键意义重大。
物理消抖
简单介绍下实现上图的消抖。
电容滤波
将电容并联在按键的两端,利用电容的放电的延时特性。将产生抖动的电平通过电容吸收掉。从而达到消抖的作用。
RS触发器
利用RS触发器来吸收按键的抖动。一旦有键按下,触发器立即翻转,触电的抖动便不会再对输出产生影响。
程序消抖
按键在FPGA中必不可少,我们需要利用按键对一些变量进行累加或累减。比如频率、分数等。
未消抖
在一开始的学习中,我们可能只用到了开关(switch),编写开关控制的程序类似于将开关当作一个布尔(Boolean)变量。
但是按键与开关不同,以下面这段代码为例,期望作用是 按键按下后,LED灯状态改变。
module Test(
input sys_clk,
input rst_n,
input key,
output reg led
);
always@(posedge sys_clk or negedge rst_n)
begin
if(rst_n == 1'b0)
led <= 1'b0;
else if(key == 1'b0)
led <= ~led;
else
led <= led;
end
endmodule
然而,上板测试发现,LED状态灯一直处于不亮的状态。
假设,我们的时钟频率是50MHz,那么它的周期就是20ns。
那么,在你按按钮的全过程中,按下的状态持续了多长时间呢?
显然,远大于20ns,所以LED会多次取反。所以这种简单粗暴的方法是不适合按键的。
消抖
我们刚刚的问题是,短时间内重复检测,从而多次执行了按键后的行为。
可以想到,给这个按键定个时:当有按键按下,计数器不断自增,若因为抖动,则计数器会清空,重新计数,当不抖动的时候,就会计满,此时才会判定为按键按下。
代码
// 定时器消除抖动
module key_filter(
input wire sys_clk, // 50M时钟
input wire sys_rst_n, // 复位信号,低电平有效
input wire key, // 按键输入
output reg key_flag, // 按键信号有效信号
output reg key_value // 消抖后的按键信号
);
reg [31:0] delay_cnt; // 32位定时器
reg key_reg;
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_reg <= 1'b1;
delay_cnt <= 32'd0;
end
else begin
key_reg <= key;
if(key_reg != key) // 检测到按键状态发生变化(按下 或 释放)
delay_cnt <= 32'd1000000; // 计数器装载初始值(计数时间为20ms)
else if(key_reg == key) begin // 按键状态稳定时,计数器递减,开始20ms倒计时
if(delay_cnt > 32'd0) // 不稳定时,重新计时
delay_cnt <= delay_cnt - 1'b1;
else
delay_cnt <= delay_cnt;
end
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin // 复位
key_flag <= 1'b0; // 无效
key_value <= 1'b1; // 高电平为未按下
end
else begin
if(delay_cnt == 32'd1) begin // 按键稳定状态维持了20ms
key_flag <= 1'b1; // 此时消抖过程结束,信号有效
key_value <= key; // 保存此时按键信号
end
else begin
key_flag <= 1'b0;
key_value <= key_value;
end
end
end
endmodule
如此,我们再重新写下刚刚的未消抖程序。
module Test(
input sys_clk, // 系统时钟
input rst_n, // 复位信号,低电平有效
input key, // 按键信号
output reg led // LED灯
);
wire key_value;
wire key_flag;
// 例化
key_filter key_filter_inst(
.sys_clk (sys_clk), // 50M时钟
.sys_rst_n (sys_rst_n), // 复位信号,低电平有效
.key (key), // 按键输入)
.key_flag (key_flag), // 按键信号有效信号
.key_value (key_value) // 按键消抖后的数据
);
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
begin
led <= 1'b0;
end
else if(key_flag && (~key_value)) // 按键是否有效按下
begin
led <= ~led;
end
else
begin
led <= led;
end
end
endmodule
成功,按键可以控制LED灯亮与熄灭。仿真略。
这是简单的计时消抖,读者有兴趣可阅读学习状态机的方法。
参考文献
[1] 按键的硬件消抖电路原理详解
[3] FPGA学习—按键控制
[4] DE2-70 User Manual
本文来自博客园,作者:江水为竭,转载请注明原文链接:https://www.cnblogs.com/Az1r/p/17589520.html