程序设计目标及程序运行效果说明
程序设计目标:通过无源蜂鸣器模块实现音乐播放。
程序运行效果说明:下载程序后,开始音乐的播放。
程序相关电路及原理说明
1.原理说明
本实验板采用的是无源蜂鸣器,无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。必须用2K~5K的方波去驱动它。相比与有源蜂鸣器,无源蜂鸣器的优点在于价格便宜,可以通过控制其振动频率来改变发出的声音,做出“多来米发索拉西”的效果。因此,无源蜂鸣器可以用于音乐的播放。而有源蜂鸣器的优点在于使用简单,不需要编写“乐谱”。本实验板使用的无源蜂鸣器是电磁式蜂鸣器,电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后,接收到的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
每一个音符的发声频率是不同的,我们需要用计时器来精确计时,用以产生方波,这样才能发出不用的音符声音。C调各音符频率与计数值如图1所示,以下的简谱码是在晶振为12MHz的情况下计算的,换算为16进制的简谱码如程序中quzi[]数组所示。
本程序中,数组music[]即是要播放的音乐,格式为音符,节拍,音符,节拍,如此循环下去。音符为要发出的音调,而节拍则是声音的持续时间。如图1,在数组music[]中,音符表示的格式为:十位代表是低八度,中八度还是高八度,1代表高八度,2代表中八度,3代表高八度,个位代表简谱的音符,例如,0x15代表低八度的S0,如图即是低5S0,0x21代表中八度的DO,如图即是中1 D0。音符中,0x00代表结束符,表示整首歌曲演唱完毕,而0xff代表休止符,表示要休止100ms。遇到这两种情况,都应该重新执行循环中的第一步。其余情况则是正常播放。
2.程序相关电路
(1)无源蜂鸣器电路原理图
(2)按键控制电路
芯片相关引脚:Beep——P3^4;
代码如下:
1:
#include<STC15F2K60S2.h>
#define uint unsigned int
#define uchar unsigned char
sbit sbtBeep=P3^4; //蜂鸣器
uchar ucTimerH,ucTimerL; //定义定时器的重装值
uchar code arrMusic[]= //音乐代码,歌曲为《同一首歌》,格式为: 音符, 节拍
{
//音符的十位代表是低中高八度,1代表高八度,2代表中八度,3代表高八度
//个位代表简谱的音符,例如0x15代表低八度的S0,0x21代表中八度的DO。
//节拍则是代表音长,例如:0x10代表一拍,0x20代表两拍,0x08代表1/2拍
0x15,0x20,0x21,0x10, //音符的十位代表是低八度,中八度还是高八度,1代表低八度,2代表中八度,3代表高八度
0x22,0x10,0x23,0x18, //个位代表简谱的音符,例如0x15代表低八度的S0,0x21代表中八度的DO。
0x24,0x08,0x23,0x10, //节拍则是代表音长,例如:0x10代表一拍,0x20代表两拍,0x08代表1/2拍
0x21,0x10,0x22,0x20,
0x21,0x10,0x16,0x10,
0x21,0x40,0x15,0x20,
0x21,0x10,0x22,0x10,
0x23,0x10,0x23,0x08,
0x24,0x08,0x25,0x10,
0x21,0x10,0x24,0x18,
0x23,0x08,0x25,0x10,
0x22,0x08,0x23,0x08,
0x23,0x08,0x22,0x08,
0x22,0x30,0x23,0x20,
0x25,0x10,0x31,0x10,
0x27,0x18,0x26,0x08,
0x26,0x20,0x25,0x10,
0x25,0x08,0x26,0x08,
0x27,0x10,0x26,0x08,
0x25,0x08,0x23,0x40,
0x24,0x18,0x24,0x08,
0x25,0x10,0x26,0x10,
0x25,0x10,0x24,0x08,
0x23,0x08,0x22,0x20,
0x17,0x10,0x17,0x08,
0x16,0x08,0x15,0x10,
0x16,0x10,0x21,0x40,
0x00,0x00
};
uchar code arrMusicToTimerNum[]=
{
//此数组数据为各个音符在定时器中的重装值,第一列是高位,第二列是低位
0xf8,0x8c, //低八度,低1
0xf9,0x5b,
0xfa,0x15, //低3
0xfa,0x67,
0xfb,0x04, //低5
0xfb,0x90,
0xfc,0x0c, //低7
0xfc,0x44, //中央C调
0xfc,0xac, //中2
0xfd,0x09,
0xfd,0x34, //中4
0xfd,0x82,
0xfd,0xc8, //中6
0xfe,0x06,
0xfe,0x22, //高八度,高1
0xfe,0x56,
0xfe,0x6e, //高3
0xfe,0x9a,
0xfe,0xc1, //高5
0xfe,0xe4,
0xff,0x03 //高7
};
//延时函数
void DelayMs(unsigned int xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=124;j>0;j--);
}
//取址函数
//取出tem音符在arrMusicToTimerNum数组中的位置值
uchar GetPosition(uchar tem)
{
uchar ucBase,ucOffset,ucPosition; //定义曲调,音符和位置
ucBase=tem/16; //高4位是曲调值,基址
ucOffset=tem%16; //低4位是音符,偏移量
if(ucBase==1) //当曲调值为1时,即是低八度,基址为0
ucBase=0;
else if(ucBase==2) //当曲调值为2时,即是中八度,基址为14
ucBase=14;
else if(ucBase==3) //当曲调值为3时,即是高八度,基址为28
ucBase=28;
//通过基址加上偏移量,即可定位此音符在arrMusicToTimerNum数组中的位置
ucPosition=ucBase+(ucOffset-1)*2;
return ucPosition; //返回这一个位置值
}
//播放音乐功能函数
void PlayMusic()
{
uchar ucNoteTmp,ucRhythmTmp,tem; // ucNoteTmp为音符,ucRhythmTmp为节拍
uchar i=0;
while(1)
{
ucNoteTmp=arrMusic[i]; //如果碰到结束符,延时1秒,回到开始再来一遍
if(ucNoteTmp==0x00)
{
i=0;
DelayMs(1000);
}
else if(ucNoteTmp==0xff) //若碰到休止符,延时100ms,继续取下一音符
{
i=i+2;
DelayMs(100);
TR0=0;
}
else //正常情况下取音符和节拍
{
//取出当前音符在arrMusicToTimerNum数组中的位置值
tem=GetPosition(arrMusic[i]);
//把音符相应的计时器重装载值赋予ucTimerH和ucTimerL
ucTimerH=arrMusicToTimerNum[tem];
ucTimerL=arrMusicToTimerNum[tem+1];
i++;
TH0=ucTimerH; //把ucTimerH和ucTimerL赋予计时器
TL0=ucTimerL;
ucRhythmTmp=arrMusic[i]; //取得节拍
i++;
}
TR0=1; //开定时器1
DelayMs(ucRhythmTmp*180); //等待节拍完成, 通过P3^4口输出音频
TR0=0; //关定时器1
}
}
//初始化函数
//功能是配置IO口
void InitSys()
{
P0M0=0xff;
P0M1=0x00;
P2M0=0x08;
P2M1=0x00;
P3M0=0x10;
P3M1=0x00;
}
//定时器0初始化函数
void InitT0()
{
TMOD=0x01;
TH0=0xD8;
TL0=0xEF;
EA=1;
ET0=1;
TR0=0;
}
//主函数
void main()
{
InitSys();
InitT0();
P0=0x00;
PlayMusic();
while(1);
}
/*---------定时器0中断处理函数---------*/
//重新装值,并把sbtBeep值取反,产生方波
void T0_Process() interrupt 1 //计时器控制频率
{
TH0=ucTimerH;
TL0=ucTimerL;
sbtBeep=~sbtBeep;
}
2:
#include "STC15F2K60S2.H"
#define uchar unsigned char
#define uint unsigned int
uint j=0;
sbit beep=P3^4;
uint reset[15]={0,1908,1701,1515,1433,1276,1136,1012,956,852,759,716,638,568,506}; // c调低音与中音1-7 对应的定时器重装值
//0 1 2 3 4 5 6 7 1 2 3 4 5 6 7
xdata uchar music[204]={0,2,10,1,10,1,10,1,10,1,12,1,12,3,0,4,0,2,9,1,9,1,10,1,9,1.5,0,7.5, 0,2,8,1,8,1,8,1,8,1,8,1,10,2,10,1,10,1,11,2,10,3,
0,8, 8,1, 8,1, 9,1, 8,1, 9,2, 0,2, 8,1, 8,2, 6,1, 6,1, 5,3, 0,8, 9,1, 9,1,10,1,11,1,11,2,0,2,9,1,8,1,10,1,9,0.5,9,2,0,6,
10,1,10,1,10,1,10,1,12,1,12,3,0,4,10,1,10,1,12,1,12,2, 10,1.5,0,6,8,1, 8,1,8,1,8,1,8,1,10,2,10,1,0,1,10,1,10,1,10,2,10,1,
10,1,11,1,10,4,0,6,8,1,8,1,9,1,8,1,8,2,6,1.5,8,1,8,2,6,1,5,3,0,5,9,1, 9,1,10,1,11,1,11,2,0,1,10,1,9,1,8,1,10,1,9,1};
//乐谱 格式为一个音调加持续的时长的组合
void timer0() interrupt 1
{
beep=~beep;
}
void delay(uint t) //延时函数 延时 t ms
{
uint z;
for(;t>0;t--)
for(z=800;z>0;z--)
;
}
void play()
{
if(reset[music[j]]!=0) //读取到不为0的某音调时,改变定时器0的初值,从而发出对应频率的方波给蜂鸣器
{
TR0=1;
TH0=(65536-reset[music[j]])/256;
TL0=(65536-reset[music[j]])%256;
}
else TR0=0;
delay(180*music[++j]); //延迟某音调对应的时长
TR0=0;
delay(60*music[j]); //乐谱中组合之间的小延迟
j++;
if(j==204) //唱完歌后延迟5s 继续唱该歌曲
{
j=0;
delay(5000);
}
}
main()
{
P3M0=0x10; //推挽输出
P3M1=0x00;
EA=1; //开启定时器中断
ET0=1;
TR0=1;
beep=0;
while(1){play();}
}