相信大家已經(jīng)在網(wǎng)上見(jiàn)識(shí)過(guò)很多“民間大神”們的天才(奇葩)手工DIY了,一些簡(jiǎn)簡(jiǎn)單單的材料在他們的搗鼓下,瞬間華麗轉(zhuǎn)身,變廢為寶。今天,我們?yōu)榇蠹艺砹艘晃皇止ご笊竦?a target="_blank">DIY心得——如何利用FPGA開(kāi)發(fā)板DIY一個(gè)音樂(lè)盒,也就是如何使我們的FPGA播放聲音和音樂(lè)!
首先需生成單個(gè)音調(diào),然后慢慢地做更有趣的事情,如制作警報(bào)聲和播放曲調(diào)。
需要準(zhǔn)備的硬件
該項(xiàng)目使用的是一個(gè)Pluto的FPGA開(kāi)發(fā)板,還有一個(gè)揚(yáng)聲器和一個(gè)1kΩ的電阻。
更加直觀(guān)的展示是下面這樣的:
本次DIY的音樂(lè)盒分為4個(gè)部分:
1.簡(jiǎn)單的嗶嗶聲
2.警報(bào)聲
3.音調(diào)
4.曲子
1.簡(jiǎn)單的嗶嗶聲
FPGA可以輕松實(shí)現(xiàn)二進(jìn)制計(jì)數(shù)器。例如,在回滾之前,16位計(jì)數(shù)器將從0到65535(65536個(gè)不同的值)計(jì)數(shù)。Pluto板具有一個(gè)25MHz的時(shí)鐘振蕩器,因此我們可以輕松構(gòu)建一個(gè)25MHz時(shí)鐘16位自動(dòng)計(jì)數(shù)器。其最高位切換頻率為25000000/65536 = 381Hz。
其VerilogHDL的代碼如下:
modulemusic(clk,speaker);
inputclk;
outputspeaker;
// 首先創(chuàng)建一個(gè)16位二級(jí)制計(jì)數(shù)器
reg[15:0]counter;
always@(posedgeclk)counter<=counter+1;
// 使用計(jì)數(shù)器的最高有效位來(lái)驅(qū)動(dòng)揚(yáng)聲器
assignspeaker=counter[15];
endmodule
詳細(xì)講解的話(huà)就是,“clk”在25MHz下運(yùn)行,“counter [0]”看起來(lái)像是一個(gè)12.5MHz信號(hào)(它以25MHz的頻率進(jìn)行更新變化,變化的值為0 1 0 1 ......因此看起來(lái)像12.5MHz信號(hào)),“counter [1]“是6.125MHz信號(hào),依此類(lèi)推。
由于我們使用計(jì)數(shù)器的最高有效位(第15位)來(lái)驅(qū)動(dòng)輸出,因此“揚(yáng)聲器”輸出會(huì)產(chǎn)生一個(gè)完美的381Hz方波信號(hào)。
2.警報(bào)聲
我們可以在兩個(gè)音調(diào)之間循環(huán)。我們首先用一個(gè)24位計(jì)數(shù)器的“音調(diào)”來(lái)產(chǎn)生一個(gè)較慢的方波。其MSB(最高有效位)“tone[23]”是一1.5Hz的頻率進(jìn)行變換的。
然后我們就可以用這個(gè)最高有效位切換到另一個(gè)計(jì)數(shù)器,從而在兩個(gè)頻率之間切換。
代碼如下:
modulemusic(clk,speaker);
inputclk;
outputspeaker;
parameterclkdivider=25000000/440/2;
reg[23:0]tone;
always@(posedgeclk)tone<=tone+1;
reg[14:0]counter;
always@(posedgeclk)if(counter==0)counter<=(tone[23]?clkdivider-1:clkdivider/2-1);elsecounter<=counter-1;
regspeaker;
always@(posedgeclk)if(counter==0)speaker<=~speaker;
endmodule
3.演奏音符
現(xiàn)在我們想要演奏一首曲子,所以需要來(lái)獲取不同的音符,就像電子琴一樣。
如果我們用6位來(lái)實(shí)現(xiàn)音符,那么我可以獲得64個(gè)音符。每個(gè)八度一共有12個(gè)音符,所以64個(gè)音符可以實(shí)現(xiàn)5個(gè)八度,完全足夠彈奏一首曲目了。
步驟1
要想實(shí)現(xiàn)一組音調(diào)不斷升高的聲音,我們用一個(gè)28位計(jì)數(shù)器來(lái)距離,提取其中6個(gè)MSB,從而給到6位的音調(diào)。
代碼如下:
reg[27:0]tone;
always@(posedgeclk)tone<=tone+1;
wire[5:0]fullnote=tone[27:22];
在25MHz的時(shí)鐘下,每個(gè)音符持續(xù)167ms,64個(gè)音符一共需要10.6s才能演奏完成。
步驟2
我們將“fullnote”分成12份。這樣就可以給到我們一個(gè)八度(一共有5個(gè)8度,所以我們只需要3位,從0到4)和音符(從0到11,需要4位)。
代碼如下:
wire[2:0]octave;
wire[3:0]note;
divide_by12 divby12(.numer(fullnote[5:0]),.quotient(octave),.remain(note));
此處用了一個(gè)叫divide_by12的子模塊來(lái)實(shí)現(xiàn)分頻。細(xì)節(jié)稍后講解。
步驟3
從一個(gè)八度到另一個(gè)八度,頻率乘以了“2”。這個(gè)在硬件上很容易實(shí)現(xiàn),我們?cè)诓襟E4上進(jìn)行實(shí)現(xiàn)。但是要從一個(gè)音符到另一個(gè)音符,頻率就要乘以1.0594。這樣的話(huà)就沒(méi)那么容易在硬件上實(shí)現(xiàn)了,所以我們需要看下提前計(jì)算好的音符值。
我們將主時(shí)鐘除以512得到音符A,除以483得到音符A#,除以456得到音符B…記住,除以一個(gè)更低的值得到的是更高的音符。
一個(gè)八度下的音符數(shù)值如下:
always@(note)
case(note)
0:clkdivider=512-1;// A
1:clkdivider=483-1;// A#/Bb
2:clkdivider=456-1;// B
3:clkdivider=431-1;// C
4:clkdivider=406-1;// C#/Db
5:clkdivider=384-1;// D
6:clkdivider=362-1;// D#/Eb
7:clkdivider=342-1;// E
8:clkdivider=323-1;// F
9:clkdivider=304-1;// F#/Gb
10:clkdivider=287-1;// G
11:clkdivider=271-1;// G#/Ab
12:clkdivider=0;// should never happen
13:clkdivider=0;// should never happen
14:clkdivider=0;// should never happen
15:clkdivider=0;// should never happen
endcase
always@(posedgeclk)if(counter_note==0)counter_note<=clkdivider;elsecounter_note<=counter_note-1;
每當(dāng)counter_note等于0時(shí),就進(jìn)入到下一個(gè)八度。
步驟4
現(xiàn)在我們要處理好不同的八度,對(duì)于最低的八度,我們將“counter_note”除以256,對(duì)于第二個(gè)八度,除以128,以此類(lèi)推。
reg[7:0]counter_octave;
always@(posedgeclk)
if(counter_note==0)
begin
if(counter_octave==0)
counter_octave<=(octave==0?255:octave==1?127:octave==2?63:octave==3?31:octave==4?15:7);
else
counter_octave<=counter_octave-1;
end
regspeaker;
always@(posedgeclk)if(counter_note==0&&counter_octave==0)speaker<=~speaker;
完整代碼如下:
modulemusic(clk,speaker);
inputclk;
outputspeaker;
reg[27:0]tone;
always@(posedgeclk)tone<=tone+1;
wire[5:0]fullnote=tone[27:22];
wire[2:0]octave;
wire[3:0]note;
divide_by12 divby12(.numer(fullnote[5:0]),.quotient(octave),.remain(note));
reg[8:0]clkdivider;
always@(note)
case(note)
0:clkdivider=512-1;// A
1:clkdivider=483-1;// A#/Bb
2:clkdivider=456-1;// B
3:clkdivider=431-1;// C
4:clkdivider=406-1;// C#/Db
5:clkdivider=384-1;// D
6:clkdivider=362-1;// D#/Eb
7:clkdivider=342-1;// E
8:clkdivider=323-1;// F
9:clkdivider=304-1;// F#/Gb
10:clkdivider=287-1;// G
11:clkdivider=271-1;// G#/Ab
12:clkdivider=0;// should never happen
13:clkdivider=0;// should never happen
14:clkdivider=0;// should never happen
15:clkdivider=0;// should never happen
endcase
reg[8:0]counter_note;
always@(posedgeclk)if(counter_note==0)counter_note<=clkdivider;elsecounter_note<=counter_note-1;
reg[7:0]counter_octave;
always@(posedgeclk)
if(counter_note==0)
begin
if(counter_octave==0)
counter_octave<=(octave==0?255:octave==1?127:octave==2?63:octave==3?31:octave==4?15:7);
else
counter_octave<=counter_octave-1;
end
regspeaker;
always@(posedgeclk)if(counter_note==0&&counter_octave==0)speaker<=~speaker;
endmodule
-
FPGA
+關(guān)注
關(guān)注
1630文章
21796瀏覽量
605727 -
開(kāi)發(fā)板
+關(guān)注
關(guān)注
25文章
5121瀏覽量
98102
原文標(biāo)題:大神教你DIY | 如何用一塊FPGA開(kāi)發(fā)板制作音樂(lè)盒?!
文章出處:【微信號(hào):EngicoolArabic,微信公眾號(hào):電子工程技術(shù)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論