按鍵是輸入裝置,一般來說,按鍵在沒有按下的時候是高電平;當按鍵按下的時候,為低電平。
在DE2-70 User Manual中
Each switch provides a high logic level (3.3 volts) when it is not pressed, and provides a low logic level (0 volts) when depressed. Since the pushbutton switches are debounced, they are appropriate for use as clock or reset inputs in a circuit.
這裡介紹到了按鍵抖動(Button Bouncing)和按鍵消抖(Button Debouncing)。
按鍵消抖通常的按鍵所用開關為機械彈性開關,當機械觸點斷開、閉合時,由於機械觸點的彈性作用,一個按鍵開關在閉合時不會馬上穩定地接通,在斷開時也不會一下子斷開。因而在閉合及斷開的瞬間均伴隨有一連串的抖動,為了不產生這種現象而作的措施就是按鍵消抖。
上圖描述的是硬體的按鍵消抖。可見,消抖後,一次按鍵,只產生了一次電平變化。
這對我們接下來利用按鍵意義重大。
簡單介紹下實現上圖的消抖。
將電容並聯在按鍵的兩端,利用電容的放電的延時特性。將產生抖動的電平通過電容吸收掉。從而達到消抖的作用。
利用RS觸發器來吸收按鍵的抖動。一旦有鍵按下,觸發器立即翻轉,觸電的抖動便不會再對輸出產生影響。
按鍵在FPGA中必不可少,我們需要利用按鍵對一些變數進行累加或累減。比如頻率、分數等。
在一開始的學習中,我們可能只用到了開關(switch),編寫開關控制的程式類似於將開關當作一個布林(Boolean)變數。
但是按鍵與開關不同,以下面這段程式碼為例,期望作用是 按鍵按下後,LED燈狀態改變。
module Test(
input sys_clk,
input rst_n,
input key,
output reg led
);
always@(posedge sys_clk or negedge rst_n)
begin
if(rst_n == 1'b0)
led <= 1'b0;
else if(key == 1'b0)
led <= ~led;
else
led <= led;
end
endmodule
然而,上板測試發現,LED狀態燈一直處於不亮的狀態。
假設,我們的時脈頻率是50MHz,那麼它的週期就是20ns。
那麼,在你按按鈕的全過程中,按下的狀態持續了多長時間呢?
顯然,遠大於20ns,所以LED會多次取反。所以這種簡單粗暴的方法是不適合按鍵的。
我們剛剛的問題是,短時間內重複檢測,從而多次執行了按鍵後的行為。
可以想到,給這個按鍵定個時:當有按鍵按下,計數器不斷自增,若因為抖動,則計數器會清空,重新計數,當不抖動的時候,就會計滿,此時才會判定為按鍵按下。
程式碼
// 定時器消除抖動
module key_filter(
input wire sys_clk, // 50M時鐘
input wire sys_rst_n, // 復位訊號,低電平有效
input wire key, // 按鍵輸入
output reg key_flag, // 按鍵訊號有效訊號
output reg key_value // 消抖後的按鍵訊號
);
reg [31:0] delay_cnt; // 32位元定時器
reg key_reg;
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_reg <= 1'b1;
delay_cnt <= 32'd0;
end
else begin
key_reg <= key;
if(key_reg != key) // 檢測到按鍵狀態發生變化(按下 或 釋放)
delay_cnt <= 32'd1000000; // 計數器裝載初始值(計數時間為20ms)
else if(key_reg == key) begin // 按鍵狀態穩定時,計數器遞減,開始20ms倒計時
if(delay_cnt > 32'd0) // 不穩定時,重新計時
delay_cnt <= delay_cnt - 1'b1;
else
delay_cnt <= delay_cnt;
end
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin // 復位
key_flag <= 1'b0; // 無效
key_value <= 1'b1; // 高電平為未按下
end
else begin
if(delay_cnt == 32'd1) begin // 按鍵穩定狀態維持了20ms
key_flag <= 1'b1; // 此時消抖過程結束,訊號有效
key_value <= key; // 儲存此時按鍵訊號
end
else begin
key_flag <= 1'b0;
key_value <= key_value;
end
end
end
endmodule
如此,我們再重新寫下剛剛的未消抖程式。
module Test(
input sys_clk, // 系統時鐘
input rst_n, // 復位訊號,低電平有效
input key, // 按鍵訊號
output reg led // LED燈
);
wire key_value;
wire key_flag;
// 例化
key_filter key_filter_inst(
.sys_clk (sys_clk), // 50M時鐘
.sys_rst_n (sys_rst_n), // 復位訊號,低電平有效
.key (key), // 按鍵輸入)
.key_flag (key_flag), // 按鍵訊號有效訊號
.key_value (key_value) // 按鍵消抖後的資料
);
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
begin
led <= 1'b0;
end
else if(key_flag && (~key_value)) // 按鍵是否有效按下
begin
led <= ~led;
end
else
begin
led <= led;
end
end
endmodule
成功,按鍵可以控制LED燈亮與熄滅。模擬略。
這是簡單的計時消抖,讀者有興趣可閱讀學習狀態機的方法。
[1] 按鍵的硬體消抖電路原理詳解
[3] FPGA學習—按鍵控制
[4] DE2-70 User Manual
本文來自部落格園,作者:江水為竭,轉載請註明原文連結:https://www.cnblogs.com/Az1r/p/17589520.html