[筆記].一種獨立鍵盤消抖的Verilog寫法

weixin_34391854發表於2010-03-13

圖1 KEY原理圖

圖1 KEY原理圖

 

圖2 LED原理圖

圖2 LED原理圖

key_led.v

module key_led(
  input            CLOCK_50,
  input            Q_KEY,
  input      [4:1] KEY,
  output reg [4:1] LED
);  

//++++++++++++++++++++++++++++++++++++++
// 獲取鍵值 開始
//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_val;                     // 鍵值

key_debounce u0(
  .i_clk            (CLOCK_50),
  .i_rst_n          (Q_KEY),
  .i_key            (KEY),
  .o_key_val        (key_val)           // 按下為0,鬆開為1
);  
//--------------------------------------
// 獲取鍵值 結束
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
// 按下鍵後開關LED 開始
//++++++++++++++++++++++++++++++++++++++
always @ (posedge CLOCK_50, negedge Q_KEY)
  if (!Q_KEY)
    LED <= 4'hF;                        // 0滅1亮
  else
    case (1'b0)
      key_val[1] : LED[1] <= ~LED[1];
      key_val[2] : LED[2] <= ~LED[2];
      key_val[3] : LED[3] <= ~LED[3];
      key_val[4] : LED[4] <= ~LED[4];
      default    : LED    <=  LED   ;   // 預設亮滅情況不變
    endcase 
//--------------------------------------
// 按下鍵後開關LED 結束
//--------------------------------------

endmodule

key_debounce.v

module key_debounce(
  input            i_clk,
  input            i_rst_n,
  input      [4:1] i_key,               // 按下為0,鬆開為1
  output reg [4:1] o_key_val            // 鍵值
);  

//++++++++++++++++++++++++++++++++++++++
reg [4:1] key_samp1, key_samp1_locked;

// 將i_key採集至key_samp1
always @ (posedge i_clk, negedge i_rst_n)
  if(!i_rst_n) 
    key_samp1 <= 4'hF;
  else         
    key_samp1 <= i_key;

// 將key_samp1鎖存至key_samp1_locked
always @ (posedge i_clk, negedge i_rst_n)
  if(!i_rst_n) 
    key_samp1_locked <= 4'hF;
  else         
    key_samp1_locked <= key_samp1;
//--------------------------------------

//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_changed1;

// 當key_samp1由1變為0時
// key_changed1由0變為1,只維持一個時鐘週期
assign key_changed1 = key_samp1_locked & (~key_samp1); 
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
reg [19:0] cnt;

// 一旦有按鍵按下,cnt立即被清零
always @ (posedge i_clk, negedge i_rst_n)
  if(!i_rst_n)
    cnt <= 20'h0;
  else if(key_changed1)
    cnt <= 20'h0;
  else
    cnt <= cnt + 1'b1;
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
reg [4:1] key_samp2, key_samp2_locked;

// 只有當按鍵不變化(不抖動),且維持20ms以上時
// 才將i_key採集至key_samp2
always @ (posedge i_clk, negedge i_rst_n)
  if(!i_rst_n)
    key_samp2 <= 4'hF;
  else if(cnt == 20'hF_FFFF)            // 0xFFFFF/50M = 20.9715ms
    key_samp2 <= i_key;

// 將key_samp2鎖存至key_samp2_locked
always @ (posedge i_clk, negedge i_rst_n)
  if(!i_rst_n)
    key_samp2_locked <= 4'hF;
  else
    key_samp2_locked <= key_samp2;
//--------------------------------------

//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_changed2;

// 當key_samp2由1變為0時
// key_changed2由0變為1,只維持一個時鐘週期
assign key_changed2 = key_samp2_locked & (~key_samp2); 
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
// 每次按鍵穩定後,輸出鍵值
// 按下為0,鬆開為1
always @ (posedge i_clk, negedge i_rst_n)
  if(!i_rst_n)
    o_key_val <= 4'hF;
  else
    o_key_val <= ~key_changed2;
//--------------------------------------

endmodule 

HDL思路

3cdd09ff-062d-4e68-85e7-697eab9c0e22

圖3 按鍵抖動

如圖3所示,按鍵在按下和釋放的時候都會有一定的抖動。本程式碼僅消除了按下抖動,沒有對釋放抖動進行處理。

程式碼中使用了一個計數器來實現延時。只要這個計數器正常計數到20'hF_FFFF,我們就取樣一個值key_samp2,並將其鎖存進key_samp2_locked。一旦有下降沿來臨,就輸出按鍵改變值key_changed2。同時,該模組也在使用高頻時鐘i_clk不停地採集鍵值到key_samp1,並鎖存進key_samp1_locked;一旦下降沿存在,輸出按鍵變化值key_changed1,這樣計數器就會被清零。假設沒有key_changed1,我們將0xFFFFF/50M = 20.9715ms取樣一次鍵值,並檢測其下降沿,輸出鍵值。但是由於抖動的原因,採用高頻時鐘i_clk取樣的key_samp1就有可能導致key_changed1為一,進而使得低頻的20.9715ms取樣延時執行。也就是說,只有按鍵處於穩定的狀態時,key_samp2才被正常取樣,才有可能檢測到key_changed2。

參考資料

1. 特權.基於verilog按鍵消抖設計

http://group.ednchina.com/1375/21132.aspx

相關文章