按鍵的抖動
按鍵在按下過程中,觸點接觸和斷開瞬間都會産生機械抖動,如果不進行處理,每一次按鍵就會産生若幹次的響應。在單片機中一般使用delay函數來去除抖動,那麼硬體去抖動該如何實作呢?
按鍵去抖動電路
原理
該按鍵消抖電路主要由四個移位寄存器構成,在按鍵按下時,每個時鐘上升沿都會對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
再看看仿真的結果,也确實達到了消抖的效果
仿真波形
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
仿真結果
從圖中可以看到,在第二次按鍵按下時,一共輸出了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過小,也有可能得不到按鍵有效脈沖。
若文檔有任何錯誤或不足,歡迎在部落格下方留言指正;