提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

按键作为基本的人机输入接口,由于其机械特性,在按键按下或松开的时候,都是会有抖动的。按键小豆的方式有很多。我的方法是通过计时来消抖,通过一个计数器,当按键输入有变化时,计数器清零,否则就累加,直到加到一个预定值,就认为按键稳定,输出按键值,这样就得到了没有抖动的按键值。


提示:以下是本篇文章正文内容,下面案例可供参考

一、按键消抖原理

普通按键的硬件示意图如下图所示。

按键结构示意图中可以看到按键存在一个反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,物理抖动便会产生电平的抖动。在按键从按下再到松开的过程中,其电平变化如下图所示,上为理想波形输出,下为实际波形输出。

上图中,产生的抖动次数以及间隔时间均是不可预期的,这就需要通过滤波来消除抖动可能对外部其他设备造成的影响。一般情况下抖动的总时间会持续 20ms 以内。这种抖动,可以通过硬件电路或者逻辑设计的方式来消除,也可以通过软件的方式完成。其中硬件电路消除抖动适用于按键数目较少的场合

二、状态机实现按键消抖

对于单按键的消抖模块,其接口如下图所示,接口声明功能描述如下:

各模块功能描述:

状态图如下:

三、Verilog代码实现

代码如下(示例):

module key_filter(
    Clk,
    Reset_n,
    Key,
//    Key_P_Flag,
//    Key_R_Flag,
    Key_Flag,
    Key_State
);
    input Clk;
    input Reset_n;
    input Key;
//    output reg Key_P_Flag;
//    output reg Key_R_Flag;
    output Key_Flag;
    output reg Key_State;
    
    reg Key_P_Flag;
    reg Key_R_Flag;
    
    assign Key_Flag = Key_P_Flag | Key_R_Flag;
    
    reg [2:0] r_Key;   //防止亚稳态用三个移位寄存器
    always@(posedge Clk)
        r_Key <= {r_Key[1:0],Key};
        
//    always@(posedge Clk)begin
//        r_Key[0] <= Key;  
//        r_Key[1] <= r_Key[0]; 
//        r_Key[2] <= r_Key[1]; 
//    end 

    wire pedge_key;
    assign pedge_key = (r_Key[2:1] == 2'b01);
    wire nedge_key;
    assign nedge_key = (r_Key[2:1] == 2'b10);
    
    reg [19:0]cnt;
    
    reg [1:0]state;
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        state <= 0;
        Key_R_Flag <= 1'b0;
        Key_P_Flag <= 1'b0;
        cnt <= 0;
        Key_State <= 1;
    end
    else begin
        case(state)
            0:
                begin
                    Key_R_Flag <= 1'b0;
                    if(nedge_key)
                        state <= 1;
                    else
                        state <= 0;
                end
            
            1:
                if((pedge_key)&&(cnt <1000000 -1))begin
                    state <= 0;
                    cnt <= 0;
                end
                else if(cnt >= 1000000 -1)begin
                    state <= 2;
                    cnt <= 0;
                    Key_P_Flag <= 1;
                    Key_State <= 0;
                end
                else begin
                    cnt <= cnt + 1'b1;
                    state <= 1;
                end
                
            2:
                begin
                    Key_P_Flag <= 0;
                    if(pedge_key)
                        state <= 3;
                    else
                        state <= 2;
                 end
                    
            3:
                if((nedge_key)&&(cnt <1000000 -1))begin
                    state <= 2;
                    cnt <= 0;
                end
                else if(cnt >= 1000000 -1)begin
                    state <= 0;
                    cnt <= 0;
                    Key_R_Flag <= 1'b1;
                    Key_State <= 1;
                end
                else begin
                    cnt <= cnt + 1'b1;
                    state <= 3;               
                end
        endcase   
    end

endmodule


【注】:上述代码注释处采用三个移位寄存器防止亚稳态产生(触发器在时钟上升沿来临时对数据进行采样,产生对应的输出。但是实际器件无法瞬时完成数据采样这一过程,需要数据在时钟沿前后均稳定一定时间)具体可参考:亚稳态现象

四、TB文件

代码如下(示例):

`timescale 1ns / 1ps

module key_filter_tb2();

    reg Clk;
    reg Reset_n;
    reg Key;
//    wire Key_P_Flag;
//    wire Key_R_Flag;
    wire Key_Flag;
    wire Key_State;    
    
    key_filter key_filter(
        Clk,
        Reset_n,
        Key,
//        Key_P_Flag,
//        Key_R_Flag,
        Key_Flag,
        Key_State
    );
    
    initial Clk = 1;
    always#10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        Key = 1;
        #201;
        Reset_n = 1;
        #3000;
        press_key(2);
        $stop;   
    end
    
    
    
    reg [31:0]rand;
    
    task press_key;
        input [3:0]seed;
        begin
            Key = 1;
            #20000000;
            repeat(5)begin
                rand = {$random(seed)} % 10000000;//(0,9999999)
                #rand Key = ~Key;
            end
            Key = 0;
            #40000000;
            
            repeat(5)begin
                rand = {$random(seed)} % 10000000;//(0,9999999)
                #rand Key = ~Key;
            end
            Key = 1;
            #40000000;           
        end
    endtask
    
endmodule

五、仿真波形展示

【附件:】链接:https://pan.baidu.com/s/1fZESYFREq6r3kTjIeDXlXA?pwd=vj42
提取码:vj42