實(shí)際系統(tǒng)中常用的按鍵大部分都是輕觸式按鍵,如下圖所示。該按鍵內(nèi)部由一個(gè)彈簧片和兩個(gè)固定觸點(diǎn)組成,當(dāng)彈簧片被按下,則兩個(gè)固定觸點(diǎn)接通,按鍵閉合。彈簧片松開(kāi),兩個(gè)觸點(diǎn)斷開(kāi),按鍵也就斷開(kāi)了。根據(jù)這種按鍵的機(jī)械特性,在按鍵按下時(shí),會(huì)先有一段時(shí)間的不穩(wěn)定期,在這期間,兩個(gè)觸點(diǎn)時(shí)而接通,時(shí)而斷開(kāi),我們稱之為抖動(dòng),當(dāng)按鍵大約按下20ms后,兩個(gè)觸點(diǎn)才能處于穩(wěn)定的閉合狀態(tài),按鍵松開(kāi)時(shí)和閉合時(shí)情況類似。而我們的FPGA工作在很高的頻率,按鍵接通或斷開(kāi)時(shí)任何一點(diǎn)小的抖動(dòng)都能輕易的捕捉到,如果不加區(qū)分的將每一次閉合或斷開(kāi)都當(dāng)做一次按鍵事件,那么勢(shì)必一次按鍵動(dòng)作會(huì)被FPGA識(shí)別為很多次按鍵操作,從而導(dǎo)致系統(tǒng)工作穩(wěn)定性下降。
![]()
輕觸按鍵實(shí)物圖
一次按鍵動(dòng)作的大致波形如下圖所示:
![]()
因此,我們所需要做的工作,就是濾除按鍵按下和釋放時(shí)各存在的20ms的不穩(wěn)定波形。做法思路是:檢測(cè)按鍵按下---》等待20Ms ----》檢測(cè)此時(shí)按鍵鍵值,若為按下值則按下有效,否則按下無(wú)效(后面可以檢測(cè)亦可以不檢測(cè),據(jù)具體情況而定----》檢測(cè)到按鍵松開(kāi)----》延遲20Ms ----》檢測(cè)此時(shí)的鍵值,若為按下值則松開(kāi)無(wú)效,否則按鍵松開(kāi))
硬件電路:
獨(dú)立按鍵屬于一種輸入設(shè)備,其與FPGA連接的IO口被接上了10K的上拉電阻,在按鍵沒(méi)有按下時(shí),F(xiàn)PGA會(huì)檢測(cè)到高電平;當(dāng)按鍵按下后,F(xiàn)PGA的IO口上則將呈現(xiàn)低電平。因此,按鍵檢測(cè)的實(shí)質(zhì)就是讀取FPGA的IO上的電平。
![]() 獨(dú)立按鍵典型電路
verilog 程序如下所示:
![]() /********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :key_shake.v ** CreateDate :2015.03 ** Funtions : 按鍵的消抖操作:在復(fù)位之后的100us內(nèi),不響應(yīng)按鍵的操作,在之后有按鍵按下后,有20ms的延遲,之后輸出按鍵輸出. ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved[F]. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content:V1.1:clk-->clk_100M, 常數(shù)聲明放到一起,便于修改。 *******************************************************************************/ module key_shake ( clk_100M, rst_n, key_in, key_out ); input clk_100M; //100Mhz input rst_n; input key_in; output key_out; //-------------------------------------- //在復(fù)位之后的100us內(nèi),不響應(yīng)按鍵的操作 localparam t_100us = 14'd9999; localparam t1ms = 17'd99999; //定時(shí)1ms localparam t_20ms = 5'd20; reg [13:0] cnt; reg key_en; //復(fù)位之后允許按鍵輸入標(biāo)志 always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt <= 0; key_en <=0; end else begin if(cnt == t_100us) begin key_en <= 1; end else begin key_en <= 0; cnt <= cnt + 1; end end end //-------------------------------------------------- wire HtoL_flag; //下降沿標(biāo)志 wire LtoH_flag; //上升沿標(biāo)志 reg [2:0] key_reg; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin key_reg <= 3'b111; //默認(rèn)沒(méi)按下?tīng)顟B(tài)為高,按下之后為低.反之則為3'b000; end else begin key_reg <= {key_reg[1:0],key_in}; end end assign HtoL_flag = key_en?(key_reg[2:1] == 2'b10):0; //下降沿檢測(cè),一個(gè)時(shí)鐘的高電平 assign LtoH_flag = key_en?(key_reg[2:1] == 2'b01):0; //上升沿檢測(cè),一個(gè)時(shí)鐘的高電平 //--------------------------------------------- reg cnt_en; //計(jì)數(shù)使能標(biāo)志 reg [16:0] cnt2; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt2 <= 17'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin cnt2 <= 17'd0; end else if(cnt_en) begin cnt2 <= cnt2 + 17'd1; end else cnt2 <= 17'd0; end reg [4:0] cnt3; always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin cnt3 <= 5'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin if(cnt3 == t_20ms ) cnt3 <= t_20ms; else cnt3 <= cnt3 + 1; end else if(!cnt_en) cnt3 <= 5'd0; end //---------------------------------- //按鍵狀態(tài)機(jī) reg [2:0] i; reg key_down; //按鍵按下標(biāo)志 reg key_up; //按鍵釋放標(biāo)志 always @(posedge clk_100M or negedge rst_n) begin if(!rst_n) begin key_down <= 0; key_up <= 0; i <= 0; cnt_en <= 0; end else begin case(i) 'd0: begin key_down <= 0; key_up <= 0; if(HtoL_flag) i <= 'd1; //檢測(cè)到按下 else if(LtoH_flag) i <= 'd2; //檢測(cè)到釋放按鍵 else i <= 'd0; end 'd1: begin if(cnt3 == t_20ms ) begin if(!key_in) //檢測(cè)到按鍵依然被按下 begin key_down <= 1; //按鍵按下成功 i <= 'd3; cnt_en <= 0; end else begin key_down <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd2: begin if(cnt3 == t_20ms ) begin if(key_in) //檢測(cè)到按鍵被釋放 begin key_up <= 1; //按鍵釋放成功 i <= 'd3; cnt_en <= 0; end else begin key_up <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd3: begin key_up <= 0; key_down <= 0; i <= 'd0; end default:i <= 'd0; endcase end end assign key_out = key_down; //當(dāng)按鍵被按下有效時(shí) // assign key_out = key_up; //當(dāng)按鍵被釋放后才有效時(shí) endmodule
測(cè)試代碼如下: ![]() /********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :key_testbench.v ** CreateDate :2015.03 ** Funtions :按鍵消抖的測(cè)試文件 ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content: *******************************************************************************/ module key_testbench; reg clk; reg rst_n; reg key_in; wire key_out; key_shake key_shake_1( .clk, .rst_n, .key_in, .key_out ); localparam tck = 24; localparam t = 1000/tck; always #(t/2) clk = ~clk; task key_in_down; begin #(3*t) key_in = 1; #(3*t) key_in = 0; #(8*t) key_in = 1; #(8*t) key_in = 0; #(13*t) key_in = 1; #(13*t) key_in = 0; end endtask task key_in_up; begin #(3*t) key_in = 0; #(3*t) key_in = 1; #(8*t) key_in = 0; #(8*t) key_in = 1; #(13*t) key_in = 0; #(13*t) key_in = 1; end endtask initial begin clk = 0; rst_n = 0; key_in = 1; #(100*t) rst_n = 1; #(100*t); #(10*t) key_in_down; #(100*t); #(10*t) key_in_up; #(8000*t); #(10*t) repeat(2) key_in_down; //按下時(shí)抖動(dòng) #(640000*t); //按下時(shí)間 #(10*t) repeat(2) key_in_up; //釋放時(shí)抖動(dòng) end endmodule 仿真結(jié)果: 1、在復(fù)位之后100us之前按下按鍵時(shí),不響應(yīng)。 2、抖動(dòng)(按下后20ms之內(nèi)釋放)。 ![]() 3、按下之后檢測(cè)以及釋放之后的檢測(cè)。
![]()
如果將按鍵按下有效時(shí)刻、按鍵釋放有效時(shí)刻和按鍵所處狀態(tài)全部表現(xiàn)出來(lái),則代碼稍作修改即可:
![]() /********************************Copyright************************************** **----------------------------File information-------------------------- ** File name :key_shake.v ** CreateDate :2015.03 ** Funtions : 按鍵的消抖操作:在復(fù)位之后的100us內(nèi),不響應(yīng)按鍵的操作,在之后有按鍵按下后,有20ms的延遲,檢測(cè),然后松開(kāi)時(shí)也有20ms的檢測(cè),之后輸出按鍵輸出. ** Operate on :M5C06N3L114C7 ** Copyright :All rights reserved[F]. ** Version :V1.0 **---------------------------Modify the file information---------------- ** Modified by : ** Modified data : ** Modify Content:V1.1:clk-->clk_100M, 常數(shù)聲明放到一起,便于修改。 *******************************************************************************/ module key_shake ( clk, rst_n, key_in, key_down_out, key_up_out, key_in_out ); input clk; //24Mhz input rst_n; input key_in; output key_down_out; //按下輸出 output key_up_out; //釋放輸出 output key_in_out; //跟隨輸入輸出 //-------------------------------------- //在復(fù)位之后的100us內(nèi),不響應(yīng)按鍵的操作 parameter t_20ms = 5'd20; `define CLK_20M // `define CLK_24M // `define CLK_50M `ifdef CLK_20M parameter t_100us = 12'd1999; parameter t1ms = 16'd19999; //定時(shí)1ms `endif `ifdef CLK_20M parameter t_100us = 12'd2399; parameter t1ms = 16'd23999; //定時(shí)1ms `endif `ifdef CLK_20M parameter t_100us = 13'd4999; parameter t1ms = 16'd49999; //定時(shí)1ms `endif reg [12:0] cnt; reg key_en; //復(fù)位之后允許按鍵輸入標(biāo)志 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt <= 0; key_en <=0; end else begin if(cnt == t_100us) begin key_en <= 1; end else begin key_en <= 0; cnt <= cnt + 1; end end end //-------------------------------------------------- wire HtoL_flag; //下降沿標(biāo)志 wire LtoH_flag; //上升沿標(biāo)志 reg [2:0] key_reg; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_reg <= 3'b111; //默認(rèn)沒(méi)按下?tīng)顟B(tài)為高,按下之后為低.反之則為3'b000; end else begin key_reg <= {key_reg[1:0],key_in}; end end assign HtoL_flag = key_en?(key_reg[2:1] == 2'b10):0; //下降沿檢測(cè),一個(gè)時(shí)鐘的高電平 assign LtoH_flag = key_en?(key_reg[2:1] == 2'b01):0; //上升沿檢測(cè),一個(gè)時(shí)鐘的高電平 //--------------------------------------------- reg cnt_en; //計(jì)數(shù)使能標(biāo)志 reg [15:0] cnt2; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt2 <= 16'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin cnt2 <= 16'd0; end else if(cnt_en) begin cnt2 <= cnt2 + 16'd1; end else cnt2 <= 16'd0; end reg [4:0] cnt3; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin cnt3 <= 5'd0; end else if((cnt_en)&&(cnt2 == t1ms)) begin if(cnt3 == t_20ms ) cnt3 <= t_20ms; else cnt3 <= cnt3 + 1; end else if(!cnt_en) cnt3 <= 5'd0; end //---------------------------------- //按鍵狀態(tài)機(jī) reg [2:0] i; reg key_down; //按鍵按下標(biāo)志 reg key_up; //按鍵釋放標(biāo)志 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_down <= 0; key_up <= 0; i <= 0; cnt_en <= 0; end else begin case(i) 'd0: begin key_down <= 0; key_up <= 0; if(HtoL_flag) i <= 'd1; //檢測(cè)到按下 else if(LtoH_flag) i <= 'd2; //檢測(cè)到釋放按鍵 else i <= 'd0; end 'd1: begin if(cnt3 == t_20ms ) begin if(!key_in) //檢測(cè)到按鍵依然被按下 begin key_down <= 1; //按鍵按下成功 i <= 'd3; cnt_en <= 0; end else begin key_down <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd2: begin if(cnt3 == t_20ms ) begin if(key_in) //檢測(cè)到按鍵被釋放 begin key_up <= 1; //按鍵釋放成功 i <= 'd3; cnt_en <= 0; end else begin key_up <= 0; i <= 'd0; cnt_en <= 0; end end else cnt_en <= 1; end 'd3: begin key_up <= 0; key_down <= 0; i <= 'd0; end default:i <= 'd0; endcase end end //--------------------------- reg key_out; always @(posedge clk or negedge rst_n) begin if(!rst_n) begin key_out <= 1; end else begin if(key_down) key_out <= 0; //按下為低 else if(key_up) key_out <= 1; //釋放為高 else key_out <= key_out; //否則保持 end end assign key_down_out = key_down; //當(dāng)按鍵被按下有效時(shí) assign key_up_out = key_up; //當(dāng)按鍵被釋放后才有效時(shí) assign key_in_out = key_out; endmodule
仿真: |
|
來(lái)自: aixuexidewau1 > 《按鍵消抖原理》