音乐是由音符构成的,每一个音符主要有三要素:音高,时值,bpm;
由于每个音的音高的频率一般都比较高,单位周期很短,所以我们主要使用定时器来实现不同频率的音高。如下所示,这是一段固定频率的音高,我们使用定时器来实现,每当时间到时,我们就用中断让电平进行反转。那么这个时间的初值是多少呢?通过上网查表可得每个音高的频率,每个音高的单位周期是频率的倒数,我们可以得到一个音高的单位周期,这个单位周期的一半就是这个音对应的定时器初值所要设定的时长。定时器时长=(2^16-定时器初值)*机器周期,我们就得到了每个音高对应的定时器初值。
为了使频率更加精确,我们采用16位的定时器模式,以下是高八位(freqh[])和低八位(freql[])对应的定时器初值表,每七个为一组,分为低音组,中音组,高音组,超高音组:
unsigned char freqh[]={0xf2,0xf3,0xf5,0xf5,0xf6,0xf7,0xf8,//低音组
0xf9,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,//中音组
0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,//高音组
0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xff//超高音组
};
unsigned char freql[]={0x42,0xc1,0x17,0xb6,0xd0,0xd1,0xb6,//同上
0x21,0xe1,0x8c,0xd8,0x68,0xe9,0x5b,
0x8f,0xee,0x44,0x6b,0xb4,0xf4,0x2d,
0x47,0x77,0xa2,0xb6,0xda,0xfa,0x16
};
每次到了时间之后,我们需要短暂的关闭定时器,重装初值(因为这个模式不能重装初值),并且反转电平,然后再打开定时器。
(在代码的开头,我们定义sbit speaker= P2^5,timer0h、timer0l用来存放每一次的频率值)
void t0init() interrupt 1
{
TR0=0;
speaker=!speaker;
TH0=timer0h;
TL0=timer0l;
TR0=1;
}
之后,我们需要确定一个音要持续多长时间,由于一个音一般出现时间较长,我们使用for循环的方式实现延时,这里每一个单位时间假设是四分之一拍,设四分之一拍所需要持续的时间1000,这个值相对较小,每拍时间较短,我们可以随时调整这个值,来控制乐曲播放的速度(bpm)。
void delay(unsigned char t)
{
unsigned char t1;
unsigned long t2;
for(t1=0;t1<t;t1++)
{for(t2=0;t2<1000;t2++);}
TR0=0;
}
接下来,我们写一段乐谱《洋娃娃和小熊跳舞》 ,这段乐谱的格式是:以三个数字为一组,每组个数字分别是(音高,组别,持续的拍数),注意:持续的拍数是以持续多少个四之一拍为单位的:
unsigned char yangwawa[]=
{
1,2,2, 2,2,2, 3,2,2, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2,
4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 5,2,4,
1,2,2, 2,2,2, 3,2,2, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2,
4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 1,2,4,
6,2,2, 6,2,2, 6,2,1, 5,2,1, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2, 4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 5,2,4,
6,2,2, 6,2,2, 6,2,1, 5,2,1, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2, 4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 1,2,4
};
好了,大概逻辑就是这样了,我们来实现一下主程序,打开定时器,打开中断总开关,到开定时器中断的开关,然后进入循环,用i来标记现在读取到乐谱的位置,用k来表示当前音符的音高,用time来确定当前音符所持续的拍数。(也就是前两个数字赋给k,用k来检索当前音高对应的频率;最后一个数字赋给time),然后用song();来“唱出“这个音符:
void song()
{
TH0=timer0h;
TL0=timer0l;
TR0=1;
delay(time);
}
void main()
{
unsigned char k,i;
TMOD=0x01;
EA=1;
ET0=1;
while(1)
{
i=0;
while(i<210)
{
k=yangwawa[i]+7*yangwawa[i+1]-1;
timer0h=freqh[k];
timer0l=freql[k];
time=yangwawa[i+2];
i=i+3;
song();
}
}
return;
}
最后,送上全篇代码:
#include <reg51.h>
sbit speaker=P2^5;
unsigned char timer0h,timer0l,time;
unsigned char freqh[]={0xf2,0xf3,0xf5,0xf5,0xf6,0xf7,0xf8,
0xf9,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,
0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,
0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xff
};
unsigned char freql[]={0x42,0xc1,0x17,0xb6,0xd0,0xd1,0xb6,
0x21,0xe1,0x8c,0xd8,0x68,0xe9,0x5b,
0x8f,0xee,0x44,0x6b,0xb4,0xf4,0x2d,
0x47,0x77,0xa2,0xb6,0xda,0xfa,0x16
};
unsigned char liangzhilaohu[]={
1,2,2, 2,2,2, 3,2,2, 1,2,2, 1,2,2, 2,2,2, 3,2,2, 1,2,2,
3,2,2, 4,2,2, 5,2,4, 3,2,2, 4,2,2, 5,2,4,
5,2,1, 6,2,1, 5,2,1, 4,2,1, 3,2,2, 1,2,2, 5,2,1, 6,2,1, 5,2,1, 4,2,1, 3,2,2, 1,2,2,
2,2,2, 5,1,2, 1,2,4, 2,2,2, 5,1,2, 1,2,4
};
unsigned char yangwawa[]=
{
1,2,2, 2,2,2, 3,2,2, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2,
4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 5,2,4,
1,2,2, 2,2,2, 3,2,2, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2,
4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 1,2,4,
6,2,2, 6,2,2, 6,2,1, 5,2,1, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2, 4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 5,2,4,
6,2,2, 6,2,2, 6,2,1, 5,2,1, 4,2,2, 5,2,2, 5,2,2, 5,2,1, 4,2,1, 3,2,2, 4,2,2, 4,2,2, 4,2,1, 3,2,1, 2,2,2, 1,2,2, 3,2,2, 1,2,4
};
void delay(unsigned char t)
{
unsigned char t1;
unsigned long t2;
for(t1=0;t1<t;t1++)
{for(t2=0;t2<1000;t2++);}
TR0=0;
}
void t0init() interrupt 1
{
TR0=0;
speaker=!speaker;
TH0=timer0h;
TL0=timer0l;
TR0=1;
}
void song()
{
TH0=timer0h;
TL0=timer0l;
TR0=1;
delay(time);
}
void main()
{
unsigned char k,i;
TMOD=0x01;
EA=1;
ET0=1;
while(1)
{
i=0;
while(i<210)
{
k=yangwawa[i]+7*yangwawa[i+1]-1;
timer0h=freqh[k];
timer0l=freql[k];
time=yangwawa[i+2];
i=i+3;
song();
}
}
return;
}
如有问题请指正。感谢您的阅读。