一.音乐播放原理

1.蜂鸣器分为有源和无源,经测试均可正常播放音乐。(网上有些回答说有源不可播放)
有源蜂鸣器是指蜂鸣器内部带有振荡源,只需在蜂鸣器两端加上稳定的电压,内部振荡源就会工作,蜂鸣器就可以响。
无源蜂鸣器是指蜂鸣器内部没有振荡源,因此需要外部施加不断变换的高低电平信号,否则蜂鸣器不响。

2.首先需要了解蜂鸣器可以播放音乐的原理。
音乐中不同的音调实际上是发声体震动的速度(即频率)不同。所以要让蜂鸣发出不同的音调只需要使蜂鸣器开启和关闭的频率改变(即用与音调相应的频率不断使输出口在高低电平之间转换,类似于输出一个占空比为50%而频率可变的方波)。可以想到,这可以通过定时器来实现,在定时器中断中切换蜂鸣器的开关,不断改变定时器的溢出频率即可改变音调

而节拍就是让蜂鸣器在固定的频率工作一段时间,只需要通过软件延时就可达到此效果。
在这里插入图片描述
这是从网上找的音调与频率的对应关系。

二.代码

#include "stc15.h"
#include "intrins.h"

//此处音调所对应的值并不是频率,为了改变定时器的溢出频率更加方便,将频率转变成了相应的周期
//time = 1/freq/2*1000000us,此处除以2是因为在一个开关周期中高电平和低电平各占一半
//低音
#define DOL 1908	
#define REL 1701
#define MIL 1515
#define FAL 1449
#define SOL 1275
#define LAL 1136
#define SIL 1012

//中音
#define DOM 956
#define REM 852
#define MIM 759
#define FAM 716
#define SOM 638
#define LAM 568
#define SIM 506


//高音
#define DOH 478
#define REH 426
#define MIH 379
#define FAH 358
#define SOH 319
#define LAH 284
#define SIH 254

//停止
#define STOP 65535

//拍数
#define B41 400		//4拍	
#define B31 300
#define B21 200
#define B32 150
#define B11 100		//1拍
#define B34 75
#define B12 50
#define B14 25		//4分之1拍

sbit buzzer=P0^6;

unsigned char th0;//定时器0高8位缓存
unsigned char tl0;//定时器0低8位缓存

//其他音乐可根据谱子以下面方式编写(一个音调加一个拍数)
//极乐净土
int code music1[]=  
{ 
	LAM,B11,LAM,B11,LAM,B11,LAM,B14,DOH,B14,REH,B14,MIH,B14,
	LAM,B11,LAM,B11,LAM,B12,SOM,B12,SOM,B12,LAM,B12,
	LAM,B11,LAM,B11,LAM,B11,LAM,B14,DOH,B14,REH,B14,MIH,B14,
	LAM,B11,LAM,B11,LAM,B12,FAH,B12,FAH,B12,MIH,B12,
	LAM,B11,LAM,B11,LAM,B11,LAM,B14,DOH,B14,REH,B14,MIH,B14,
	LAM,B11,LAM,B11,LAM,B12,LAM,B12,SOM,B12,LAM,B12,
	LAM,B11,LAM,B11,LAM,B14,LAM,B14,DOH,B14,REH,B14,MIH,B14,
	LAM,B32,SOM,B12,SOM,B12,LAM,B12,LAM,B11,
	
	STOP,B32,LAM,B12,LAM,B12,SOM,B14,SOM,B14,SOM,B12,LAM,B12,
	SOM,B12,MIM,B12,MIM,B11,SOM,B14,MIM,B21,
	STOP,B32,LAM,B12,LAM,B12,SOM,B12,LAM,B12,SIM,B12,
	DOH,B11,SIM,B11,LAM,B12,SIM,B14,LAM,B14,SOM,B11,
	0,0//结束标志
};

//爱河
int code music2[]= 
{
	LAM,B12,DOH,B12,REH,B12,REH,B12,REH,B12,REH,B12,REH,B11,MIH,B12,
	FAH,B12,FAH,B11,MIH,B32,DOH,B11,REH,B11,MIH,B11,MIH,B11,SOH,B11,
	LAH,B12,LAH,B11,DOH,B11,DOH,B11,MIH,B11,REH,B21,DOH,B11,REH,B11,MIH,B21,
	
	DOH,B11,REH,B11,MIH,B11,MIH,B11,SOH,B11,LAH,B12,LAH,B11,DOH,B11,
	DOH,B11,MIH,B11,REH,B21,DOH,B11,SIM,B11,DOH,B21,
	0,0//结束标志
};

//ms延时函数
void delay_ms(unsigned int ms) //1ms
{
	unsigned int  i,j;
	for(i=0;i<ms;i++)
	 for(j=0;j<686;j++) //1T模式时 686
	 {
	  _nop_();
	  _nop_();
	  _nop_();
	  _nop_();
	 }
}

//设置频率,即设置定时器的初值以改变溢出频率
void SetFreq(int time)
{
	//定时器0
	th0=(65536-time)>>8;
	tl0=(65536-time)&0x00ff;
	TH0=th0;
	TL0=tl0;
}

//设置频率和节拍
void Buzzer(int Tone, int Beat)
{
	SetFreq(Tone);
	delay_ms(5*Beat);	//改变Beat的系数即可控制音乐的快慢,数字越大越慢
}

//播放音乐
void PlayMusic(int* music)
{
	int tone=0;	//音调索引
	int beat=1;	//拍数索引
	
	while(music[beat]>0)
	{
		Buzzer(music[tone],music[beat]);
		Buzzer(STOP,5);
		tone+=2;
		beat+=2;
	}
}

void Timer0Init(void)		//100微秒@11.0592MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//16位不可自动重装载模式
	TL0 = 0;		//设置定时初值
	TH0 = 0;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}

void main()
{
	Timer0Init();
	EA=1;
	ET0=1;
	P2=(P2&0x1f)|0xa0;buzzer=1;P2&=0x1f;
	while(1)
	{
		PlayMusic(music1);	//改变播放的音乐,只需要改变此函数的参数
	};
}

//定时器0中断服务函数,用于开关定时器
void Time0_Int() interrupt 1
{
	TH0=th0;	//根据音乐不断更新频率
	TL0=tl0;
	
	P2=(P2&0x1f)|0xa0;buzzer=!buzzer;P2&=0x1f;
}