Altera MAX10: 按鍵消抖
按鍵消抖
在之前的實(shí)驗中我們學(xué)習了如何用按鍵作為FPGA的輸入控制,在本實(shí)驗中將學(xué)習如何進(jìn)行按鍵消抖,用按鍵完成更多的功能。
====硬件說(shuō)明====
按鍵是一種常用的電子開(kāi)關(guān),電子設計中不可缺少的輸入設備。當按下時(shí)使開(kāi)關(guān)導通,松開(kāi)時(shí)則開(kāi)關(guān)斷開(kāi),內部結構是靠金屬彈片來(lái)實(shí)現通斷。
按鍵抖動(dòng)的原理
在點(diǎn)亮LED實(shí)驗中我們知道了小腳丫板子上按鍵的設計,當按鍵未被按下時(shí),連接到FPGA管腳認為是高電平;當按鍵被按下時(shí),連接到FPGA管腳認為是低電平。
要消除按鍵的抖動(dòng),我們需要去掃描按鍵,也就是不斷的去采集按鍵的狀態(tài)。軟件消抖時(shí)我們一般只考慮按鍵按下時(shí)的抖動(dòng),而放棄對釋放時(shí)抖動(dòng)的消除。用系統時(shí)鐘(頻率較高)去采集按鍵狀態(tài),當檢測到按下時(shí)用計數器延時(shí)20ms,再去檢測按鍵狀態(tài),如果這時(shí)仍為按下?tīng)顟B(tài),確認是一次按下動(dòng)作,否側的話(huà)認為無(wú)按鍵按下。如何檢測按鍵狀態(tài)變化就需要用到脈沖邊沿檢測的方法。
脈沖邊沿檢測
檢測按鍵按下時(shí)要用到脈沖邊沿檢測的方法,捕捉信號的突變、捕捉時(shí)鐘的上升下降沿等經(jīng)常會(huì )用到這種方法。簡(jiǎn)單地說(shuō)就是用一個(gè)頻率更高的時(shí)鐘去觸發(fā)要檢測的信號,用兩個(gè)寄存器去儲存相鄰兩個(gè)時(shí)鐘采集到的值,然后進(jìn)行異或運算,如果不為零,代表發(fā)生了上升沿或者下降沿。
在按鍵消抖的過(guò)程中,同樣運用了脈沖邊沿檢測。用兩個(gè)寄存器儲存相鄰時(shí)鐘采集的值(例如datapre,data),然后將data取反與前一個(gè)值相與(state=datapre&(~data)),如果為1,則判斷有下降沿既按鍵按下由高到低;否則無(wú)變化。
將一個(gè)信號由連續時(shí)鐘采集,相鄰兩個(gè)鐘觸發(fā)的值存入兩個(gè)寄存器。理解verilog實(shí)現這個(gè)過(guò)程要充分了解其中的非阻塞賦值。
====Verilog代碼====
本實(shí)驗主要通過(guò)按鍵來(lái)控制led的翻轉,當按下一次led變亮,再按下一次led變暗。首先我們做個(gè)試驗,對按鍵不做處理通過(guò)按鍵來(lái)控制led翻轉。
// ******************************************************************** // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // ******************************************************************** // File name : top.v // Module name : top // Author : STEP // Description : control led through the button // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2017/03/02 |Initial ver // -------------------------------------------------------------------- // Module Function:按鍵控制led翻轉,未做消抖 module top( key, //按鍵輸入 rst, //復位輸入 led //led輸出 ); input key,rst; output reg led; always @(key or rst) if (!rst) //復位時(shí)led熄滅 led = 1; else if(key == 0) led = ~led; //按鍵按下時(shí)led翻轉 else led = led; endmodule
未經(jīng)過(guò)消抖的程序下載到小腳丫上會(huì )發(fā)現按鍵有時(shí)不能夠控制led翻轉,這是因為按鍵的抖動(dòng)造成了led狀態(tài)變化不可控,所以我們必須將抖動(dòng)消除。下面是一種延時(shí)去抖的程序
// ******************************************************************** // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // ******************************************************************** // File name : debounce.v // Module name : debounce // Author : STEP // Description : // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2017/03/02 |Initial ver // -------------------------------------------------------------------- // Module Function:按鍵消抖 module debounce (clk,rst,key,key_pulse); parameter N = 1; //要消除的按鍵的數量 input clk; input rst; input [N-1:0] key; //輸入的按鍵 output [N-1:0] key_pulse; //按鍵動(dòng)作產(chǎn)生的脈沖 reg [N-1:0] key_rst_pre; //定義一個(gè)寄存器型變量存儲上一個(gè)觸發(fā)時(shí)的按鍵值 reg [N-1:0] key_rst; //定義一個(gè)寄存器變量?jì)Υ鎯Ξ斍皶r(shí)刻觸發(fā)的按鍵值 wire [N-1:0] key_edge; //檢測到按鍵由高到低變化是產(chǎn)生一個(gè)高脈沖 //利用非阻塞賦值特點(diǎn),將兩個(gè)時(shí)鐘觸發(fā)時(shí)按鍵狀態(tài)存儲在兩個(gè)寄存器變量中 always @(posedge clk or negedge rst) begin if (!rst) begin key_rst <= {N{1'b1}}; //初始化時(shí)給key_rst賦值全為1,{}中表示N個(gè)1 key_rst_pre <= {N{1'b1}}; end else begin key_rst <= key; //第一個(gè)時(shí)鐘上升沿觸發(fā)之后key的值賦給key_rst,同時(shí)key_rst的值賦給key_rst_pre key_rst_pre <= key_rst; //非阻塞賦值。相當于經(jīng)過(guò)兩個(gè)時(shí)鐘觸發(fā),key_rst存儲的是當前時(shí)刻key的值,key_rst_pre存儲的是前一個(gè)時(shí)鐘的key的值 end end assign key_edge = key_rst_pre & (~key_rst); //脈沖邊沿檢測。當key檢測到下降沿時(shí),key_edge產(chǎn)生一個(gè)時(shí)鐘周期的高電平 reg [17:0] cnt; //產(chǎn)生延時(shí)所用的計數器,系統時(shí)鐘12MHz,要延時(shí)20ms左右時(shí)間,至少需要18位計數器 //產(chǎn)生20ms延時(shí),當檢測到key_edge有效是計數器清零開(kāi)始計數 always @(posedge clk or negedge rst) begin if(!rst) cnt <= 18'h0; else if(key_edge) cnt <= 18'h0; else cnt <= cnt + 1'h1; end reg [N-1:0] key_sec_pre; //延時(shí)后檢測電平寄存器變量 reg [N-1:0] key_sec; //延時(shí)后檢測key,如果按鍵狀態(tài)變低產(chǎn)生一個(gè)時(shí)鐘的高脈沖。如果按鍵狀態(tài)是高的話(huà)說(shuō)明按鍵無(wú)效 always @(posedge clk or negedge rst) begin if (!rst) key_sec <= {N{1'b1}}; else if (cnt==18'h3ffff) key_sec <= key; end always @(posedge clk or negedge rst) begin if (!rst) key_sec_pre <= {N{1'b1}}; else key_sec_pre <= key_sec; end assign key_pulse = key_sec_pre & (~key_sec); endmodule
以上就是一個(gè)N位按鍵的消抖程序,如果有按鍵按下會(huì )輸出一個(gè)時(shí)鐘周期的高脈沖。下面我們可以試試用這個(gè)按鍵消抖的輸出來(lái)觸發(fā)LED的顯示,既按鍵一次LED翻轉。你也可以不加按鍵消抖試試用按鍵來(lái)控制LED(按一次變亮,再按一次滅掉)。
下面的程序是例化調用debounce模塊來(lái)控制LED
// ******************************************************************** // >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<< // ******************************************************************** // File name : top.v // Module name : top // Author : STEP // Description : // // -------------------------------------------------------------------- // Code Revision History : // -------------------------------------------------------------------- // Version: |Mod. Date: |Changes Made: // V1.0 |2017/03/02 |Initial ver // -------------------------------------------------------------------- // Module Function:進(jìn)過(guò)按鍵消抖后控制led顯示翻轉 module top (clk,rst,key,led); input clk; input rst; input key; output reg led; wire key_pulse; //當按鍵按下時(shí)產(chǎn)生一個(gè)高脈沖,翻轉一次led always @(posedge clk or negedge rst) begin if (!rst) led <= 1'b1; else if (key_pulse) led <= ~led; else led <= led; end //例化消抖module,這里沒(méi)有傳遞參數N,采用了默認的N=1 debounce u1 ( .clk (clk), .rst (rst), .key (key), .key_pulse (key_pulse) ); endmodule
====引腳分配====
設置好復位鍵可消抖的按鍵,編譯完成后下載,通過(guò)按鍵就可以翻轉LED。你也可以定義多個(gè)按鍵控制多個(gè)LED,還可以比較不加按鍵消抖情況下實(shí)際的效果對比如何。
信號 | 引腳 |
---|---|
clk | J5 |
rst | J9 |
key | K14 |
led | N15 |
評論