按鍵消抖
在之前的實驗中我們學(xué)習(xí)了如何用按鍵作為FPGA的輸入控制,在本實驗中將學(xué)習(xí)如何進(jìn)行按鍵消抖,用按鍵完成更多的功能。
硬件說明
按鍵是一種常用的電子開關(guān),電子設(shè)計中不可缺少的輸入設(shè)備。當(dāng)按下時使開關(guān)導(dǎo)通,松開時則開關(guān)斷開,內(nèi)部結(jié)構(gòu)是靠金屬彈片來實現(xiàn)通斷。
按鍵抖動的原理

在點亮LED實驗中我們知道了小腳丫板子上按鍵的設(shè)計,當(dāng)按鍵未被按下時,連接到FPGA管腳認(rèn)為是高電平;當(dāng)按鍵被按下時,連接到FPGA管腳認(rèn)為是低電平。
要消除按鍵的抖動,我們需要去掃描按鍵,也就是不斷的去采集按鍵的狀態(tài)。軟件消抖時我們一般只考慮按鍵按下時的抖動,而放棄對釋放時抖動的消除。用系統(tǒng)時鐘(頻率較高)去采集按鍵狀態(tài),當(dāng)檢測到按下時用計數(shù)器延時20ms,再去檢測按鍵狀態(tài),如果這時仍為按下狀態(tài),確認(rèn)是一次按下動作,否側(cè)的話認(rèn)為無按鍵按下。如何檢測按鍵狀態(tài)變化就需要用到脈沖邊沿檢測的方法。
脈沖邊沿檢測
檢測按鍵按下時要用到脈沖邊沿檢測的方法,捕捉信號的突變、捕捉時鐘的上升下降沿等經(jīng)常會用到這種方法。簡單地說就是用一個頻率更高的時鐘去觸發(fā)要檢測的信號,用兩個寄存器去儲存相鄰兩個時鐘采集到的值,然后進(jìn)行異或運(yùn)算,如果不為零,代表發(fā)生了上升沿或者下降沿。
在按鍵消抖的過程中,同樣運(yùn)用了脈沖邊沿檢測。用兩個寄存器儲存相鄰時鐘采集的值(例如datapre,data),然后將data取反與前一個值相與(state=datapre&(~data)),如果為1,則判斷有下降沿既按鍵按下由高到低;否則無變化。將一個信號由連續(xù)時鐘采集,相鄰兩個鐘觸發(fā)的值存入兩個寄存器。理解verilog實現(xiàn)這個過程要充分了解其中的非阻塞賦值。
Verilog代碼
本實驗主要通過按鍵來控制led的翻轉(zhuǎn),當(dāng)按下一次led變亮,再按下一次led變暗。首先我們做個試驗,對按鍵不做處理通過按鍵來控制led翻轉(zhuǎn)。
//******************************************************************** //>>>>>>>>>>>>>>>>>>>>>>>>>COPYRIGHTNOTICE<<<<<<<<<<<<<<<<<<<<<<<<< //******************************************************************** //Filename:top.v //Modulename:top //Author:STEP //Description:controlledthroughthebutton // //-------------------------------------------------------------------- //CodeRevisionHistory: //-------------------------------------------------------------------- //Version:|Mod.Date:|ChangesMade: //V1.0|2017/03/02|Initialver //-------------------------------------------------------------------- //ModuleFunction:按鍵控制led翻轉(zhuǎn),未做消抖 moduletop( key,//按鍵輸入 rst,//復(fù)位輸入 led//led輸出 ); inputkey,rst; outputregled; always@(keyorrst) if(!rst)//復(fù)位時led熄滅 led=1; elseif(key==0) led=~led;//按鍵按下時led翻轉(zhuǎn) else led=led; endmodule
未經(jīng)過消抖的程序下載到小腳丫上會發(fā)現(xiàn)按鍵有時不能夠控制led翻轉(zhuǎn),這是因為按鍵的抖動造成了led狀態(tài)變化不可控,所以我們必須將抖動消除。下面是一種延時去抖的程序
//******************************************************************** //>>>>>>>>>>>>>>>>>>>>>>>>>COPYRIGHTNOTICE<<<<<<<<<<<<<<<<<<<<<<<<< //******************************************************************** //Filename:debounce.v //Modulename:debounce //Author:STEP //Description: // //-------------------------------------------------------------------- //CodeRevisionHistory: //-------------------------------------------------------------------- //Version:|Mod.Date:|ChangesMade: //V1.0|2017/03/02|Initialver //-------------------------------------------------------------------- //ModuleFunction:按鍵消抖 moduledebounce(clk,rst,key,key_pulse); parameterN=1;//要消除的按鍵的數(shù)量 inputclk; inputrst; input [N-1:0]key;//輸入的按鍵 output[N-1:0]key_pulse;//按鍵動作產(chǎn)生的脈沖 reg[N-1:0]key_rst_pre;//定義一個寄存器型變量存儲上一個觸發(fā)時的按鍵值 reg[N-1:0]key_rst;//定義一個寄存器變量儲存儲當(dāng)前時刻觸發(fā)的按鍵值 wire[N-1:0]key_edge;//檢測到按鍵由高到低變化是產(chǎn)生一個高脈沖 //利用非阻塞賦值特點,將兩個時鐘觸發(fā)時按鍵狀態(tài)存儲在兩個寄存器變量中 always@(posedgeclkornegedgerst) begin if(!rst)begin key_rst<={N{1'b1}};//初始化時給key_rst賦值全為1,{}中表示N個1 key_rst_pre<={N{1'b1}}; end elsebegin key_rst<=key; //第一個時鐘上升沿觸發(fā)之后key的值賦給key_rst,同時key_rst的值賦給key_rst_pre key_rst_pre<=key_rst; //非阻塞賦值。相當(dāng)于經(jīng)過兩個時鐘觸發(fā),key_rst存儲的是當(dāng)前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值 end end assignkey_edge=key_rst_pre&(~key_rst);//脈沖邊沿檢測。當(dāng)key檢測到下降沿時,key_edge產(chǎn)生一個時鐘周期的高電平reg [17:0] cnt; //產(chǎn)生延時所用的計數(shù)器,系統(tǒng)時鐘12MHz,要延時20ms左右時間,至少需要18位計數(shù)器 //產(chǎn)生20ms延時,當(dāng)檢測到key_edge有效是計數(shù)器清零開始計數(shù) always@(posedgeclkornegedgerst) begin if(!rst) cnt<=18'h0; elseif(key_edge) cnt<=18'h0; else cnt<=cnt+1'h1; endreg[N-1:0]key_sec_pre;//延時后檢測電平寄存器變量 reg[N-1:0]key_sec; //延時后檢測key,如果按鍵狀態(tài)變低產(chǎn)生一個時鐘的高脈沖。如果按鍵狀態(tài)是高的話說明按鍵無效 always@(posedgeclkornegedgerst) begin if(!rst) key_sec<={N{1'b1}}; elseif(cnt==18'h3ffff) key_sec<=key; end always@(posedgeclkornegedgerst) begin if(!rst) key_sec_pre<={N{1'b1}}; else key_sec_pre<=key_sec; end assignkey_pulse=key_sec_pre&(~key_sec); endmodule
以上就是一個N位按鍵的消抖程序,如果有按鍵按下會輸出一個時鐘周期的高脈沖。下面我們可以試試用這個按鍵消抖的輸出來觸發(fā)LED的顯示,既按鍵一次LED翻轉(zhuǎn)。你也可以不加按鍵消抖試試用按鍵來控制LED(按一次變亮,再按一次滅掉)。
下面的程序是例化調(diào)用debounce模塊來控制LED
//******************************************************************** //>>>>>>>>>>>>>>>>>>>>>>>>>COPYRIGHTNOTICE<<<<<<<<<<<<<<<<<<<<<<<<< //******************************************************************** //Filename:top.v //Modulename:top //Author:STEP //Description: // //-------------------------------------------------------------------- //CodeRevisionHistory: //-------------------------------------------------------------------- //Version:|Mod.Date:|ChangesMade: //V1.0|2017/03/02|Initialver //-------------------------------------------------------------------- //ModuleFunction:進(jìn)過按鍵消抖后控制led顯示翻轉(zhuǎn) moduletop(clk,rst,key,led); inputclk; inputrst; input key; outputregled; wirekey_pulse;//當(dāng)按鍵按下時產(chǎn)生一個高脈沖,翻轉(zhuǎn)一次led always@(posedgeclkornegedgerst) begin if(!rst) led<=1'b1; elseif(key_pulse) led<=~led; else led<=led; end //例化消抖module,這里沒有傳遞參數(shù)N,采用了默認(rèn)的N=1 debounceu1( .clk(clk), .rst(rst), .key(key), .key_pulse(key_pulse) ); endmodule
引腳分配
設(shè)置好復(fù)位鍵可消抖的按鍵,編譯完成后下載,通過按鍵就可以翻轉(zhuǎn)LED。你也可以定義多個按鍵控制多個LED,還可以比較不加按鍵消抖情況下實際的效果對比如何。
| 信號 | 引腳 |
|---|---|
| clk | C1 |
| rst | L14 |
| key | N14 |
| led | N13 |
小結(jié)
在本實驗學(xué)習(xí)了如何進(jìn)行按鍵的消抖。在很多應(yīng)用情況下我們必須采取消抖才能更好地控制邏輯。在下一個實驗計時控制中我們將學(xué)習(xí)計時的顯示和控制,在這里我們要用到按鍵的消抖以及數(shù)碼管,我們甚至可以用小腳丫做一個計時器甚至電子表。
