日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

FPGA实战篇——【3】按键控制蜂鸣器

發布時間:2024/1/18 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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解決部分解決)

**************按鍵消抖模塊************** module key_debounce( //消抖模塊input sys_clk,input sys_rst_n,input key,output reg key_value,output reg key_flag);reg key_reg; //存儲key的值,相當于temp的作用 reg [19:0] delay_cnt; //計數器,計數20ms內key未變即按鍵被按下 20ms / 20ns = 100_0000,20位//按鍵延時計數器 always @ (posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginkey_reg <= 1'b1; //1為未按下delay_cnt <= 20'd0; //計數器值清零endelse beginkey_reg <= key; //把key的值存儲到寄存器中去if(key_reg != key) //說明按鍵值有變化!!!!!(不管怎么抖,都以變化后為計時起點重新計時) delay_cnt <= 20'd100_0000; //一旦檢測到key值變化,就把計數器設到倒計時最大值。else if(key_reg == key)begin //說明此時按鍵還是按下的狀態if(delay_cnt > 20'd0)delay_cnt <= delay_cnt - 1'b1;else //else 它要等于自己!!!!!delay_cnt <= delay_cnt;endend end//按鍵抖動判斷 always @ (posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginkey_value <= 1'b1;key_flag <= 1'b0;endelse if(delay_cnt == 20'd1) begin //從100_0000到了1,說明持續了20mskey_flag <= 1'b1;key_value <= key; //寄存此時的按鍵值endelse begin //計數器減到0key_flag <= 1'b0; //這是個flag 標志位,不需要一直為1,只需要一個周期即可。key_value <= key_value;end end endmodule

這里有一點需要講解一下:

我們在key_reg <= key;之后,馬上就判斷key_reg和key是否相等,而且FPGA是并行的,這又是非阻塞賦值,那這不是肯定相等嗎,其實不然。
always語句塊是在posedge sys_clk的時候執行,其他時候不執行,而key_reg <= key; 這句雖然執行了,但是并不是馬上把key的值給key_reg,而是需要等下一個clk上升沿才真正賦值,此時key_reg是上一clk的值,key就是實時的按鍵值,此時可以進行比較。

**************蜂鳴器模塊************** module beep_ctrl(input sys_clk,input sys_rst_n,input key_value,input key_flag,output reg beep );always @ (posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beep <= 1'b0;else if(key_flag && (~key_value))beep <= ~beep; endendmodule **************頂層模塊************** module top_key_beep(input sys_clk,input sys_rst_n,input key,output beep);//只需要作為導線把兩個模塊連接到一起即可,不需要是reg型 wire key_value; wire key_flag; key_debounce key_debounce_u(.sys_clk (sys_clk) ,.sys_rst_n (sys_rst_n), .key (key) ,.key_value (key_value), .key_flag (key_flag) );beep_ctrl beep_ctrl_u(.sys_clk (sys_clk) ,.sys_rst_n (sys_rst_n) ,.key_value (key_value) ,.key_flag (key_flag) ,.beep (beep) ); endmodule


發現頂層模塊并不是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】按键控制蜂鸣器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。