設計規劃
驅動無源蜂鳴器進行七個基本音調“哆來咪發梭拉西”的循環鳴叫,每個音階持續鳴叫0.5s后鳴叫下一個音階。
對于無源蜂鳴器,輸入不同的PWM方波就能發出不同的聲音。方波的頻率影響音調,占空比影響音量。音調頻率對應表格如下,占空比保持為50%。因此這個實驗我們只要循環產生占空比為50%的七個音調頻率即可。
Do | Re | Mi | Fa | So | La | Si |
---|---|---|---|---|---|---|
262 | 294 | 330 | 349 | 392 | 440 | 494 |
現在我們考慮,除了時鐘和復位,需要幾個計數器:
1、蜂鳴維持0.5s,(50MHz對應20ns)計數從0-24999999,計數器名稱cnt。
2、需要蜂鳴7個調,狀態計數從0-6,cnt計滿一次就+1,計數器名稱cnt_500ms。
3、不同的調對應不同頻率,需要一個頻率計數器freq_cnt。
以Do為例,頻率為262,周期為1/262=3.816794ms。時鐘頻率50MHz,對應周期是20ns,即計數個數為190840個,對應do的freq_cnt計數從0-190839。占空比為50%,即高電平和低電平的時長一樣,高電平持續時鐘脈沖個數為95420。
頻率 | 262 | 294 | 330 | 349 | 392 | 440 | 494 |
---|---|---|---|---|---|---|---|
頻率數值 | 190840 | 170068 | 151515 | 143266 | 127551 | 113636 | 101214 |
占空比數值 | 95420 | 85034 | 75757 | 71633 | 63775 | 56818 | 50607 |
以Re為例的波形圖:
改變頻率數值freq_data和占空比計數duty_data,就可以得到不同頻率的波形。
編寫代碼
module beep
#(
parameter TIME_500MS = 25'd24999, //0.5s計數值
parameter DO = 18'd190 , //Do頻率262
parameter RE = 18'd170 , //Re頻率294
parameter MI = 18'd151 , //Mi頻率330
parameter FA = 18'd143 , //Fa頻率349
parameter SO = 18'd127 , //So頻率392
parameter LA = 18'd113 , //La頻率440
parameter XI = 18'd101 //Si頻率494
)
(
input wire sys_clk ,
input wire sys_rst_n ,
output reg beep
);
//reg define
reg [24:0] cnt ; //0.5s計數器
reg [17:0] freq_cnt ; //音調計數器
reg [2:0] cnt_500ms ; //0.5s個數計數
reg [17:0] freq_data ; //音調分頻計數值,取不同的DO,RE...可以得到不同頻率的波形
//wire define
wire [16:0] duty_data ; //占空比計數值,即DO,RE...的一半
assign duty_data = freq_data > > 1'b1; //二進制右移一位就是原值的1/2
//cnt:0.5s循環計數器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'd0;
else if(cnt == TIME_500MS )
cnt <= 25'd0;
else
cnt <= cnt + 1'b1;
//cnt_500ms:對500ms個數進行計數,每個音階鳴叫時間0.5s,7個音節一循環
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_500ms <= 3'd0;
else if(cnt == TIME_500MS && cnt_500ms == 6)
cnt_500ms <= 3'd0;
else if(cnt == TIME_500MS)
cnt_500ms <= cnt_500ms + 1'b1;
//不同時間鳴叫不同的音階
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq_data <= DO;
else case(cnt_500ms)
0: freq_data <= DO;
1: freq_data <= RE;
2: freq_data <= MI;
3: freq_data <= FA;
4: freq_data <= SO;
5: freq_data <= LA;
6: freq_data <= XI;
default: freq_data <= DO;
endcase
//freq_cnt:當計數到音階計數值或跳轉到下一音階時,開始重新計數
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq_cnt <= 18'd0;
else if(freq_cnt == freq_data || cnt == TIME_500MS)
freq_cnt <= 18'd0;
else
freq_cnt <= freq_cnt + 1'b1;
//beep:輸出蜂鳴器波形
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
beep <= 1'b0;
else if(freq_cnt >= duty_data)
beep <= 1'b1;
else
beep <= 1'b0;
endmodule