有时候我们需要借助单片机实现音乐播放,可行的方案有很多,但成本最低的方案还是通过单片机驱动蜂鸣器来实现音乐播放。本文通过改进常见的单片机驱动蜂鸣器方法,一定程度上实现了任意音乐可以通过蜂鸣器播放(能听出来的程度)。不再需要手动输入乐谱参数,大大节省了时间和提高了准确度。
国际标准音高包含了哆啦咪法嗦来嘻(表中用1234567)的多个音高的频率,为了尽可能的还原音乐,这里模拟了其中9个频率的音高。下表为音高对应的频率。
接下来根据T=65535-1/频率1/2/1.0851000000+1的公式,在EXCEL表格中添加此公式,可以很快求出各个音高对应的计数值。下图便是上述频率对应的计数值:
到这里还不算完,因为51单片机定时器计数值分为高位计数值和低位计数值两个部分。所以还需要将上述10进制计数值转为16进制计数值,这样便能直接将2进制的计数值分割成高位和低位两部分。 实现方法很简单,在EXCEL中编辑=DEC2HEX(单元格)便能将对应单元格的10进制计数值转为16进制计数值。下图便是上述10进制计数值对应的16进制计数值:
为了实现音乐的还原,设计采用定时器中断产生一定频率的方波信号驱动无源蜂鸣器还原音高,延时函数将使该频率维持特定时间还原音长。这样便能实现简谱上一个音符的还原,连贯起来便能还原整段音乐。
code unsigned char yingaoh[63]={
0x92,0xC8,0xE4,0xF2,0xF9,0xFC,0xFE,0xFF,0xFF, //音符1的9个音高
0x9E,0xCE,0xE7,0xF3,0xF9,0xFC,0xFE,0xFF,0xFF, //音符2的9个音高
0xA8,0xD4,0xEA,0xF5,0xFA,0xFD,0xFE,0xFF,0xFF, //音符3的9个音高
0xAD,0xD6,0xEB,0xF5,0xFA,0xFD,0xFE,0xFF,0xFF, //音符4的9个音高
0XB6,0XDB,0XED,0XF6,0XFB,0XFD,0XFE,0XFF,0XFF, //音符5的9个音高
0XBE,0XDF,0XEF,0XF7,0XFB,0XFD,0XFE,0XFF,0XFF, //音符6的9个音高
0XC5,0XE2,0XF1,0XF8,0XFC,0XFE,0XFF,0XFF,0XFF, //音符7的9个音高
}; //7个音符的9个八度高位计数值
code unsigned char yingaol[63]={
0x3C,0xF3,0x79,0x3C,0x1E,0x8F,0x47,0x23,0x91, //音符1的9个音高
0x2A,0xF3,0x79,0xBC,0xDE,0xEF,0x77,0x3B,0x9D, //音符2的9个音高
0x9D,0x4E,0x27,0x13,0x89,0x45,0xA2,0x51,0xA8, //音符3的9个音高
0x6D,0xCE,0x61,0xB0,0xD8,0x6C,0xB6,0x5B,0xAD, //音符4的9个音高
0X86,0X43,0XA1,0XD0,0X68,0XB4,0XDA,0X6D,0XB6, //音符5的9个音高
0X8A,0X45,0XA2,0XD1,0XE8,0XF4,0XFA,0X7D,0XBE, //音符6的9个音高
0XBE,0XD3,0X6C,0XB5,0X5A,0X2D,0X16,0X8B,0XC5, //音符7的9个音高
}; 7个音符的9个八度低位计数值
code unsigned char music[]={
8,89,
3,4,4,7,4,4,6,4,4,
7,4,4,6,4,4,6,4,4,
7,4,4,7,4,16,5,4,4,
6,4,4,7,4,4,3,4,4,
5,4,4,3,4,4,5,4,4,
3,4,4,3,4,24,3,4,4,
5,4,4,5,4,4,3,4,4,
5,4,4,6,4,4,6,4,24,
1,5,4,1,5,4,1,5,4,
5,4,4,5,4,4,6,4,4,
7,4,16,7,4,4,3,4,4,
7,4,4,6,4,4,7,4,4,
6,4,4,6,4,4,7,4,4,
7,4,16,7,4,4,3,4,4,
7,4,4,6,4,4,7,4,4,
6,4,4,5,4,4,3,4,4,
3,4,24,3,4,4,5,4,4,
5,4,4,3,4,4,5,4,4,
6,4,4,6,4,16,6,4,4,
7,4,4,1,5,4,1,5,4,
1,5,4,5,4,4,6,4,4,
7,4,4,7,4,24,6,4,4,
5,4,4,6,4,4,5,4,4,
6,4,4,7,4,4,6,4,24,
5,4,4,7,4,4,7,4,4,
6,4,4,6,4,4,5,4,4,
5,4,4,3,4,4,3,4,12,
3,4,4,5,4,4,5,4,4,
5,4,4,5,4,4,5,4,4,
3,4,4,5,4,24,4,4,24,
4,4,24,4,4,24,4,4,24,
4,4,24,3,4,4,4,4,4,
7,4,8,3,5,4,7,4,4,
7,4,8,2,5,4,3,5,4,
3,5,8,3,5,8,3,5,4,
5,5,4,3,5,4,2,5,4,
3,5,4,2,5,4,7,4,4,
6,4,4,7,4,16,3,4,8,
7,4,4,6,4,4,6,4,8,
7,4,4,3,5,4,3,5,4,
5,4,4,5,4,8,7,4,4,
3,5,4,3,5,4,7,4,4,
3,5,4,4,5,4,4,5,4,
7,4,8,3,5,4,7,4,4,
7,4,8,2,5,4,3,5,4,
3,5,8,3,5,8,3,5,4,
5,5,4,3,5,4,2,5,4,
3,5,4,2,5,4,7,4,4,
6,4,4,7,4,16,3,4,8,
7,4,4,6,4,4,6,4,8,
7,4,4,7,4,4,7,4,4,
3,4,4,3,4,8,4,4,8,
4,4,8,4,4,8,6,4,4,
5,4,4,4,4,4,3,4,4,
3,4,4,3,4,16,5,4,12,
6,4,4,6,4,4,7,4,4,
7,4,8,6,4,8,5,4,8,
4,4,2,5,4,4,6,4,8,
3,4,24,3,5,16,7,4,8,
2,5,16,6,4,8,6,4,16,
7,4,8,7,4,16,7,4,8,
1,5,16,1,5,4,2,5,4,
2,5,12,3,5,4,2,5,4,
6,4,4,7,4,8,6,4,4,
7,4,4,7,4,8,6,4,4,
7,4,4,5,4,16,7,4,12,
6,4,4,7,4,4,3,5,4,
2,5,12,6,4,4,6,4,4,
5,4,4,4,4,2,6,4,4,
5,4,4,4,4,4,3,4,24,
3,4,4,7,4,4,6,4,4,
7,4,4,6,4,4,6,4,4,
7,4,4,7,4,16,5,4,4,
6,4,4,7,4,4,3,4,4,
5,4,4,3,4,4,5,4,4,
3,4,4,3,4,24,3,4,4,
5,4,4,5,4,4,3,4,4,
5,4,4,6,4,4,6,4,24,
1,5,4,1,5,4,1,5,4,
5,4,4,5,4,4,6,4,4,
7,4,16,7,4,4,3,4,4,
7,4,4,6,4,4,7,4,4,
6,4,4,6,4,4,7,4,4,
7,4,16,7,4,4,3,4,4,
7,4,4,6,4,4,7,4,4,
6,4,4,5,4,4,3,4,4,
3,4,24,3,4,4,5,4,4,
5,4,4,3,4,4,5,4,4,
6,4,4,6,4,16,6,4,4,
7,4,4,1,5,4,1,5,4,
1,5,4,5,4,4,6,4,4,
7,4,4,7,4,24,6,4,4,
5,4,4,6,4,4,5,4,4,
6,4,4,7,4,4,6,4,24,
5,4,4,7,4,4,7,4,4,
6,4,4,6,4,4,5,4,4,
5,4,4,3,4,4,3,4,12,
3,4,4,5,4,4,5,4,4,
5,4,4,5,4,4,5,4,4,
3,4,4,5,4,24,4,4,24,
4,4,24,4,4,24,4,4,24,
4,4,24,3,4,4,4,4,4,
7,4,8,3,5,4,7,4,4,
7,4,8,2,5,4,3,5,4,
3,5,8,3,5,8,3,5,4,
5,5,4,3,5,4,2,5,4,
3,5,4,2,5,4,7,4,4,
6,4,4,7,4,16,3,4,8,
7,4,4,6,4,4,6,4,8,
7,4,4,3,5,4,3,5,4,
5,4,4,5,4,8,7,4,4,
3,5,4,3,5,4,7,4,4,
3,5,4,4,5,4,4,5,4,
7,4,8,3,5,4,7,4,4,
7,4,8,2,5,4,3,5,4,
3,5,8,3,5,8,3,5,4,
5,5,4,3,5,4,2,5,4,
3,5,4,2,5,4,7,4,4,
6,4,4,7,4,16,3,4,8,
7,4,4,6,4,4,6,4,8,
7,4,4,7,4,4,7,4,4,
3,4,4,3,4,8,4,4,8,
4,4,8,4,4,8,6,4,4,
5,4,4,4,4,4,3,4,4,
3,4,4,3,4,16,7,4,8,
3,5,4,7,4,4,7,4,8,
2,5,4,3,5,4,3,5,8,
3,5,8,3,5,4,5,5,4,
3,5,4,2,5,4,3,5,4,
2,5,4,7,4,4,6,4,4,
7,4,16,3,4,8,7,4,4,
6,4,4,6,4,8,7,4,4,
3,5,4,3,5,4,5,4,4,
5,4,8,4,5,8,7,4,4,
3,5,4,3,5,4,7,4,4,
3,5,6,3,5,1,4,5,4,
7,4,8,3,5,4,7,4,4,
7,4,8,2,5,4,3,5,4,
3,5,8,3,5,8,3,5,4,
5,5,4,3,5,4,2,5,4,
3,5,4,2,5,4,7,4,4,
6,4,4,7,4,16,3,4,8,
7,4,4,6,4,4,6,4,8,
7,4,4,7,4,4,7,4,4,
3,4,4,3,4,8,4,4,8,
4,4,8,4,4,8,6,4,4,
5,4,4,4,4,4,3,4,4,
3,4,4,3,4,16,3,4,4,
7,4,4,6,4,4,7,4,4,
6,4,4,6,4,4,7,4,4,
7,4,16,5,4,4,6,4,4,
7,4,4,6,4,4,6,4,4,
5,4,4,5,4,4,3,4,4,
3,4,24,3,4,4,5,4,4,
5,4,4,3,4,4,5,4,4,
6,4,4,6,4,24,4,4,24,
4,4,24,4,4,24,6,4,4,
5,4,4,4,4,4,3,4,4,
3,4,4,3,4,16,0
};
void t0int() interrupt 1 //定时器0中断函数
{
TR0=0; //关闭定时器TR0
beep=!beep; //蜂鸣状态取反
TH0=timeh; //把timeh赋给定时0高位计数值
TL0=timel; //把timel赋给定时0低位计数值
TR0=1; //开定时器TR0
}
void Delay10ms() //11.0592MHz下的10ms延时函数
{
unsigned char i, j;
i = 108;
j = 145;
do
{
while (--j);
} while (--i);
}
void jiepaidelay(float t) //节拍延时函数
{
unsigned int time;
time=sifenyinfu*(t/km); //根据节拍算出10ms的计数次数
for(;time>0;time--)
{
Delay10ms();
}
TR0=0;
}
int main(void)
{
float k;
unsigned int i;
unsigned char m;
float n;
TMOD=0x01; //置T0定时器工作在方式1
EA=1; //开总中断
ET0=1; //开定时器0中断,这两句等效于IE=0x82
i=2;
km=music[0];//获取divisions值
paizishu=music[1]; //获取曲子每分钟拍子数
k=100*((60/paizishu)/4)+0.5; //根据每分钟节拍数,求解四分之一音符保持时长,四舍五入单位为10ms
sifenyinfu=k;//把四分之一音符保持时长值赋给unsigned int型sifenyinfu
while(1)
{
m=(music[i]-1)*9+music[i+1]; //获取音符对应计数值所在下标
n=music[i+2]; //获取音符保持时长值
timeh=yingaoh[m]; //获取音符对应定时器计数值高位
timel=yingaol[m]; //获取音符对应定时器计数值低位
TH0=timeh; //把timeh赋给定时0高位计数值
TL0=timel; //把timel赋给定时0低位计数值
TR0=1; //开定时器0
jiepaidelay(n); //执行音符保持函数
i=i+3; //读下一个音符
if(music[i]==0)
i=2; //如果到末尾则回到开头,循环播放
}
}
如果你手边正好没有51单片机,或者你不想动手操作。接下来可以使用Proteus 8 Professional V8.9进行仿真。组成部分很简单,只有单片机和扬声器,注意一定要选择图下所示的扬声器才能发声(为了尽可能还原真实场景,最好把仿真软件中的速度晶振频率调整为11.0592MHz):