天天看點

FPGA中的按鍵消抖按鍵的抖動按鍵去抖動電路Testbench

按鍵的抖動

按鍵在按下過程中,觸點接觸和斷開瞬間都會産生機械抖動,如果不進行處理,每一次按鍵就會産生若幹次的響應。在單片機中一般使用delay函數來去除抖動,那麼硬體去抖動該如何實作呢?

按鍵去抖動電路

FPGA中的按鍵消抖按鍵的抖動按鍵去抖動電路Testbench

原理

該按鍵消抖電路主要由四個移位寄存器構成,在按鍵按下時,每個時鐘上升沿都會對key_in進行檢測,隻有當連續四個時鐘上升沿都檢測到鍵值為1時,key

Verilog 結構描述

源代碼

module xiaodou_structure(key_in,clk,key_out);

input key_in,clk;
output key_out;
reg q0,q1,q2,q3;

always @(posedge clk)
begin 
 q3 <= q2;
 q2 <= q1;
 q1 <= q0;
 q0 <= key_in;
end

assign key_out=q0&q1&q2&q3;

endmodule
           

我一直對阻塞指派和非阻塞指派有疑惑,如果不能确定這樣寫是否會綜合為移位寄存器,我們也可以這樣寫:

module xiaodou_structure2(key_in,clk,key_out);

input key_in,clk;
output key_out;
reg [3:0] q;

always @(posedge clk)
begin 
 q={q[2:0],key_in};
end

assign key_out=(q===4'b1111)?1:0;
endmodule
           

檢查一下,綜合器綜合後生成的網表,确實是一個4位的移位寄存器。

Technology Map Viewer

FPGA中的按鍵消抖按鍵的抖動按鍵去抖動電路Testbench

再看看仿真的結果,也确實達到了消抖的效果

仿真波形

FPGA中的按鍵消抖按鍵的抖動按鍵去抖動電路Testbench

Verilog 行為描述

源代碼

module xiaodou_action(key_in,clk,key_out);

input key_in,clk;
output reg key_out;
reg [1:0] state;

parameter S0='d0,S1='d1,S2='d2,S3='d3;

always @(posedge clk)
begin
 case(state)
  S0: begin 
    if(key_in) begin key_out<=0; state<=S1; end
    else begin key_out<=0; state<=S0; end
    end 
  S1: begin 
    if(key_in) begin key_out<=0; state<=S2; end
    else begin key_out<=0; state<=S0; end
    end 
  S2: begin 
    if(key_in) begin key_out<=0; state<=S3; end
    else begin key_out<=0; state<=S0; end
    end 
  S3: begin 
    if(key_in) begin key_out<=1; state<=S0; end  //S3改為S0
    else begin key_out<=0; state<=S0; end
    end 
  default state<=S0;
 endcase
end

endmodule
           

這段代碼其實就是用狀态機設計的1111序列檢測器,這裡我做了微微的改動,在日常經驗中我們知道,如果長按某個按鍵,按鍵按下且穩定時,每隔一段時間,就會有一個按鍵有效脈沖,這樣就可以實作對某個參數的連續調整。是以這裡我将S3下一個狀态設定為S0,而不是保持S3,這樣在按鍵長按的情況下,每4個時鐘就會有一個按鍵有效脈沖輸出。

RTL

FPGA中的按鍵消抖按鍵的抖動按鍵去抖動電路Testbench

仿真結果

FPGA中的按鍵消抖按鍵的抖動按鍵去抖動電路Testbench

從圖中可以看到,在第二次按鍵按下時,一共輸出了3個按鍵有效脈沖。

Testbench

以上仿真所使用的testbench主要代碼均相同,具體如下:

initial                                                
begin                                                  
clk=0;
key_in=0;
//按下
#20 key_in=1;
#3 key_in=0;
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
#2 key_in=1;
//穩定
#90 key_in=0;
//松手
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;

//按下
#150 key_in=1;
#3 key_in=0;
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
#2 key_in=1;
//穩定
#280 key_in=0;
//松手
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;

#100 $stop;                      
$display("Running testbench");                       
end 

always                                                               
begin                                                  
#10 clk=~clk;                                
end                                                    
endmodule
           

注意

最後需要注意的是時鐘clk的選取,不能過大,一般取100Hz以下,但如果clk過小,也有可能得不到按鍵有效脈沖。

若文檔有任何錯誤或不足,歡迎在部落格下方留言指正;