九、按键消抖

目录

按键消抖

1、硬件消抖

2、软件消抖

代码编写

仿真


按键是常见的电子元器件,在电子设计中应用广泛。在FPGA的实验工程中,我们可以使用按键作为系统复位信号或者控制信号的外部输入。目前按键种类繁多,常见的有自锁按键、薄膜按键等。我们开发板上的按键也是按键的一种,其特点是接触电阻小,手感好,按键按下或谈起时有清脆响声,但由于其构造和原理,在按键闭合及断开的瞬间均伴有一连串的抖动

本文章将根据机械按键的构造与原理设计并实现按键消抖模块。以开发板上的物理按键作为输入信号,使用设计的按键消抖模块对输入的按键信号进行消抖处理,输出能够正常使用的按键出发信号。

按键消抖

我们所使用的按键开关为机械弹性开关,当机械接触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合及断开的瞬间伴随有一连串的抖动。按键抖动原理:

按键消抖分为硬件消抖和软件消抖。

1、硬件消抖

在按键个数较小时可采用硬件方法进行消抖。其中两个与非门构成一个RS触发器,当按键未按下时,输出为0;当按下时输出为1。此时机试使用按键的机械性能,使按键因弹性抖动而产生瞬时断开,只要按键不返回原始状态A,双稳态电路的状态就不会改变,输出保持为0,不会产生抖动的波形。

2、软件消抖

如果按键个数较多,则常采用软件方法去抖,即检测出按键闭合后执行一个延时程序,根据抖动时间产生一个20ms的延时,让前沿抖动消失后再一次检测的状态,如果仍保持闭合状态电平,则确认为真正有键按下。

前抖动和后抖动的时间范围是在5-10ms,中间的稳定状态大于20ms,如果说当有20ms的时间内都没有产生抖动,这个按键就处于一个稳定的状态,换句话说就是只要在20ms的时间内没有抖动的产生,就可以认为按键信号是可以被使用的。这里我们就要找到最后一次抖动的时间是从什么时候开始的,才能开启20ms的计数,否则在20ms之内的都不能保证是20ms的稳定时间。

这里添加一个20ms的计数器,当输入信号为低电平时就开始计数,在计数器计数期间如果再次检测到高电平,说明上一次检测到的低电平一定是一个抖动,那么就将计数器清零。总结来说就是,检测到输入信号低电平计数,高电平清零。

1、计数器计数值的问题

晶振的时钟是50Mhz=50ns,计数是20ms,所及计数值就是M=999999。

2、计数器清零的问题

按照一般思路我们当计数到最大值的时候清零重新计数,后面会讨论这种方式是否合理。

3、输出信号什么时候拉高的问题

输出信号是一个脉冲信号,只有一个时钟周期是高电平,当计数器达到最大值的时候,拉高一个时钟周期。如果稳定时间足够长,低电平时间会远远大于20ms,计数器就会出现多次清零,会有多次脉冲,这显然不是我们想要的结果。

我们就需要考虑这种结果到底是拉高条件导致的,还是计数器清零条件导致的。首先,假设是拉高条件导致的,不论我们在0,或者999999拉高,都会出现多次脉冲的情况,所以这种假设不成立。那么就只剩下是计数器清零导致的了。我们让计数器达到最大值之后不清零,保持最大值,只有当输入信号为高电平时才对计数器进行清零,修改波形图如下:

但是这里输出信号就不再是脉冲信号了,这也不是我们想要的结果,根本原因就是计数器保持最大值的时间太长。解决方法:当计数器计数到最大值-1的时候,拉高flag信号,其他时候保持低电平,这就能保持flag信号还是脉冲信号,并且只拉高了一次(计数器达到最大值-1的情况只有一次,并且和最大值是很接近的),如图:

代码编写

module key_filter
#(parameter CNT_MAX = 20'd999999)
(
input wire sys_clk,
input wire sys_rst_n,
input wire key_in,
output reg key_flag
);
reg [19:0] cnt_20ms;
//计数器赋值,输入信号低电平计数,高电平清零
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)//复位信号有效清零
        cnt_20ms <= 20'd0;
    else if(key_in == 1'b1)//高电平清零
        cnt_20ms <= 20'd0;
    else if(cnt_20ms == CNT_MAX)//计数器达到最大值,保持最大值不清零
        cnt_20ms <= CNT_MAX;
    else
        cnt_20ms <= cnt_20ms + 20'd1;
//输出信号赋值
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else if(cnt_20ms == (CNT_MAX-20'd1))
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;
endmodule

仿真

仿真代码:

`timescale 1ns/1ns
module tb_key_filter();
reg sys_clk;
reg sys_rst_n;
reg key_in;
reg [7:0] tb_cnt;//模拟一次按键按下的全过程
wire key_flag;

//赋初值
initial
    begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
    end
    
always #10 sys_clk = ~sys_clk;

always @(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        tb_cnt <= 8'd0;
    else if(tb_cnt == 8'd249)
        tb_cnt <= 8'd0;
    else
        tb_cnt <= tb_cnt+8'd1;

always @(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_in <= 1'b1;
    else if(((tb_cnt >= 8'd19) && (tb_cnt <= 8'd49)) ||
            ((tb_cnt >= 8'd149) && (tb_cnt <= 8'd199)))
        key_in <= {$random}%2;
    else if((tb_cnt < 8'd19) || (tb_cnt > 8'd199))
        key_in <= 1'b1;
    else
        key_in <= 1'b0;
        
//实例化
key_filter 
#(.CNT_MAX(20'd24))
key_filter_inst
(
.sys_clk  (sys_clk),
.sys_rst_n(sys_rst_n),
.key_in   (key_in),
.key_flag (key_flag)
);
endmodule

仿真波形:

这里就不再进行上班验证了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值