FPGA实战篇——【3】按键控制蜂鸣器
FPGA實戰——按鍵控制蜂鳴器
目錄
- FPGA實戰——按鍵控制蜂鳴器
- 實驗任務:
- 蜂鳴器
- 硬件設計
- 程序設計
- rtl文件
- 按鍵消抖
- ucf文件
- 編譯
- RTL圖
- 補充——例化模塊 的軟件操作:
- 下載及debug
- 仿真
實驗任務:
復位后蜂鳴器發聲,按下按鍵后停止發聲,再次按下繼續發聲。
蜂鳴器
有源無源的判斷:
1.將蜂鳴器引腳朝上,可以看出有綠色電路板的一種是無源蜂鳴器,沒有電路板而用黑膠封閉的一種是有源蜂鳴器。
2.萬用表電阻檔Rxl檔: 用黑表筆接蜂鳴器 "+"引腳,紅表筆在另一引腳上來回碰觸,如果觸發出咔、咔聲的且電阻只有8Ω(或16Ω)的是無源蜂鳴器;如果能發出持續聲音的,且電阻在幾百歐以上的,是有源蜂鳴器。
本次實驗使用有源蜂鳴器
硬件設計
當BUZZER引腳輸出低電平時,PNP導通,蜂鳴器發聲工作。
程序設計
rtl文件
這次我們用到了按鍵消抖,需要單獨寫一個模塊比較方便,所以這節開始,我們就開始用多個.V文件,然后寫頂層模塊調用例化的底層模塊這種寫法了。回顧一下例化的知識(偽代碼):
比如我已經定義了 <底層led模塊> module pled(input sys_clk,output led_value ); 那么我在頂層模塊例化他的時候: <底層模塊> 首先定義頂層模塊: module top_flow_led(input sys_clk,output led ); 例化led模塊時: pled pled_u(.sysclk(sys_clk),.led_value(led) );在例化模塊中,.sysclk后的()表示的是輸入,也就是說,因為在原來的pled模塊中,sysclk是input,所以()中的變量為一個輸入,現在,只需要將開發板上的晶振連接到頂層文件的input sysclk,然后這個頂層文件的sysclk再輸入到led 的sysclk就實現了對底層模塊的時鐘的賦值。***********************而底層模塊pled中 led_value是個output,所以這里.led_value(led)中led是作為led_value的輸出接收信號,接收底層模塊的輸出,然后通過頂層模塊,連接到開發板的LED燈上。***********************這就是通過例化連接到頂層模塊的寫法。具體后面的例子變量多,看起來可能更容易理解。按鍵消抖
思想:當檢測到按鍵值發生變化,也就是被按下時,將按鍵值保存,保存之后,每隔一段時間取按鍵值與原來值進行比較,如果持續20ms不變,就認為按鍵確實按下了。
(實際上,只需要檢測到按鍵按下之后,啟動計數器(不管怎么抖,都以變化后為計時起點重新計時),只要計數器在計數到20ms之前 ,按鍵值沒有發生變化,就認為按鍵被按下)
那么我們寫一個按鍵消抖模塊,輸入key(只用一個按鍵)輸出一個key_flag表示按鍵被按下的標志,再輸出一個key_value來存儲key的值。
(一些疑問在最后的 下載與debug解決部分解決)
這里有一點需要講解一下:
我們在key_reg <= key;之后,馬上就判斷key_reg和key是否相等,而且FPGA是并行的,這又是非阻塞賦值,那這不是肯定相等嗎,其實不然。
always語句塊是在posedge sys_clk的時候執行,其他時候不執行,而key_reg <= key; 這句雖然執行了,但是并不是馬上把key的值給key_reg,而是需要等下一個clk上升沿才真正賦值,此時key_reg是上一clk的值,key就是實時的按鍵值,此時可以進行比較。
發現頂層模塊并不是top_key_beep,所以我們設置一下,
如果設置完之后沒有變化,說明我們的代碼有錯誤,一定要好好檢查。比如endmodule會忘記加。(如果代碼添加進ise之后發現全編譯的綠色按鈕灰色,下方工具欄很多沒有顯示,也可能使endmodule未加)
這樣就OK了。
ucf文件
NET sys_clk TNM_NET = sys_clk_pin; TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 20ns HIGH 50%;NET sys_clk LOC = T8 | IOSTANDARD = LVCMOS33; NET sys_rst_n LOC = L3 | IOSTANDARD = LVCMOS33;################## KEY ############################ NET key LOC = C3 | IOSTANDARD = LVCMOS33;################## BEEP ############################ NET beep LOC = J11 | IOSTANDARD = LVCMOS33;編譯
編譯報錯:
Line 29: Target of concurrent assignment or output port connection should be a net type.
原因是:
例化時輸出必須為wire類型的!!!!!
RTL圖
上次我們用了第一種,是需要我們手動添加的,這次我們用第二個,就不需要手動添加了。
首先是這樣的,但是我們還想看內部的圖,那么我們可以雙擊這個rtl圖,就可以看到內部
補充——例化模塊 的軟件操作:
首先兩個底層模塊加入進ISE,然后創建一個頂層模塊。接下來,對于底層模塊的例化,我們可以
將這段代碼復制進頂層文件即可。
下載及debug
生成bit流文件,就可以下載進開發板了。
發現不能按一下就一直響,響了按一下不能停,問題出在下圖那
我們先分析下邏輯,如果計數器從1百萬減到1了,就可以把flag變成1,此時讓key_value等于0,就是按下了。但是我們忽略了一點,就是按鍵松開的時候,也需要消抖!!!!那這時候,是不是消抖完還讓他等于0呢,不是應該是等于穩定后的key值也就是把這里改成:key_value <=key;
如果按我們剛才那種,那沒錯,按下去是正常的,但是松開的時候,因為我們flag =1 ,但是按鍵值key_vlaue卻還是0,此時,在下圖的邏輯中相當于又按下了一次!!!!
另外,這個bug 也解決了了另外一個問題,就是為什么key_flag只持續了一個clk周期,不能一直等于1,這樣對于后面的按鍵松開消抖不利,實驗證明,這樣按鍵會及其不靈敏!!
仿真
tb很簡單,思想就是模擬按鍵抖動即可。
`timescale 1ns / 1nsmodule tb_top_key_beep;// Inputsreg sys_clk;reg sys_rst_n;reg key;// Outputswire beep;// Instantiate the Unit Under Test (UUT)top_key_beep u_top_key_beep (.sys_clk(sys_clk), .sys_rst_n(sys_rst_n), .key(key), .beep(beep));initial begin// Initialize Inputssys_clk <= 1'b0;sys_rst_n <= 1'b0;key <= 1'b1;// Wait 100 ns for global reset to finish#100;sys_rst_n <= 1'b1;// Add stimulus here#150 key <= 1'b0; // 在第250ns按下按鍵#1_000_000 key <= 1'b1; //模擬按鍵抖動1ms#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#21_000_000 key <= 1'b1; //20ms之后松開按鍵#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#17_000_000 key <= 1'b0; //開始按下#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#17_000_000 key <= 1'b1; //松開#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#1_000_000 key <= 1'b0;#1_000_000 key <= 1'b1;#20_000_000 key <= 1'b0; //松開end//生成時鐘 always #10 sys_clk = ~sys_clk; endmodule模擬了100ms之后的結果,我設置的是按鍵按下的延遲先20ms,后兩個17ms,再一個20ms,可以看到符合我們的預期,之后前后兩個才改變了beep的狀態。
總結
以上是生活随笔為你收集整理的FPGA实战篇——【3】按键控制蜂鸣器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑屏幕变黑白
- 下一篇: 易维帮助台让IT服务从部门级应用到企业级