在數(shù)字電路中,開關(guān)用于用于產(chǎn)生高、低電平,按鍵用于產(chǎn)生單次脈沖。由于開關(guān)和按鍵為機械部件,每次按下或者釋放時,由于簧片的彈性會產(chǎn)生短暫的抖動,然后才能穩(wěn)定接通或者斷開。
抖動現(xiàn)象會導(dǎo)致按鍵電路的輸出產(chǎn)生毛刺,如圖1所示,從而可能導(dǎo)致系統(tǒng)產(chǎn)生誤動作。
開關(guān)和按鍵的抖動時間一般在20ms以內(nèi)。為了防止因按鍵抖動引起的系統(tǒng)誤動作,必須對按鍵電路進行消抖,只在按鍵閉合或者斷開穩(wěn)定后才允許輸出。
圖1按鍵抖動現(xiàn)象
按鍵消抖有軟件消抖和硬件消抖兩類方法。軟件消抖是在嵌入式系統(tǒng)中,檢測到按鍵按下時,應(yīng)用軟件延時20ms后再次檢測按鍵的狀態(tài),如果兩次狀態(tài)相同,則確認(rèn)按鍵已經(jīng)按下。
這種處理方式雖然簡單,但是會浪費CPU資源。
硬件消抖有多種方法。第一種方法是應(yīng)用施密特電路的回差特性配合積分電路實現(xiàn)按鍵消抖,應(yīng)用電路如圖2所示。
圖2應(yīng)用積分電路實現(xiàn)按鍵消抖
第二種方法是應(yīng)用鎖存器的保持功能實現(xiàn)開關(guān)消抖,應(yīng)用電路如圖3所示。
圖3應(yīng)用鎖存器實現(xiàn)開關(guān)消抖
除了上述兩種按鍵消抖方法外,在基于FPGA的數(shù)字系統(tǒng)設(shè)計中,也可以應(yīng)用狀態(tài)機設(shè)計按鍵消抖電路,在FPGA內(nèi)部實現(xiàn)。
基于狀態(tài)機設(shè)計按鍵消抖電路時,需要將按鍵的一次動作分解為:按下前、按下時、穩(wěn)定期和釋放時4個狀態(tài),如圖1中所示,分別用KEY_IDLE、KEY_PRESSED、KEY_ACTIVE和KEY_RELEASE表示。
設(shè)按鍵輸入用key_in表示,低電平有效,設(shè)計消抖時間為20ms,則按鍵消抖狀態(tài)機的狀態(tài)轉(zhuǎn)換關(guān)系如圖4所示。
圖4按鍵消抖狀態(tài)機
根據(jù)上述狀態(tài)轉(zhuǎn)換關(guān)系,描述按鍵消抖模塊的Verilog HDL代碼參考如下:
module KEY_debounce #(parameter DEBOUNCE_TIME = 1000_000 )( // 50MHz時鐘時,對應(yīng)消抖時間為20ms input clk_50, // 50MHz時鐘,周期為20ns input rst_n, // 復(fù)位信號 input key_in, // 按鍵輸入 output reg key_out // 消抖后輸出 ); // 內(nèi)部狀態(tài)定義,循環(huán)編碼方式 localparam KEY_IDLE = 2'b00, // 按下前 KEY_PRESSED = 2'b01, // 按下時 KEY_ACTIVE = 2'b11, // 穩(wěn)定期 KEY_RELEASE = 2'b10; // 釋放時 // 內(nèi)部變量定義 reg [19:0] debounce_cnt; // 消抖計數(shù)變量 reg [1:0] current_state,next_state; // 現(xiàn)態(tài)和次態(tài) reg [0:1] keytmp; // 同步寄存器 // 內(nèi)部線網(wǎng)定義 wire cnt_en,cnt_end; // 計數(shù)允許和停止計數(shù)標(biāo)志 wire cnt_flag; // 消抖計數(shù)標(biāo)志 wire release_flag; // 按鍵釋放標(biāo)志 // 允許消抖計數(shù)邏輯:按鍵按下時或者釋放時,cnt_en有效。 assign cnt_en = current_state == KEY_PRESSED || current_state == KEY_RELEASE; // 停止計數(shù)標(biāo)志:cnt_en有效并且debounce_cnt達到最大值,則cnt_end有效。 assign cnt_end = cnt_en && ( debounce_cnt == DEBOUNCE_TIME - 1 ); // 正在計數(shù)標(biāo)志:cnt_en有效并且debounce_cnt未達到最大值,則cnt_flag有效。 assign cnt_flag = cnt_en && ( debounce_cnt < DEBOUNCE_TIME ); // 按鍵已釋放標(biāo)志: cnt_end有效,并且keytmp[1]為高電平,則release_flag有效。 assign release_flag = cnt_end && keytmp[1]; // 按鍵輸入兩級同步寄存過程,以消除亞穩(wěn)態(tài)。 always @(posedge clk_50 or negedge rst_n) if ( !rst_n ) keytmp <= 2'b00; // 清零 else keytmp[0:1] <= {key_in,keytmp[0]}; // 右移 // 時序邏輯過程,描述狀態(tài)轉(zhuǎn)換 always @(posedge clk_50 or negedge rst_n) if ( !rst_n ) current_state <= KEY_IDLE; else current_state <= next_state; // 組合邏輯過程,定義次態(tài) always @(*) begin case ( current_state ) KEY_IDLE: if ( !keytmp[1] ) // 按鍵按下時,進入KEY_PRESSED next_state = KEY_PRESSED; else // 否則,保持KEY_IDLE next_state = current_state; KEY_PRESSED: if ( cnt_end && !keytmp[1] ) // 消抖時間到且keytmp[1]為0,確認(rèn)按下有效 next_state = KEY_ACTIVE; else if ( cnt_flag && keytmp[1] ) // 正在計數(shù),但keytmp[1]為1,則為抖動 next_state = KEY_IDLE; else // 否則狀態(tài)保持 next_state = current_state; KEY_ACTIVE: if ( keytmp[1] ) // keytmp[1]跳變?yōu)?則進入釋放狀態(tài) next_state = KEY_RELEASE; else next_state = current_state; // 否則狀態(tài)保持 KEY_RELEASE: if ( release_flag ) // 按鍵已釋放,返回 next_state = KEY_IDLE; else if ( cnt_flag && !keytmp[1] ) // 正在計數(shù),但keytmp[1]為0,則為抖動 next_state = KEY_ACTIVE; else // 否則狀態(tài)保持 next_state = current_state; default: next_state = KEY_IDLE; endcase end // 時序邏輯過程,消抖計時 always @( posedge clk_50 or negedge rst_n ) if ( !rst_n ) debounce_cnt <= 20'b0; else if ( cnt_en ) // 計數(shù)允許信號有效 if ( cnt_end ) // 消抖時間到 debounce_cnt <= 20'b0; else // 消抖時間未到 debounce_cnt <= debounce_cnt + 1'b1; else // 計數(shù)允許信號無效 debounce_cnt <= 20'b0; // 時序邏輯過程,按鍵消抖后輸出 always @( posedge clk_50 or negedge rst_n ) if ( !rst_n ) key_out <= 1'b1; else case ( current_state ) KEY_IDLE : key_out <= 1'b1; KEY_PRESSED: key_out <= 1'b1; KEY_ACTIVE : key_out <= 1'b0; KEY_RELEASE: key_out <= 1'b0; default: key_out <= 1'b1; endcase endmodule
對上述代碼進行仿真驗證時,需要建立testbench文件,應(yīng)用系統(tǒng)函數(shù)$random產(chǎn)生隨機數(shù),以模擬不規(guī)則的抖動脈沖間隔。
應(yīng)用系統(tǒng)函數(shù)$random產(chǎn)生隨機整數(shù)的語法格式為
num = $random%b其中b為十進制整數(shù),num為-(b-1) ~ (b-1)范圍內(nèi)的隨機整數(shù)。 應(yīng)用系統(tǒng)函數(shù)$random產(chǎn)生隨機正整數(shù)的語法格式為
num ={$random}%b其中b為十進制整數(shù),num為0 ~ (b-1)范圍內(nèi)的隨機整數(shù)。 測試按鍵消抖模塊功能的testbench代碼參考如下:
`timescale 1ns/1ps module KEY_debounce_vlg_tst(); reg clk; reg rst_n; reg key_in; wire key_out; // 模塊參數(shù)重定義,減少計數(shù)容量,以縮短仿真時間 defparam KEY_debounce.DEBOUNCE_TIME = 50000; // 內(nèi)部變量定義 reg [15:0] rand_num; // 仿真參數(shù)定義 parameter RESET_TIME = 2, STEP = 5; // 按鍵消抖模塊例化 KEY_debounce i1 ( .clk (clk), .rst_n (rst_n), .key_in (key_in), .key_out (key_out)); // 設(shè)置復(fù)位信號波形 initial begin rst_n = 1; #1; rst_n = 0; #(STEP * RESET_TIME); rst_n = 1; end // 設(shè)置按鍵輸入 initial begin #1; key_in = 1; //按下前 #(STEP * 10); press_key; // 第1次按鍵過程 #10_000; press_key; // 第2次按鍵過程 end // 設(shè)置時鐘信號 initial clk_50 = 0; always #(STEP/2) clk_50 = ~clk_50; // 監(jiān)測任務(wù) initial $monitor($time,"clk_50=%b rst_n=%b key_in=%b key_out=%b", clk_50,rst_n,key_in,key_out); // 按鍵任務(wù)定義 task press_key; begin repeat (20) begin // 模擬前沿抖動過程 rand_num = {$random}%5000; #rand_num key_in = ~key_in; end key_in = 0; #300_000; repeat (20) begin // 模擬后沿抖動過程 rand_num = {$random}%5000; #rand_num key_in = ~key_in; end key_in = 1; #300_000; end endtask endmodule
上述代碼中使用了defparam語句用于對KEY_debounce模塊中的DEBOUNCE_TIME參數(shù)進行重定義,在確保功能驗證的前提下縮短消抖時間。啟動modelsim進行仿真,結(jié)果如圖5所示。
圖5按鍵消抖模塊仿真波形
從波形圖中可以看出,消抖電路對按鍵按下和釋放產(chǎn)生的4次抖動都能實現(xiàn)有效消抖,因此驗證基于狀態(tài)機設(shè)計的按鍵消抖模塊功能正確。
審核編輯:劉清
-
FPGA
+關(guān)注
關(guān)注
1643文章
21944瀏覽量
613386 -
鎖存器
+關(guān)注
關(guān)注
8文章
922瀏覽量
42064 -
數(shù)字電路
+關(guān)注
關(guān)注
193文章
1637瀏覽量
81521 -
狀態(tài)機
+關(guān)注
關(guān)注
2文章
493瀏覽量
28044 -
按鍵消抖
+關(guān)注
關(guān)注
2文章
28瀏覽量
10610
原文標(biāo)題:按鍵消抖電路的幾種設(shè)計方案
文章出處:【微信號:LowHuangMakerSpace,微信公眾號:LowHuangMakerSpace】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
按鍵的硬件消抖電路原理詳解

按鍵消抖電路的實現(xiàn)方式
技術(shù)分享:明德?lián)P按鍵消抖的原理和基于fpga的消抖設(shè)計
基于FPGA的按鍵消抖電路設(shè)計
51單片機的獨立按鍵和按鍵消抖及矩陣按鍵的電路與程序免費下載

評論