51单片机:蜂鸣器播放音乐

         音乐是由音符构成的,每一个音符主要有三要素:音高,时值,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;
}

如有问题请指正。感谢您的阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值