相關(guān)閱讀?
FPGA開發(fā)專欄https://blog.csdn.net/weixin_45791458/category_12388695.html?spm=1001.2014.3001.5482
? ? ? ? FPGA開發(fā)板上的蜂鳴器可以用來播放音樂,只需要控制蜂鳴器信號(hào)的方波頻率、占空比和持續(xù)時(shí)間即可。
1、簡(jiǎn)譜原理
? ? ? ? 簡(jiǎn)譜上的4/4表示該簡(jiǎn)譜以4分音符為一拍,每小節(jié)4拍,簡(jiǎn)譜上應(yīng)該也會(huì)標(biāo)注每分鐘多少拍。音符時(shí)值對(duì)照表如下圖所示,這表示了每個(gè)音符的演奏時(shí)長(zhǎng)。
????????音符是記錄音的高低和長(zhǎng)短的符號(hào),簡(jiǎn)譜中的音符是七個(gè)阿拉伯?dāng)?shù)字,它們是:1(Do)、2(Re)、3(Mi)、4(Fa)、5(Sol)、6(La)、7(Ti),為了標(biāo)記更高或更低的音,則在基本符號(hào)的上面或下面加上小圓點(diǎn)。在簡(jiǎn)譜中,不帶點(diǎn)的基本符號(hào)叫中音。記在簡(jiǎn)譜基本音符號(hào)下面的小圓點(diǎn),叫低音點(diǎn),它表示將基本音符降低一個(gè)音組,即降低一個(gè)純八度。在基本符號(hào)下面加一個(gè)點(diǎn)叫低音,加兩個(gè)點(diǎn)叫倍低音,加三個(gè)點(diǎn)叫超低音。記在簡(jiǎn)譜基本音符號(hào)上面的小圓點(diǎn),叫高音點(diǎn),它表示將基本音符升高一個(gè)音組,即升高一個(gè)純八度。在基本符號(hào)上面加一個(gè)點(diǎn)叫高音,加兩個(gè)點(diǎn)叫倍高音,加三個(gè)點(diǎn)叫超高音。
? ? ? ? 音符所對(duì)應(yīng)的頻率如下表所示。
音符 | 頻率 |
低音1 | 261Hz |
低音2 | 293Hz |
低音3 | 329Hz |
低音4 | 349Hz |
低音5 | 392Hz |
低音6 | 440Hz |
低音7 | 499Hz |
中音1 | 523Hz |
中音2 | 587Hz |
中音3 | 659Hz |
中音4 | 698Hz |
中音5 | 784Hz |
中音6 | 880Hz |
中音7 | 998Hz |
高音1 | 1046Hz |
高音2 | 1174Hz |
高音3 | 1318Hz |
高音4 | 1396Hz |
高音5 | 1568Hz |
高音6 | 1760Hz |
高音7 | 1976Hz |
2、結(jié)構(gòu)設(shè)計(jì)
2.1、按鍵消抖模塊
? ? ? ? 由于要是用按鍵控制音樂開始播放,所以需要一個(gè)按鍵消抖模塊,具體可以在FPGA開發(fā):按鍵消抖一文中找到。
Debounce debounce_0
(
.clk (clk),
.rst (rst_n),
.button_in (button_in),
.button_out (button_out)
);
? ? ? ? 同時(shí)我們還需要一個(gè)邊沿檢測(cè)的機(jī)制來保證一次按下只觸發(fā)一次按鍵操作。
always @ (posedge clk or posedge rst)begin
if(rst == 1'b1)begin
button_out_d0 <= 1'b1;
button_negedge <= 1'b0;
end
else begin
button_out_d0 <= button_out;
button_negedge <= button_out_d0 & ~button_out;
end
end
2.2、ROM模塊
? ? ? ??使用ROM保存音符時(shí)長(zhǎng)和音調(diào),創(chuàng)建ROM的過程可以根據(jù)不同的FPGA開發(fā)環(huán)境而定,如果是Quartus的話步驟如下:
? ? ? ? 首先新建兩個(gè)個(gè)MIF文件,它們是用來初始化ROM的,如下圖所示。
? ? ? ? ?根據(jù)你的簡(jiǎn)譜長(zhǎng)度,設(shè)置深度,如下圖所示。
? ? ? ? 隨后根據(jù)簡(jiǎn)譜填入對(duì)應(yīng)信息并保存,如下圖所示。?
? ? ? ? 接著在IP窗口搜索ROM IP,如下圖所示。
? ? ? ? ?選好模塊名和HDL類型并保存,這里選擇Verilog HDL,如下圖所示。
? ? ? ? 在ROM創(chuàng)建菜單中選擇創(chuàng)建的ROM大?。ㄟ@里應(yīng)該要和剛才的MIF文件一致),如下圖所示。
? ? ? ? 在初始化界面,選擇使用剛才創(chuàng)建的MIF文件并Finish即可完成ROM的創(chuàng)建,如下圖所示。
2.3、頻率譯碼模塊
? ? ? ? ?規(guī)定中音1使用十進(jìn)制數(shù)11表示,而低音1使用01表示,中音2使用12表示。譯碼模塊根據(jù)對(duì)應(yīng)的音符頻率,輸出相應(yīng)的周期,其中CLK_FRE根據(jù)開發(fā)板的頻率而定。
module music_hz(
input [7:0] hz_sel,
output reg [19:0] cycle
);
parameter CLK_FRE = 50 ;
always @(*)begin
case(hz_sel)
8'h01 : cycle = CLK_FRE*1000000/261 ; //low 1 261Hz
8'h02 : cycle = CLK_FRE*1000000/293 ; //low 2 293Hz
8'h03 : cycle = CLK_FRE*1000000/329 ; //low 3 329Hz
8'h04 : cycle = CLK_FRE*1000000/349 ; //low 4 349Hz
8'h05 : cycle = CLK_FRE*1000000/392 ; //low 5 392Hz
8'h06 : cycle = CLK_FRE*1000000/440 ; //low 6 440Hz
8'h07 : cycle = CLK_FRE*1000000/499 ; //low 7 499Hz
8'h11 : cycle = CLK_FRE*1000000/523 ; //middle 1 523Hz
8'h12 : cycle = CLK_FRE*1000000/587 ; //middle 2 587Hz
8'h13 : cycle = CLK_FRE*1000000/659 ; //middle 3 659Hz
8'h14 : cycle = CLK_FRE*1000000/698 ; //middle 4 698Hz
8'h15 : cycle = CLK_FRE*1000000/784 ; //middle 5 784Hz
8'h16 : cycle = CLK_FRE*1000000/880 ; //middle 6 880Hz
8'h17 : cycle = CLK_FRE*1000000/998 ; //middle 7 998Hz
8'h21 : cycle = CLK_FRE*1000000/1046 ; //high 1 1046Hz
8'h22 : cycle = CLK_FRE*1000000/1174 ; //high 2 1174Hz
8'h23 : cycle = CLK_FRE*1000000/1318 ; //high 3 1318Hz
8'h24 : cycle = CLK_FRE*1000000/1396 ; //high 4 1396Hz
8'h25 : cycle = CLK_FRE*1000000/1568 ; //high 5 1568Hz
8'h26 : cycle = CLK_FRE*1000000/1760 ; //high 6 1760Hz
8'h27 : cycle = CLK_FRE*1000000/1976 ; //high 7 1976Hz
default : cycle = 20'd0 ;
endcase
end
endmodule
2.4、狀態(tài)機(jī)演奏模塊
? ? ? ? 狀態(tài)機(jī)設(shè)有四個(gè)狀態(tài),IDLE,PLAY,PLAY_WAIT和PLAY_END,其中PLAY狀態(tài)使用一個(gè)計(jì)數(shù)器對(duì)每個(gè)音符的演奏時(shí)長(zhǎng)進(jìn)行計(jì)數(shù),PLAY_WAIT用于檢查是否全部音符演奏完畢,如果否,則會(huì)對(duì)演奏時(shí)長(zhǎng)計(jì)數(shù)器清零并再次進(jìn)入PLAY狀態(tài)。
always @(*)begin
case(state)
IDLE:begin
if (button_negedge)
next_state = PLAY;
else
next_state = IDLE;
end
PLAY:begin
if (play_cnt == music_time)
next_state = PLAY_WAIT;
else
next_state = PLAY;
end
PLAY_WAIT:begin
if (music_cnt == music_len - 1)
next_state = PLAY_END;
else
next_state = PLAY;
end
PLAY_END:next_state = IDLE;
default:next_state = IDLE;
endcase
end
? ? ? ? 周期計(jì)數(shù)器用于對(duì)音符的每個(gè)周期進(jìn)行計(jì)數(shù),并提供計(jì)數(shù)值給輸出信號(hào)模塊。
always @(posedge clk or negedge rst_n)begin
if (~rst_n)
hz_cnt <= 20'd0;
else if (state == PLAY || state == PLAY_WAIT)begin
if (hz_cnt == cycle - 1)
hz_cnt <= 20'd0;
else
hz_cnt <= hz_cnt + 1'b1;
end
else
hz_cnt <= 20'd0;
end
????????輸出信號(hào)模塊根據(jù)計(jì)數(shù)值輸出信號(hào),其中還可以控制占空比。
always @(posedge clk or negedge rst_n)begin
if (~rst_n)
buzzer <= 1'b1;
else if (state == PLAY || state == PLAY_WAIT)begin
if (hz_cnt < cycle/32) //控制占空比
buzzer <= 1'b0;
else
buzzer <= 1'b1;
end
else if (state == IDLE || state == PLAY_END)
buzzer <= 1'b1;
end
????????演奏時(shí)長(zhǎng)計(jì)數(shù)器用于對(duì)每個(gè)音符的演奏時(shí)間計(jì)數(shù)。
always @(posedge clk or negedge rst_n)begin
if (~rst_n)
play_cnt <= 32'd0;
else if (state == PLAY)
play_cnt <= play_cnt + 1'b1;
else
play_cnt <= 32'd0;
end
????????演奏個(gè)數(shù)計(jì)數(shù)器用于對(duì)演奏的音符數(shù)計(jì)數(shù)。文章來源:http://www.zghlxwxcb.cn/news/detail-625747.html
always @(posedge clk or negedge rst_n)begin
if (~rst_n)
music_cnt <= 32'd0;
else if (state == PLAY_WAIT)
music_cnt <= music_cnt + 1'b1;
else if (state == IDLE || state == PLAY_END)
music_cnt <= 32'd0;
end
? ? ? ? 最后實(shí)例化ROM,并且注意,這里規(guī)定演奏時(shí)長(zhǎng)rom值以8為一拍,所以讀取rom值后需要進(jìn)行轉(zhuǎn)換,假設(shè)一分鐘85拍。文章來源地址http://www.zghlxwxcb.cn/news/detail-625747.html
music_hz hz0
(
.hz_sel(rom_hz_data),
.cycle(cycle)
) ;
music_rom hz_rom
(
.address(music_cnt[8:0]),
.clock(clk),
.q(rom_hz_data)
);
music_time_rom time_rom
(
.address(music_cnt[8:0]),
.clock(clk),
.q(rom_time_data)
);
always @(posedge clk or negedge rst_n)begin
if (~rst_n)
music_time <= 32'hffff_ffff;
else
music_time <= rom_time_data*(CLK_FRE*1000000*60/85/8);
end
到了這里,關(guān)于FPGA開發(fā):音樂播放器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!