文章目录
一、按键消抖简介
按键在我们日常生活中是很常见的,主要有机械按键和虚拟按键。在我们用来进行FPGA开发的开发板上一般都是机械按键,由于机械按键的物理特性,按键在按下的过程中,存在一段时间的抖动,同时在释放按键的过程中也会存在抖动,这就导致在识别按键的时候可以检测到多次的按键按下,而通常检测到一次按键输入信号的状态为低电平,就可以确认按键被按下了,所以我们在使用按键时往往需要进行按键消抖,以确保按键被按下一次只检测到一次低电平。
按键的抖动对于人类来说是感觉不到的,但是对于芯片来说,则是完全可以检测到电平的变化的,如果不对按键进行消抖处理,芯片可能会做出错误的判断,也就会导致我们的实验失败,所以进行按键消抖是非常有必要的。
二、按键消抖方式
1、硬件消抖
硬件消抖这种方式可以适用于在按键数较少时。硬件消抖的典型方法是,采用并联电容或者RS触发器。采用并联电容消抖是由于电容两端电压值不可突破,可以是上升沿和下降沿平滑无抖动。
但在实际应用中,采用硬件消抖的效果往往不理想,不仅局限于按键的数量,并且这也增加了电路的成本和复杂程度,所以实际项目中采用物理消抖的方式并不常用。
2、软件消抖
硬件消抖具有一定的局限性,当按键数量较多时,硬件消抖的方法就不是很实用了。
采用软件进行按键消抖原理也是很简单的,就是检测到按键闭合后执行一个延时程序,机械按键的抖动一般都在20ms之类,我们只需要延时20ms后再一次去检测按键的状态,如果仍然保持闭合状态,则确认按键按下。当按键释放的时候也要进行一个20ms的延时。
三、程序设计
1、设计思路
按键消抖,就是当按键不再抖动的时候按键有效。整体设计思路就是当检测到按键下降沿的时候开始计时20ms,如果在计时20ms期间由出现了一个上升沿,则计数器清零,等待下一个下降沿的到来便开始计数,反复进行,直到计时20ms内没有出现上升沿,就表明按键处于稳定状态了,表示按键按下。
2、程序代码
按键消抖模块代码如下:
/*========================================*
filename : key_filter.v
description : 按键消抖实验
time : 2022-11-08
author : 卡夫卡与海
*========================================*/
module key_filter(
input clk ,//系统时钟 50MHZ
input rst_n ,//系统复位
input key_in ,//按键输入
output reg key_down //按键输出
);
//参数定义
parameter DELAY_TIME = 1_000_000;//20ms
//信号定义
reg filter_flag ;//抖动标志
reg key_r0 ;//打拍
reg key_r1 ;
reg key_r2 ;
wire nedge ;//下降沿
wire podge ;//上升沿
reg [19:0] cnt_20ms ;//计数器,计数20ms
wire add_cnt_20ms;
wire end_cnt_20ms;
//对输入按键进行打拍,异步信号同步并检测边沿
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= -1;//负数以补码方式存放,对原码取反加一
key_r1 <= -1;
key_r2 <= -1;
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
key_r2 <= key_r1;
end
end
assign nedge = ~key_r1 && key_r2 ? 1'b1 :1'b0;//检测下降沿
assign podge = key_r1 && ~key_r2 ? 1'b1 :1'b0;//检测上升沿
//当检测到下降沿,filter_flag为1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
filter_flag <= 1'b0;
end
else if(nedge)begin
filter_flag <= 1'b1;
end
else if(end_cnt_20ms)begin
filter_flag <= 1'b0;
end
else begin
filter_flag <= filter_flag;
end
end
//当检测到filter_flag为1时开始计数 cnt_20ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20ms <= 0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= 0;
end
else begin
cnt_20ms <= cnt_20ms + 1'b1;
end
end
end
assign add_cnt_20ms = filter_flag;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == (DELAY_TIME - 1);
//key_down取是最后当前周期的key_r2的值,是稳定的
//当计数器计数满20ms后,并且没有抖动时,表面抖动结束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_down <= 0;
end
else if(end_cnt_20ms)begin
key_down <= ~key_r2;
end
else begin
key_down <= 0;
end
end
endmodule
仿真模块代码如下:
`timescale 1ns/1ns
module key_filter_tb();
reg clk ;
reg rst_n ;
reg key_in ;
wire key_down ;
//参数定义
parameter CYCLE = 20;//20ns
parameter RST_TIME = CYCLE*3;
//产生时钟
initial begin
clk = 1'b0;
forever
#(CYCLE/2)
clk = ~clk;
end
//产生复位
initial begin
rst_n = 1'b0;
#(RST_TIME);
rst_n = 1'b1;
end
//产生激励
initial begin
key_in = 1'b1;
#(RST_TIME);
key_in = 1'b0;
#20;
key_in = 1'b1;
#40;
key_in = 1'b0;
#3000;
key_in = 1'b1;
#5000;
key_in = 1'b0;
#20000;
$stop;
end
//模块例化
key_filter u_key_filter(
/*input */.clk (clk ),//系统时钟 50MHZ
/*input */.rst_n (rst_n ),//系统复位
/*input */.key_in (key_in ),//按键输入
/*output reg */.key_down (key_down) //按键输出
);
endmodule
3、仿真验证
说明:
通过仿真波形可以看到,当计数20ms之类按键状态发生跳变是,计数器清零,并重新开始计数,直到计数器20ms内按键状态没有发生改变时,此时表示按键按下。
总结
按键消抖还是比较简单的,主要时要弄清楚其中的原理,这里的打拍就是寄存一次,打几拍就是寄存几次,这样使得数据更加稳定,便于进行边沿检测。
这里按键输入只有一个按键,感兴趣的小伙伴可以去尝试对几个按键进行消抖处理,原理都是一样的,应该问题不大。