首先,做兩個假定,以方便后面的描述
- 假定按鍵的默認狀態為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仿真,但還沒有在實際的項目中驗證。在整理准備這個博客的時候,我又想到了一個感覺是更巧妙的方案,具體是這樣的:在第三個方案的基礎上,因為按鍵輸入有變化的第一時刻,輸出就已經改變了,在這種情況下,我可以把計時的時長改為一個很小的值,該值只要比抖動中的最長高低電平變化時間長即可。但想想也沒這個必要,且這個抖動的高低電平變化時長我也很難去給它界定一個值。
