按鍵消抖


摘要:

本節將單 Bit 數據的異步信號同以及邊沿檢測方法引入到 FPG A中常用 的按鍵消抖設計,並在仿真測試吉利文件中引入隨機數發生函數

消抖的實現分為硬件實現和軟件實現,

 

 

 

產生的抖動次數以及間隔時間均是不可預期的,這就需要通過
濾波來消除抖動可能對外部其他設備造成的影響。一般情況下抖動的總時間會持
續20ms 以內。這種抖動,可以通過硬件電路或者邏輯設計的方式來消除,也可
以通過軟件的方式完成。其中硬件電路消除抖動適用於按鍵數目較少的場合。

下面先來講硬件實現:

使用基於與非門的RS 觸發器來消除抖動的電路圖,如圖 3.7-4 所示。假設
初始狀態開關與B 接通,此時S=1,R=0,觸發器置0 即Q=0;當B 切換到A 的
過程中,存在開關既沒有與A 也沒有與B 接觸,此時S=1,R=1,觸發器保持即
Q=0;當切換到A 瞬間時有R=1,S=0,觸發器置1 即Q=1。此時即使當A 出現抖
動即S=1,觸發器的狀態仍能保持當前狀態即Q=1 不變。整個過程波形圖如圖所
示Q。同理,可以分析出開關由A 切換到B 時觸發器的狀態變化。

 

 請注意:這只適合於單刀雙擲開關。

而對於兩腳和四腳的按鍵目前常用的是RC電路和555定時器組成的單穩態觸發器。

 

接下來是軟件實現:

 

 

 在按鍵沒有按下時,處於高電平狀態。

代碼實現:

module key_filter(
                clk,
                rst_n,
                key_in,
                key_flag,
                key_state
                );
    input clk;
    input rst_n;
    input key_in;
    output reg key_flag;//按鍵狀態切換標志
    output reg key_state;//按鍵狀態
    //因為按鍵是異步信號,先同步到系統時鍾域
    reg key_in_r,key_in_rr;
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin    
            key_in_r<=0;
            key_in_rr<=0;
        end
        else begin
            key_in_r<=key_in;
            key_in_rr<=key_in_r;
        end
    //按鍵檢測用到上升沿和下降沿檢測
    reg key_in_rr_1,key_in_rr_11;
    wire pedge,nedge;
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            key_in_rr_1<=0;
            key_in_rr_11<=0;
        end
        else begin
            key_in_rr_1<=key_in_rr;
            key_in_rr_11<=key_in_rr_1;
        end
    assign pedge=(!key_in_rr_11) && key_in_rr_1;
    assign nedge=key_in_rr_11 && (!key_in_rr_1);
    //抖動計數包含使能模塊和計數模塊(按20ms來濾除,需要計數到1000000-1)
    reg [19:0]cnt;
    reg cnt_full;
    reg en_cnt;
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            cnt<=20'd0;
        else if(en_cnt)
            cnt<=cnt+1'b1;
        else
            cnt<=20'd0;
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            cnt_full<=1'b0;
        else if(cnt==20'd999_999)
            cnt_full<=1'b1;
        else
            cnt_full<=1'b0;
    //狀態機
    localparam IDLE=4'b0001,FILTER0=4'b0010,DOWN=4'b0100,FILTER1=4'b1000;
    reg [3:0]state;
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            en_cnt<=1'b0;
            state<=IDLE;
            key_flag<=1'b0;
            key_state<=1'b1;
        end
        else begin
            case(state)
                IDLE:begin
                    key_flag=1'b0;
                    if(nedge)begin
                        en_cnt<=1'b1;
                        state<=FILTER0;
                    end
                    else
                        state<=IDLE;
                end
                FILTER0:begin
                    if(cnt_full)begin
                        en_cnt<=1'b0;
                        state<=DOWN;
                        key_flag<=1'b1;
                        key_state<=1'b0;
                    end
                    else if(pedge)begin
                        state<=IDLE;
                        en_cnt<=1'b0;
                    end
                    else
                        state<=FILTER0;
                end
                DOWN:begin
                    key_flag<=1'b0;
                    if(pedge)begin
                        en_cnt<=1'b1;
                        state<=FILTER1;
                    end
                    else
                        state<=DOWN;
                end
                FILTER1:begin
                    if(cnt_full)begin
                        en_cnt<=1'b0;
                        state<=IDLE;
                        key_flag<=1'b1;
                        key_state<=1'b1;
                    end
                    else if(nedge)
                        state<=DOWN;
                    else
                        state<=FILTER1;
                end
                default:state<=IDLE;
            endcase
        end
        
    
endmodule 
View Code

tb:

`timescale 1ns/1ns
module key_filter_tb;
    reg clk;
    reg rst_n;
    reg key_in;
    wire key_flag;//按鍵狀態切換標志
    wire key_state;//按鍵狀態
    key_filter key_filter(
                .clk(clk),
                .rst_n(rst_n),
                .key_in(key_in),
                .key_flag(key_flag),
                .key_state(key_state)
                );
    initial clk=0;
    always #10 clk=~clk;
    
    initial begin
        rst_n=0;
        #21;
        rst_n=1;
        //模擬抖動在20ms以內
        key_in=0;#1000;
        key_in=1;#2000;
        key_in=0;#1400;
        key_in=1;#2600;
        key_in=0;#1300;
        key_in=1;#200;
        //產生一個低電平大於20ms,代表穩定按下
        key_in=0;#20_000_100;
        #30_000_000;
        //模擬釋放抖動20ms以內
        key_in=1;#1000;
        key_in=0;#2000;
        key_in=1;#1400;
        key_in=0;#2600;
        key_in=1;#1300;
        key_in=0;#200;
        //產生一個高電平大於20ms,代表穩定釋放
        key_in=1;#20_000_100;
        #30_000_000;
        $stop;
    end
endmodule
View Code

tb_task:

`timescale 1ns/1ns
module key_filter_tb_task;
    reg clk;
    reg rst_n;
    reg key_in;
    wire key_flag;//按鍵狀態切換標志
    wire key_state;//按鍵狀態
    key_filter key_filter(
                .clk(clk),
                .rst_n(rst_n),
                .key_in(key_in),
                .key_flag(key_flag),
                .key_state(key_state)
                );
    integer myrand;
    initial clk=0;
    always #10 clk=~clk;
    task press_key;
        begin
            repeat(50)begin
                myrand={$random}%65536;
                #myrand key_in=~key_in;
            end
            key_in=0;
            #50_000_000;
            repeat(50)begin
                myrand={$random}%65536;
                #myrand key_in=~key_in;
            end
            key_in=1;
            #50_000_000;
        end
    endtask
    initial begin
        rst_n=0;
        key_in=1;
        #21;
        rst_n=1;
        press_key;#10000;
        press_key;#10000;
        press_key;#10000;
        $stop;
    end
endmodule
View Code

波形圖:

 

 

 

 

 

 抖動放大:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM