51单片机实现音乐播放功能:《平凡之路》教程
怎么使用蜂鸣器播放音乐?
音乐是由一连串音符构成的,每个音符都具有特定的频率,所以可以通过一定频率数字信号使蜂鸣器产生音符效果,然后根据音乐简谱排列出音符。
部分音符频率:
频率的产生可用定时器中断实现,定时器初值(十进制)计算代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main(void)//计算计时器初值 @11.0592MHz
{
int x = 0;
float result = 0;
while (1)
{
printf("请输入音符频率:");
scanf("%d", &x);
result = 65535+1 - (float)11059200 / (12 * x);//12分频
printf("%0.2f\n", result);
}
return 0;
}
《平凡之路》简谱:
《平凡之路》为A调,完整代码如下:
#include<reg52.h>
#include<intrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit buzzer = P2 ^ 5; //蜂鸣器管脚
u8 time0h = 0; //定时器高8位
u8 time0l = 0; //定时器低8位
u8 bats = 0; //延时拍数
bit stop = 0; //是否为休止符,0是,1不是(给stop赋值为非0则stop等于1)
bit smooth = 0; //1圆滑,0不圆滑
code u8 node[] = {
//0xf2,0xf3,0xf5,0xf5,0xf6,0xf7,0xf8,//C调低音高8位 @11.0592MHz
//0xf9,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,//C调中音
//0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,//C调高音
//0x42,0xc1,0x17,0xaf,0xd1,0xd1,0xb6,//C调低音低8位
//0x1e,0xde,0x8a,0xd8,0x68,0xe9,0x5b,
//0x8f,0xf0,0x45,0x69,0xb4,0xf4,0x2e,
//0xf3,0xf5,0xf6,0xf6,0xf7,0xf8,0xf9,//D调
//0xf9,0xfa,0xfb,0xfb,0xfb,0xfc,0xfc,
//0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,0xfe,
//0xc1,0x17,0x45,0xd1,0xd1,0xb6,0x80,
//0xde,0x8a,0x23,0x68,0xe9,0x5b,0xc1,
//0xf0,0x45,0x91,0xb4,0xf4,0x2e,0x78,
//0xf5,0xf6,0xf7,0xf7,0xf8,0xf9,0xfa,//E调
//0xfa,0xfb,0xfb,0xfb,0xfc,0xfc,0xfd,
//0xfd,0xfd,0xfd,0xfd,0xfe,0xfe,0xfe,
//0x17,0x45,0x53,0xd1,0xb6,0x80,0x36,
//0x8a,0x23,0xab,0xe9,0x5b,0xc1,0x1c,
//0x45,0x91,0xd5,0xf4,0x2e,0x78,0xa3,
//0xf5,0xf6,0xf7,0xf8,0xf9,0xf9,0xfa,//F调
//0xfa,0xfb,0xfb,0xfc,0xfc,0xfc,0xfd,
//0xfd,0xfd,0xfd,0xfe,0xfe,0xfe,0xfe,
//0xaf,0xd1,0xd1,0x46,0x1e,0xde,0x8a,
//0xd8,0x68,0xe9,0x23,0x8f,0xf0,0x45,
//0x69,0xb4,0xf4,0x12,0x60,0x8e,0xb6,
//0xf6,0xf7,0xf8,0xf9,0xf9,0xfa,0xfb,//G调
//0xfb,0xfb,0xfc,0xfc,0xfc,0xfd,0xfd,
//0xfd,0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,
//0xd1,0xd1,0xb6,0x1e,0xde,0x8a,0x23,
//0x69,0xe9,0x5b,0x8f,0xf0,0x45,0x91,
//0xb4,0xf4,0x2e,0x60,0x8e,0xb6,0xea,
0xf7,0xf8,0xf9,0xf9,0xfa,0xfb,0xfb,//A调
0xfb,0xfc,0xfc,0xfc,0xfd,0xfd,0xfd,
0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xff,
0xd1,0xb6,0x80,0xde,0x8a,0x23,0xab,
0xe9,0x5b,0xc1,0xf0,0x45,0x91,0xd5,
0xf4,0x2e,0x78,0x8e,0xb6,0xea,0x09
//0xf8,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,//B调
//0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,
//0xfe,0xfe,0xfe,0xfe,0xfe,0xff,0xff,
//0xb6,0x80,0x36,0x8a,0x23,0xab,0x23,
//0x5b,0xc1,0x1c,0x45,0x91,0xd5,0x12,
//0x2e,0x78,0xa3,0xb6,0xea,0x09,0x23
};
code u8 music[] = {
0,1,2,0, 3,1,2,0, 3,1,2,0, 6,1,1,0, 6,1,1,1, 6,1,2,0, 1,1,2,0, 2,1,2,0,
3,1,1,0, 3,1,1,1, 3,1,4,0, 0,1,4,0, 0,1,4,0, 0,1,4,0, 0,1,2,0, 3,1,2,0,
3,1,2,0, 6,1,2,0, 6,1,3,0, 5,1,1,0, 5,1,3,0, 4,1,1,1, 4,1,1,0, 3,1,3,1,
3,1,4,0, 0,1,4,0, 0,1,4,0, 0,1,2,0, 3,1,2,0, 3,1,2,0, 6,1,2,1, 6,1,2,0,
1,1,2,0, 2,1,2,0, 3,1,6,0, 0,1,4,0, 0,1,4,0, 0,1,4,0, 0,1,2,0, 3,1,2,0,
3,1,2,0, 1,1,2,0, 3,1,2,0, 3,1,1,0, 4,1,1,1, 4,1,2,0, 3,1,2,0, 1,1,8,0,
0,1,4,0, 0,1,4,0, 0,1,2,0, 3,1,2,0, 3,1,2,0, 6,1,1,0, 6,1,1,1, 6,1,2,0,
1,1,2,0, 2,1,2,0, 3,1,1,0, 3,1,1,1, 3,1,4,0, 0,1,4,0, 0,1,4,0, 0,1,4,0,
0,1,2,0, 3,1,2,0, 3,1,2,0, 6,1,2,0, 6,1,3,0, 5,1,1,0, 5,1,3,0, 4,1,1,1,
4,1,1,0, 3,1,3,1, 3,1,4,0, 0,1,4,0, 0,1,4,0, 0,1,2,0, 3,1,1,0, 3,1,1,0,
3,1,2,0, 6,1,2,1, 6,1,2,0, 1,1,2,0, 2,1,2,0, 3,1,2,1, 3,1,4,0, 0,1,4,0,
0,1,4,0, 0,1,4,0, 0,1,2,0, 3,1,2,0, 3,1,2,0, 1,1,2,0, 3,1,2,0, 3,1,1,0,
4,1,1,1, 4,1,2,0, 3,1,2,0, 1,1,8,0, 0,1,2,0, 5,1,2,0, 6,1,2,0, 7,1,2,0,
1,2,2,0, 7,1,1,0, 1,2,1,1, 1,2,2,0, 5,1,1,0, 6,1,1,0, 6,1,4,0, 5,1,2,0,
4,1,1,0, 4,1,1,1, 4,1,2,0, 3,1,2,0, 3,1,2,0, 3,1,1,0, 2,1,1,1, 2,1,2,0,
5,1,2,0, 6,1,2,0, 7,1,2,0, 1,2,2,0, 7,1,1,0, 1,2,1,1, 1,2,2,0, 5,1,1,0,
6,1,1,0, 6,1,4,0, 6,1,2,0, 7,1,1,0, 1,2,1,1, 1,2,2,0, 1,2,2,0, 1,2,2,0,
1,2,1,0, 2,2,1,1, 2,2,2,0, 0,1,1,0, 5,1,1,0, 6,1,2,0, 7,1,2,0, 1,2,2,0,
7,1,1,0, 1,2,1,1, 1,2,2,0, 3,2,2,0, 6,2,3,0, 5,2,1,1, 5,2,2,0, 4,2,2,0,
3,2,4,0, 3,2,2,0, 2,2,2,1, 2,2,4,0, 6,1,2,0, 7,1,2,0, 1,2,4,0, 1,2,2,0,
2,2,1,0, 1,2,1,1, 1,2,3,0, 1,2,1,0, 7,1,2,0, 1,2,1,0, 2,2,1,1, 2,2,4,0,
1,2,2,0, 7,1,2,0, 1,2,8,0, 0,1,0,0
};//歌谱格式为:音符(0-7)+低中高音(0-2)+拍数(单位0.25拍)+是否圆滑(1,0)(最后四位中的第一第三位为终止参数)
void time0() interrupt 1
{
TR0 = 0; //定时器关后赋值,防止从0开始定时
buzzer = !buzzer;
TH0 = time0h;
TL0 = time0l;
TR0 = 1; //定时器开
}
void Delayms(u16 ms)//延时ms毫秒
{
while (ms--)
{
u8 i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void Sing()
{
if (stop)
{
TH0 = time0h;
TL0 = time0l;
TR0 = 1;
}
else
TR0 = 0;
Delayms(200*bats); //72拍/min
if (!smooth)
{
TR0 = 0;
Delayms(10); //音符间间歇
TR0 = 1;
}
}
void main()
{
u16 i = 0;
u8 j = 0;
EA = 1; //总中断开
ET0 = 1; //T0中断允许
TMOD = 1; //工作方式1,16位定时计数器
while (1) //控制播放次数
{
i = 0;
while (music[i] != 0 || music[i + 2] != 0)//这里定义音符为0与拍数为0组合代表结束歌曲
{
j = music[i] + 7 * music[i + 1] - 1; //确定音符高8位位置
time0h = node[j];
time0l = node[j+21];
bats = music[i + 2];
stop = music[i];
smooth = music[i + 3];
Sing();
i += 4;
}
}
}
/*下面是两个神秘乐谱*/
//乐谱1
/*6,1,6,0, 5,1,2,0, 3,1,4,0, 5,1,4,0, 1,2,4,0, 6,1,2,1, 5,1,2,0, 6,1,8,0,
3,1,4,0, 5,1,2,0, 6,1,2,0, 5,1,4,0, 3,1,2,1, 2,1,2,0, 1,1,2,1, 6,0,2,0,
5,1,2,1, 3,1,2,0, 2,1,8,0, 2,1,6,0, 3,1,2,0, 5,1,4,0, 5,1,2,0, 6,1,2,0,
3,1,6,1, 2,1,2,0, 1,1,8,0, 5,1,6,0, 3,1,2,0, 2,1,2,1, 1,1,2,0, 6,0,2,1,
1,1,2,0, 5,0,8,0, 0,1,0,0
*/
//乐谱2
/*1,1,4,0, 2,1,4,0, 3,1,4,0, 1,1,4,0, 1,1,4,0, 2,1,4,0, 3,1,4,0, 1,1,4,0,
3,1,4,0, 4,1,4,0, 5,1,8,0, 3,1,4,0, 4,1,4,0, 5,1,8,0, 5,1,3,0, 6,1,1,0,
5,1,3,0, 4,1,1,0, 3,1,4,0, 1,1,4,0, 5,1,3,0, 6,1,1,0, 5,1,3,0, 4,1,1,0,
3,1,4,0, 1,1,4,0, 1,1,4,0, 5,0,4,0, 1,1,8,0, 1,1,4,0, 5,0,4,0, 1,1,8,0,
0,1,0,0
*/