簡單的說,進入了電子,不管是學純模擬,還是學單片機,DSP、ARM等處理器,或者是我們的FPGA,一般沒有不用到按鍵的地方。按鍵:人機交互控制,主要用于對系統的控制,信號的釋放等。因此在這里,FPGA上應用的按鍵消抖動,也不得不講! 一、為什么要消抖動 如上圖所示,在按鍵被按下的短暫一瞬間,由于硬件上的抖動,往往會產生幾毫秒的抖動,在這時候若采集信號,勢必導致誤操作,甚至系統崩潰;同樣,在釋放按鍵的那一刻,硬件上會相應的產生抖動,會產生同樣的后果。因此,在模擬或者數字電路中,我們要避免在最不穩定的時候采集信號,進行操作。 對此一般產用消抖動的原理。一般可分為以下幾種: (1)延時 (2)N次低電平計數 (3)低通濾波 在數字電路中,一般產用(1)(2)種方法。后文中將詳細介紹。 二、各種消抖動 1. 模擬電路按鍵消抖動 對于模擬電路中,一般消抖動用的是電容消抖動或者施密特觸發等電路,再次不做具體介紹。施密特觸發電路如下所示,具體可參考百度文庫:http://wenku.baidu.com/view/c77025d9ce2f0066f5332276.html 2. 單片機中按鍵消抖動 對于單片機中的按鍵消抖動,本節Bingo根據自己當年寫過的單片機其中的一個代碼來講解,代碼如下所示: unsigned char key_scan(void) { if(key == 0) //檢測到被按下 { delay(5); //延時5ms,消抖 if(key != 0) retrurn 0; //是抖動,返回退出 while(!key1); // 確認被按下,等下釋放 delay(5); //延時5ms,消抖 while(!key1); //確認被釋放 return 1; //返回按下信號 } return 0; //沒信號 } 針對以上代碼,消抖動的順序如下所示: (1)檢測到信號 (2)延時5ms,消抖動 (3)繼續檢測信號,確認是否被按下 a) 是,則開始等待釋放 b) 否,則返回0,退出 (4)延時5ms,消抖動 (5)確認,返回按下信號,退出 當然在單片機中也可以循環計數來確認是否被按下。Bingo認為如此,太耗MCU資源,因此再次不做講述。 3. FPGA中的按鍵消抖動 對于FPGA中的消抖動,很多教科書上都沒有講述。但Bingo覺得這個很有必要。對于信號穩定性以及準確性分析,按鍵信號必須有一個穩定的脈沖,不然對系統穩定性有很大的干擾。 此處Bingo用兩種方法對FPGA中按鍵消抖動分析。其中第一種是通過狀態機的使用直接移植以上MCU的代碼,這個思想在FPGA狀態機中很重要。第二種,通過循環n次計數的方法來確認是否真的被按下,這種方法很實用在FPGA這種高速并行器件中。 (1)利用狀態機移植MCU按鍵消抖動 此模塊由Bingo無數次修改測試最后成型的代碼,在功能上可適配n個按鍵,在思想上利用單片機采用了單片機消抖動的思想。具體代碼實現過程請有需要的自行分析,本模塊移植方便,Verilog代碼如下所示: /************************************************* * Module Name : key_scan_jitter.v * Engineer : Crazy Bingo * Target Device : EP2C8Q208C8 * Tool versions : Quartus II 11.0 * Create Date : 2011-6-26 * Revision : v1.0 * Description : **************************************************/ module key_scan_jitter #( parameter KEY_WIDTH = 2 ) ( input clk, input rst_n, input [KEY_WIDTH-1:0] key_data, output key_flag, output reg [KEY_WIDTH-1:0] key_value ); reg [19:0] cnt; //delay_5ms(249999) reg [2:0] state; //----------------------------------- always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt MCU按鍵消抖動的狀態,此模塊可以模擬成一下5個狀態,見state machine: (2)循環n次計數消抖動 同樣,此模塊也是Bingo無數次修改測試最后成型的代碼,利用了更少的資源,更適用于并行高速FPGA的性能要求。具體代碼實現過程請有需要的自行分析,本模塊通過相關時鐘的適配,n次計數來確認按鍵信號,Verilog代碼如下所示: /************************************************* * Module Name : key_scan.v * Engineer : Crazy Bingo * Target Device : EP2C8Q208C8 * Tool versions : Quartus II 11.0 * Create Date : 2011-6-25 * Revision : v1.0 * Description : **************************************************/ module key_scan #( parameter KEY_WIDTH = 2 ) ( input clk, //50MHz input rst_n, input [KEY_WIDTH-1:0] key_data, output key_flag, output reg [KEY_WIDTH-1:0] key_value ); //--------------------------------- //escape the jitters reg [19:0] key_cnt; //scan counter reg [KEY_WIDTH-1:0] key_data_r; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_data_r <= {KEY_WIDTH{1'b1}}; key_cnt <= 0; end else begin key_data_r <= key_data; //lock the key value if((key_data == key_data_r) && (key_data != {KEY_WIDTH{1'b1}})) //20ms escape jitter begin if(key_cnt < 20'hfffff) key_cnt <= key_cnt + 1'b1; end else key_cnt <= 0; end end wire cnt_flag = (key_cnt == 20'hffffe) ? 1'b1 : 1'b0;//!! //----------------------------------- //sure the key is pressed reg key_flag_r; always@(posedge clk or negedge rst_n) begin if(!rst_n) begin key_flag_r <= 0; key_value <= 0; end else if(cnt_flag) begin key_flag_r <= 1; key_value <= key_data; //locked the data end else //let go your hand key_flag_r <= 0; //lock the key_value end //--------------------------------------- //Capture the rising endge of the key_flag reg key_flag_r0,key_flag_r1; always@(posedge clk or negedge rst_n) begin if(!rst_n) begin key_flag_r0 <= 0; key_flag_r1 <= 0; end else begin key_flag_r0 <= key_flag_r; key_flag_r1 <= key_flag_r0; end end assign key_flag = ~key_flag_r1 & key_flag_r0; endmodule |