按鍵控制LED-ISE操作工具
本系列將帶來FPGA的系統性學習,從最基本的數位電路基礎開始,最詳細操作步驟,最直白的言語描述,手把手的“傻瓜式”講解,讓電子、資訊、通訊類專業學生、初入職場小白及打算進階提升的職業開發者都可以有系統性學習的機會。
系統性的掌握技術開發以及相關要求,對個人就業以及職業發展都有著潛在的幫助,希望對大家有所幫助。後續會陸續更新 Xilinx 的 Vivado、ISE 及相關操作軟體的開發的相關內容,學習FPGA設計方法及設計思想的同時,實操結合各類操作軟體,會讓你在技術學習道路上無比的順暢,告別技術學習小BUG卡破腦殼,告別目前忽悠性的培訓誘導,真正的去學習去實戰應用。話不多說,上貨。
按鍵控制LED-ISE操作工具
作者:李西銳校對:陸輝
利用按鍵控制LED的要求為:按一下按鍵,改變一下LED的狀態。按鍵按一次,LED由熄滅變為點亮,按鍵再按一次,LED由點亮變為熄滅。
- 硬體介紹
開發板上面有四個按鍵,當按鍵按下時,將對應的網路置成低電平;當按鍵釋放時,將對應的網路置成高電平。
開發板上面有四個LED發光二極體,FPGA輸出高電平時,LED點亮;FPGA輸出低電平時,LED熄滅。
- 設計原理
通常的按鍵所用開關為機械彈性開關,當機械觸點斷開、閉合時,由於機械觸點的彈性作用,一個按鍵開關在閉合時不會馬上穩定地接通,在斷開時也不會一下子斷開。因而在閉合及斷開的瞬間均伴隨有一連串的抖動。
按鍵抖動會引起一次按鍵被誤讀多次。為確保CPU對鍵的一次閉合僅作一次處理,必須去除鍵抖動。在鍵閉合穩定時讀取鍵的狀態,並且必須判別到鍵釋放穩定後再作處理。
抖動時間的長短由按鍵的機械特性決定,一般為5ms~10ms。這是一個很重要的時間引數,在很多場合都要用到。按鍵穩定閉合時間的長短則是由操作人員的按鍵動作決定的,一般為零點幾秒至數秒。
我們可以在按鍵和主控裝置之間加入消抖電路(消抖晶片、電容等),此種方法會增大PCB面積和花費一定的物料費用。大多數的板子直接將按鍵和主控裝置相連線,將帶有抖動的波形輸入到主控裝置內部,由內部進行消抖處理。
微控制器一般採用延遲重取樣的方式進行消抖。當檢測到訊號為低時,延遲一段時間(一般為20ms),再次檢測訊號是否為低,如果為低,則證明按鍵按下,否則認為按鍵沒有按下,繼續下一次檢查。
在FPGA設計時,筆者推薦另外一種方式:持續取樣。當檢測到訊號持續為低10ms,認為按鍵按下;當檢測到訊號持續為高10ms,認為按鍵釋放。
在設計時,需要考慮到外部的按鍵訊號為非同步訊號,需要進行同步處理。具體請參考附錄2 FPGA中的同步訊號、非同步訊號和亞穩態。
每次按鍵按下的時間的長短不一,經過消抖後,低電平的持續長度長短也不一樣。此長度遠遠大於一個時鐘週期的長度。要求每次按下只能夠切換一次LED的狀態,所以不能夠直接用此電平當做輸出翻轉的使能。
經過消抖的波形,每次按下只有一個下降沿(按鍵按下時)、只有一個上升沿(按鍵釋放時)。所以通過檢測下降沿(上升沿)的變化,產生一個新的訊號------脈衝(一個時鐘週期的脈衝),利用此脈衝作為翻轉的使能即可。利用檢測到下降沿的脈衝翻轉時,LED的狀態會在按下時就會改變;利用檢測到上升沿的脈衝翻轉時,LED的狀態會在釋放時發生改變。本設計中採用檢測到下降沿的脈衝進行翻轉。
- 設計架構和訊號說明
本設計模組命名為key_led。
在設計中,共分為三個模組。
key_filter(按鍵消抖模組):將外部輸入的帶有抖動的波形進行消抖。
edge_check(邊沿檢測模組):將消抖後的波形進行下降沿檢測,併產生對應的脈衝。
led_ctrl(led控制模組):利用脈衝,翻轉led的輸出狀態。
- key_filter設計實現
本設計採用狀態機實現,狀態機的具體原理請參看相關文章。
對key_n訊號為非同步訊號,需要進行同步兩拍,命名為key_n_r和key_n_rr。狀態機的判斷訊號為key_n_rr訊號。
本設計共分為四個狀態,KEY_OFF(按鍵釋放狀態),SHAKE_ON(按鍵按下時抖動判斷狀態),KEY_ON(按鍵按下狀態),SHAKE_OFF(按鍵釋放時抖動判斷狀態)。
按鍵沒有按下時,一直KEY_OFF狀態,當按鍵訊號變為低電平時,就轉入SHAKE_ON狀態,檢測低電平的持續時間。如果持續時間沒有達到T_10ms就變為高電平,則清零計數器並返回KEY_OFF狀態;如果持續時間沒有達到T_10ms並且也一直為低電平,則繼續在SHAKE_ON狀態計數;如果持續時間達到T_10ms並且為低電平,則清零計數器並進入KEY_ON狀態。在KEY_ON狀態,外部輸入為低電平時,則繼續在KEY_ON狀態;如果外部輸出為高電平,則轉入SHAKE_OFF狀態。在SHAKE_OFF狀態,如果持續時間沒有到達T_10ms就變為低電平,則清零計數器並返回KEY_ON狀態;如果持續時間沒有達到T_10ms並且一直為高電平,則繼續在SHAKE_OFF狀態計數;如果持續時間達到T_10ms並且一直為高電平,則清零計數器並轉入KEY_OFF狀態。
在KEY_OFF和SHAKE_ON狀態,認為按鍵沒有按下;在KEY_ON和SHAKE_OFF狀態,認為按鍵為按下;
狀態轉移圖如下:
設計程式碼為:
module key_filter (
input wire clk,
input wire rst_n,
input wire key_n,
output reg okey_n
);
parameter T_10ms = 500_000;
localparam KEY_OFF = 4'b0001;
localparam SHAKE_ON = 4'b0010;
localparam KEY_ON = 4'b0100;
localparam SHAKE_OFF = 4'b1000;
reg [3:0] c_state;
reg [3:0] n_state;
reg key_n_r;
reg key_n_rr;
reg [18:0] cnt;
always @ (posedge clk) key_n_r <= key_n;
always @ (posedge clk) key_n_rr <= key_n_r;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
c_state <= KEY_OFF;
else
c_state <= n_state;
end
always @ * begin
case (c_state)
KEY_OFF : begin
if (key_n_rr == 1'b1)
n_state = KEY_OFF;
else
n_state = SHAKE_ON;
end
SHAKE_ON : begin
if (key_n_rr == 1'b1)
n_state = KEY_OFF;
else
if (cnt < T_10ms - 1'b1)
n_state = SHAKE_ON;
else
n_state = KEY_ON;
end
KEY_ON : begin
if (key_n_rr == 1'b0)
n_state = KEY_ON;
else
n_state = SHAKE_OFF;
end
SHAKE_OFF : begin
if (key_n_rr == 1'b0)
n_state = KEY_ON;
else
if (cnt < T_10ms - 1'b1)
n_state = SHAKE_OFF;
else
n_state = KEY_OFF;
end
default : n_state = KEY_OFF;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cnt <= 19'd0;
else
case (c_state)
KEY_OFF : begin
cnt <= 19'd0;
end
SHAKE_ON : begin
if (key_n_rr == 1'b0 && cnt < T_10ms - 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= 19'd0;
end
KEY_ON : begin
cnt <= 19'd0;
end
SHAKE_OFF : begin
if (key_n_rr == 1'b1 && cnt < T_10ms - 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= 19'd0;
end
default : cnt <= 19'd0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
okey_n <= 1'b1;
else
if (c_state == KEY_ON || c_state == SHAKE_OFF)
okey_n <= 1'b0;
else
okey_n <= 1'b1;
end
endmodule
localparam可以定義引數,與parameter的區別在於,parameter定義的引數可以在例化時進行引數修改,而localparam定義的引數在例化時則不能夠修改。定義狀態機狀態時,一般採用localparam的定義方式。在不希望別人修改引數時,也可以定義為localparam。
- edge_check設計實現
在一個波形中,如果當前時刻為低電平,上一個時刻為高電平,則認為波形中有一個下降沿;如果當前時刻為高電平,上一個時刻為低電平,則認為波形中有一個上升沿。
在數位電路設計時,可以採用暫存器來儲存上一個時刻的值。
在暫存器電路中,Q的值,永遠是上一個CLK的有效邊沿所取樣的D值。因此Q為上一時刻值,而D為當前時刻的值。
設計程式碼為:
module edge_check (
input wire clk, // 50MHz
input wire wave, // wave in
output wire flag_pos, // flag - posedge
output wire flag_neg // flag - negedge
);
reg wave_r;
always @ (posedge clk) wave_r <= wave;
assign flag_pos = wave & (~wave_r);
assign flag_neg = (~wave) & wave_r;
endmodule
在設計中,註釋掉的兩行程式碼和其下方的一行程式碼的功能是相同的。例:對於上升沿脈衝來說,現在為1,過去為0即為上升沿。由於暫存器每個時鐘週期都重新整理,滿足這個要求的只會存在一個時鐘週期,所以flag_pos為一個時鐘週期的脈衝。
- led_ctrl設計實現
本模組中,利用脈衝進行led狀態的翻轉即可。
設計程式碼為:
module led_ctrl (
input wire clk,
input wire rst_n,
input wire flag,
output reg led
);
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
led <= 1'b0;
else
if (flag == 1'b1)
led <= ~led;
else
led <= led;
end
endmodule
- key_led設計實現
本模組只是負責將上述的三個模組按照架構圖的方式進行連線,形成最終的設計。
設計程式碼為:
module key_led (
input wire clk,
input wire rst_n,
input wire key_n,
output wire led
);
wire okey_n;
wire flag;
key_filter key_filter_inst(
);
edge_check edge_check_inst(
);
led_ctrl led_ctrl_inst(
endmodule
在設計中,採用了按鍵按下時的脈衝(檢測到下降沿的脈衝),按鍵按下時led的狀態即可進行翻轉。
- 功能模擬
在模擬時,將按鍵消抖中的T_10ms的引數修改為20,即持續時間不超過400ns都不認為是有效按下或者抬起。
模擬程式碼如下:
module key_led_tb;
reg clk;
reg rst_n;
reg key_n;
wire led;
key_led key_led_inst(
);
initial clk = 1'b0;
always # 10 clk = ~clk;
initial begin
rst_n = 1'b0;
key_n = 1'b1;
# 201
rst_n = 1'b1;
# 200
# 2;
key_n = 1'b0;
# 320
# 2;
key_n = 1'b1;
# 159
# 2;
key_n = 1'b0;
# 320
# 2;
key_n = 1'b1;
# 159
//--------------------------------------
# 2;
key_n = 1'b0;
# 5000
# 2;
key_n = 1'b0;
# 320
# 2;
key_n = 1'b1;
# 159
# 2;
key_n = 1'b0;
# 320
//-------------------------------------
# 2;
key_n = 1'b1;
# 10000
$stop;
end
endmodule
將okey_n、flag訊號添加出來。
通過RTL模擬圖,可以清晰的看到okey_n訊號將key_n的抖動濾除掉;flag訊號為okey_n訊號的下降沿時所產生的脈衝;led在flag訊號為高時,反正翻轉。
分配管腳、下板測試之前,應該將按鍵消抖裡面的T_10ms引數重新改為500_000,否則下板後可能會達不到消抖的效果。
下板觀察現象:
下板成功後,可以修改在設計中使用上升沿的脈衝,得到的現象應該是按鍵釋放時,LED的狀態發生反轉。
切記:每次修改程式碼,一定要進行重新編譯,否則更改將不會生效。
- End -
【福利】:QQ交流群173560979,進群備註名字+學校/企業。淘寶店鋪:https://shop588964188.taobao.com
論壇網址:www.sxznfpga.com
郝旭帥團隊製作
往期推薦