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