【FPGA】四、按键消抖

文章目录

一、按键消抖简介

 二、按键消抖方式

1、硬件消抖

2、软件消抖

三、程序设计

1、设计思路

2、程序代码

3、仿真验证

 总结


一、按键消抖简介

       按键在我们日常生活中是很常见的,主要有机械按键和虚拟按键。在我们用来进行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内按键状态没有发生改变时,此时表示按键按下。 


 总结

        按键消抖还是比较简单的,主要时要弄清楚其中的原理,这里的打拍就是寄存一次,打几拍就是寄存几次,这样使得数据更加稳定,便于进行边沿检测。

        这里按键输入只有一个按键,感兴趣的小伙伴可以去尝试对几个按键进行消抖处理,原理都是一样的,应该问题不大。

猜你喜欢

转载自blog.csdn.net/weixin_62912626/article/details/127756359