崔凱 孟祥順 李彬彬
【摘要】本文以AT89C51單片機(jī)為核心,利用C語言設(shè)計制作了《祝你生日快樂》歌曲的電子音樂。并在播放音樂的同時伴隨發(fā)光二極管的閃爍。
【關(guān)鍵詞】單片機(jī);電子音樂
1.引言
以單片機(jī)為核心制作的電子音樂結(jié)構(gòu)簡單、成本經(jīng)濟(jì)、工作效果好,在兒童玩具、禮物等很多場合都能得到大規(guī)模的應(yīng)用。而利用單片機(jī)設(shè)計制作電子音樂對于高校學(xué)生理解、掌握單片機(jī)結(jié)構(gòu)與原理,熟悉編寫、調(diào)試程序,搭建硬件電路都有很大幫助。
2.單片機(jī)產(chǎn)生音樂的原理
聲音是通過物體的振動產(chǎn)生的。如果單片機(jī)通過某一I/O口以一定的頻率輸出高電平、低電平,這一引腳便產(chǎn)生了一定頻率的方波。該方波經(jīng)放大電路放大,加到揚聲器便會產(chǎn)生一定頻率的聲音。若方波頻率發(fā)生變化,則聲音頻率也隨之變化。通過控制輸出方波時間的長短,聲音的長短就會得到相應(yīng)控制。因此根據(jù)樂譜,單片機(jī)就可以產(chǎn)生電子音樂。
3.方波的產(chǎn)生
8051單片機(jī)產(chǎn)生固定頻率方波可以使用定時器/計數(shù)器T0或T1的溢出中斷。在中斷發(fā)生時,將P口引腳的電壓取反使其輸出方波。本文采用T1溢出中斷。在T1的溢出中斷服務(wù)程序中,執(zhí)行重新裝入計數(shù)初值,然后將輸出引腳電壓取反,在下次T1中斷時,P口引腳再取反,即可輸出方波。音樂節(jié)拍也可以利用延時程序來控制。
4.音符頻率與定時器初始值
本文單片機(jī)采用12MHZ脈沖,則一個幾周為1us,所以需要多長的時間(頻率),就可以個定時器裝入一個對應(yīng)的初始值。
5.音符節(jié)拍選擇
節(jié)拍就是音符演奏時間段長短。設(shè)一拍的十年為0.4秒,則其余拍數(shù)均可以此節(jié)拍推算出。而節(jié)拍的時間也可以通過延時實現(xiàn)。
6.音符代碼
程序中使用一個字節(jié)來表示音符和該音符的節(jié)拍,音符代碼的高4位是簡碼,由它可以找到對應(yīng)的定時器T1初始值;低4位代表音符節(jié)拍,其也可以通過查找節(jié)拍代碼表獲得。所以,我們只要找到《祝你生日快樂》這首樂曲的樂譜,找到其所對應(yīng)的“簡譜碼”做高4位,再找到對應(yīng)的節(jié)拍碼做低4位,把高4位和低4位合成一個音符代碼即可。
現(xiàn)將根據(jù)《祝你生日快樂》樂曲簡譜轉(zhuǎn)換后的音符代碼表示如下:0x42,0x01,0x41,0x54, 0x44,0x74,0x64,0x42,x01,0x41,0x54,0x44,0x84, 0x74,0x74,0x42,0x01,0x41,0xB4,0x84,0x74, 0x64,0xC4,0xA2,0x01,0xA1,x94,0x74,0x84,0x74, 0x74。
將此代碼存入程序存儲器,程序執(zhí)行時將音符代碼按字節(jié)逐一讀出,高4位是簡譜碼,低4為是音符節(jié)拍碼,并根據(jù)T1計數(shù)器的初始值來設(shè)置時間,由此控制每一個音符的演奏時間,最終行動一段樂曲。
7.電子音樂電路設(shè)計
如圖1所示,電路中使用20腳的AT80C2051為音樂電路核心。Q1、Q2組成復(fù)合管電路。P3.5端是音頻信號的輸出端,輸出的方波信號經(jīng)復(fù)合管電路驅(qū)動后驅(qū)動揚聲器發(fā)聲;發(fā)光二極管DS1~DS7和三極管Q3~Q9等元件組成音符顯示電路,分別顯示1、2、~7音符;P1.0~P1.6端經(jīng)過R4~R10電阻分別連接Q3~Q9基極。當(dāng)P1.0~P1.6端有輸出低電平時,三極管飽和導(dǎo)通,對應(yīng)的發(fā)光二極管點亮。S2與R3構(gòu)成啟動電路,按下S2給P3.7引腳送入低電平,啟動放音。
圖1 電子音樂電路圖
8.源程序設(shè)計
#include
sbit sp=P3^5;sbit sw=P3^7;unsigned char i=0;
un,signed char hi_note=0;unsigned char low_note=0;
code unsigned song[ ]={0x42,0x01,0x41, 0x54,0x44,0x74,0x64,0x42,x01,0x41,0x54, 0x44,0x84,0x74,0x74,0x42,0x01,0x41,0xB4, 0x84,0x74,0x64,0xC4,0xA2,0x01,0xA1,x94,0x74,0x84,0x74,0x74,
0x42,0x01,0x41,0x54,0x44,0x74,0x64, 0x42,x01,0x41,0x54,0x44,0x84,0x74,0x74,0x42, 0x01,0x41,0xB4,0x84,0x74,0x64,0xC4,0xA2, 0x01,0xA1,x94,0x74,0x84,0x74,0x74,0x00};
code int note[ ]={0x0000,0xFB03,0xFB83, 0xFC0B,0xFC43,0xFCAB,0xFD08,0xFD32,0xFD81, 0xFDC7,0xFE05,0xFE21,0xFE55,0xFE83,0xFE99, 0xFEC0}
code unsigned char led[ ]={0xFF,0xEF,0xDF,0xBF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF};
void delay(unsigned int n)
{
unsigned int I,j;
for(i=0;i<1000*n;i++)
for(j=0;j<120;j++);
}
static void timer1_isr(void)interrupt TF1_vector using 2
{
TR1=0;
TL1=low_note;
TH1=hi_note;
TR1=1;
sp=~sp;
}
static void timer1_initialize(void)
{
EA=0;
TR1=0;
TMOD=0X10;
ET1=1;
EA=1;
}
void singing()
{
unsigned char beat,temp;
i=0;
do
{
temp=song[i];
if(temp==0){TR1=0;return;}
beat=temp&0X0F;
temp=(temp>>4)&0X0F;
if(temp==0)TR1=0;
else
{
hi_note=note[temp]>>8;
low_note=note[temp]&0X00FF;
TR1=1;
P1=led[temp];
}
Delay(beat);
P1=0XFF;
i++;}
while(1);
}
Void main(void)
{
timer1_initialize();
do
{
sw_press();
singing();
}while(1);
}
作者簡介:崔凱(1980—),男,遼寧本溪人,碩士,營口職業(yè)技術(shù)學(xué)院講師,主要從事單片機(jī)開發(fā)設(shè)計及電機(jī)專業(yè)教學(xué)。