幾種按鍵消抖方案的verilog描述


首先,做兩個假定,以方便后面的描述

  • 假定按鍵的默認狀態為0,被按下后為1
  • 假定按鍵抖動時長小於20ms,也即使用20ms的消抖時間

核心:方案

  • 最容易想到的方案

    在按鍵電平穩定的情況下,當第一次檢測到鍵位電平變化,開始20ms計時,計時時間到后將按鍵電平更新為當前電平

  • 或許這才是最容易想的方案

    在20ms計時的過程中,有任何的電平變化都立即復位計時

  • 消除按鍵反應延時抖方案

    在有電平變化時立即改變按鍵輸出電平,並開始20ms計時,忽略這其中抖動

測試平台設計(修改代碼以仿真的1us代替實際1ms)

  • 無抖動 上升沿抖動5毫秒
  • 下降沿抖動15毫秒
  • 上升和下降沿均抖動19毫秒

  附加測試(可以不通過)

  • 抖動25毫秒

代碼

  • 方案1
module debounce(
    input wire clk, nrst,
    input wire key_in,
    output reg key_out
    );

    // 20ms parameter
//    localparam TIME_20MS = 1_000_000;
    localparam TIME_20MS = 1_000;       // just for test

    // variable
    reg [20:0] cnt;
    reg key_cnt;
    
    // debounce time passed, refresh key state
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_out <= 0;
        else if(cnt == TIME_20MS - 1)
            key_out <= key_in;
    end

    // while in debounce state, count, otherwise 0
    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt <= 0;
        else if(key_cnt)
            cnt <= cnt + 1'b1;
        else
            cnt <= 0; 
    end
     
     // 
     always @(posedge clk or negedge nrst) begin
            if(nrst == 0)
                key_cnt <= 0;
            else if(key_cnt == 0 && key_in != key_out)
                key_cnt <= 1;
            else if(cnt == TIME_20MS - 1)
                key_cnt <= 0;
     end
endmodule

 

  • 方案2
module debounce(
    input wire clk, nrst,
    input wire key_in,
    output reg key_out
    );

//    localparam TIME_20MS = 1_000_000;
    localparam TIME_20MS = 1_000;

    reg key_cnt;
    reg [20:0] cnt;

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_cnt <= 0;
        else if(cnt == TIME_20MS - 1)
            key_cnt <= 0;
        else if(key_cnt == 0 && key_out != key_in)
            key_cnt <= 1;
    end

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt <= 0;
        else if(key_cnt) begin
            if(key_out == key_in)
                cnt <= 0;
            else
                cnt <= cnt + 1'b1;
        end
        else
            cnt <= 0;
    end
     
     always @(posedge clk or negedge nrst) begin
            if(nrst == 0)
                key_out <= 0;
            else if(cnt == TIME_20MS - 1)
                key_out <= key_in;
     end
endmodule

 

  • 方案3
module debounce(
    input wire clk, nrst,
    input wire key_in,
    output reg key_out
    );

//    localparam TIME_20MS = 1_000_000;
    localparam TIME_20MS = 1_000;       // just for test

    reg key_cnt;
    reg [20:0] cnt;

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_cnt <= 0;
        else if(key_cnt == 0 && key_out != key_in)
            key_cnt <= 1;
        else if(cnt == TIME_20MS - 1)
            key_cnt <= 0;
    end

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            cnt <= 0;
        else if(key_cnt)
            cnt <= cnt + 1'b1;
        else
            cnt <= 0;
    end

    always @(posedge clk or negedge nrst) begin
        if(nrst == 0)
            key_out <= 0;
        else if(key_cnt == 0 && key_out != key_in)
            key_out <= key_in;
    end
endmodule

 

  • 測試代碼
// 按鍵消抖測試電路

// 時間單位
`timescale 1ns/10ps

// module
module  debounce_tb;

    // time period parameter
    localparam T = 20;

    // variable
    reg clk, nrst;
    reg key_in;
    wire key_out;

    // instantiate
    debounce uut(
        .clk    (clk    ),
        .nrst   (nrst   ),
        .key_in (key_in ),
        .key_out(key_out)
    );

    // clock
    initial begin
        clk = 1;
        forever #(T/2) clk = ~clk;
    end

    // reset
    initial begin
        nrst = 1;
        @(negedge clk) nrst = 0;
        @(negedge clk) nrst = 1;
    end

    // key_in
    initial begin
        // initial value
        key_in = 0;
        
        // wait reset
        repeat(3) @(negedge clk);
        
        // no bounce
        // key down
        key_in = 1;

        // last 60ms
        repeat(3000) @(negedge clk);

        // key up
        key_in = 0;

        // wait 50ms
        repeat(2500) @(negedge clk);

        // down 5ms, up 15ms
        // key down, bounce 5ms
        repeat(251) @(negedge clk) key_in = ~key_in;

        // last 60ms
        repeat(3000) @(negedge clk);

        // key up, bounce 15ms
        repeat(751) @(negedge clk) key_in = ~key_in;

        // wait 50ms
        repeat(2500) @(negedge clk);

        // down 19ms, up 19ms
        // key down, bounce 19ms
        repeat(951) @(negedge clk) key_in = ~key_in;

        // last 60ms
        repeat(3000) @(negedge clk);

        // key up, bounce 19ms
        repeat(951) @(negedge clk) key_in = ~key_in;

        // wait 50ms
        repeat(2500) @(negedge clk);
        
        // additional, this situation shoud not ever happen
        // down 25ms, up 25ms
        // key down, bounce 25ms
        repeat(1251) @(negedge clk) key_in = ~key_in;

        // last 60ms
        repeat(3000) @(negedge clk);

        // key up, bounce 25ms
        repeat(1251) @(negedge clk) key_in = ~key_in;

        // wait 50ms
        repeat(2500) @(negedge clk);

        // stop
        $stop;
    end
endmodule

 

放在最后的,並不一定是最不重要的

  對於上面的三種方案,我比較喜歡第三種方案,它更貼合實際的按鍵狀態,以上的代碼我都做過modelsim仿真,但還沒有在實際的項目中驗證。在整理准備這個博客的時候,我又想到了一個感覺是更巧妙的方案,具體是這樣的:在第三個方案的基礎上,因為按鍵輸入有變化的第一時刻,輸出就已經改變了,在這種情況下,我可以把計時的時長改為一個很小的值,該值只要比抖動中的最長高低電平變化時間長即可。但想想也沒這個必要,且這個抖動的高低電平變化時長我也很難去給它界定一個值。


免責聲明!

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



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