51单片机学习--蜂鸣器播放音乐

51单片机学习--蜂鸣器播放音乐_第1张图片
51单片机学习--蜂鸣器播放音乐_第2张图片
由原理图可知,蜂鸣器BEEP与P1_5 相关,但其实这个原理图有错,实测接的是P2_5
下面这个代码就是以500HZ的频率响500ms的例子

sbit Buzzer = P2^5;

unsigned char KeyNum;
unsigned int i;

void main()
{
	while(1)
	{
		KeyNum = Key();
		if(KeyNum)
		{
			for(i = 0; i < 500; i ++) {
				Buzzer = !Buzzer;
				Delay(1);// 1ms翻转一次,周期就是2ms,频率就是500HZ 
			} //一共会响500ms
			
		}
		
	}
}

接下来先把这个发出声响的代码封装成Buzzer模块,接下来的目标是实现发出不同音调的声响



51单片机学习--蜂鸣器播放音乐_第3张图片
先来看用定时器实现的蜂鸣器发生,每次中断就翻转一次Buzzer,中断每1ms执行一次,所以翻转周期是2ms,音调一样

#include 
#include "Timer0.h"
#include "Delay.h"


sbit Buzzer = P2^5;



void main()
{
	Timer0_Init();
	
	while(1)
	{
		
	}
}


void Timer0_Routine() interrupt 1
{
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	Buzzer = !Buzzer;
}

只要更改中断函数中的定时初值,翻转需要的的时长就会变化,从而改变声音的周期进而改变音调
51单片机学习--蜂鸣器播放音乐_第4张图片
比如中央C的重装载值是64580,这个声调对应的初值如下

void Timer0_Routine() interrupt 1
{
	TL0 = 64580 % 256;		//设置定时初值
	TH0 = 64580 / 256;		//设置定时初值
	Buzzer = !Buzzer;
}

接下来只要稍微费点力气,把所有重装载值整理到一个数组里,使数组的每个序号对应一个音调,需要的时候调用序号就行

unsigned int FreqTable[] = { //中央C以及高八度低八度的音调由低到高
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283
};

扒出小星星的简谱,把音调对应的数组序号存到一个Music数组里,按序遍历这个数组就能比较完整地播放一首小星星啦

#include 
#include "Timer0.h"
#include "Delay.h"


sbit Buzzer = P2^5;

unsigned int FreqTable[] = {
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283
};

unsigned char FreqSelect, MusicSelect;

unsigned char Music[] = {12,12,19,19,21,21,19,17,17,16,16,14,14,12};
//小星星简谱音调对应的数组下标(前两句)

void main()
{
	Timer0_Init();
	
	while(1)
	{
		FreqSelect = Music[MusicSelect];
		MusicSelect ++;
		Delay(500); //音符时长,可修改
		TR0 = 0; //定时器关闭,自然停顿,否则两个连音之间没有了间隔
		Delay(5);
		TR0 = 1; //定时器开启
	}
}


void Timer0_Routine() interrupt 1
{
	TL0 = FreqTable[FreqSelect] % 256;		//设置定时初值
	TH0 = FreqTable[FreqSelect] / 256;		//设置定时初值
	Buzzer = !Buzzer;
}


但这样还不完美,每个音符只能固定地响500ms,想要自主控制音符的时长,则又需要一个数组来记录每个音符的长度!十六分音符记为1,八分音符记为2,四分音符记为4。。。。。

#include 
#include "Timer0.h"
#include "Delay.h"


sbit Buzzer = P2^5;

unsigned int FreqTable[] = {
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283
};

unsigned char FreqSelect, MusicSelect;

unsigned char Music[] = {12,12,19,19,21,21,19,17,17,16,16,14,14,12};
//小星星简谱音调对应的数组下标(前两句)

unsigned char Music2[] ={4,4,4,4,4,4,8,4,4,4,4,4,4,8};
//每个音符的时长

void main()
{
	Timer0_Init();
	
	while(1)
	{
		FreqSelect = Music[MusicSelect];
		Delay(125 * Music2[MusicSelect]);
		MusicSelect ++;
		TR0 = 0; //定时器关闭,自然停顿,否则两个连音之间没有了间隔
		Delay(5);
		TR0 = 1; //定时器开启
	}
}


void Timer0_Routine() interrupt 1
{
	TL0 = FreqTable[FreqSelect] % 256;		//设置定时初值
	TH0 = FreqTable[FreqSelect] / 256;		//设置定时初值
	Buzzer = !Buzzer;
}


最后还有一点,歌曲中不是一直都有声音的,所以需要有休止符,不发声,在音调数组的最后加上一个0,这样Music数组中也能存休止符了,中断函数中特判一下,不为0时才发声
以下就是一个比较完整地蜂鸣器播放音乐的代码了!

#include 
#include "Timer0.h"
#include "Delay.h"


sbit Buzzer = P2^5;

unsigned int FreqTable[] = {
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
	0
}; //最后一个是休止符

unsigned char FreqSelect, MusicSelect;

unsigned char Music[] = {12,12,19,19,21,21,19,17,17,16,16,14,14,12};
//小星星简谱音调对应的数组下标(前两句)

unsigned char Music2[] ={4,4,4,4,4,4,8,4,4,4,4,4,4,8};
//每个音符的时长

void main()
{
	Timer0_Init();
	
	while(1)
	{
		FreqSelect = Music[MusicSelect];
		Delay(125 * Music2[MusicSelect]);
		MusicSelect ++;
		TR0 = 0; //定时器关闭,自然停顿,否则两个连音之间没有了间隔
		Delay(5);
		TR0 = 1; //定时器开启
	}
}


void Timer0_Routine() interrupt 1
{
	if(FreqTable[FreqSelect] != 0) //若不为休止符
	{
		TL0 = FreqTable[FreqSelect] % 256;		//设置定时初值
		TH0 = FreqTable[FreqSelect] / 256;		//设置定时初值
		Buzzer = !Buzzer;
	}
}

最后一个问题,当歌曲音符比较多变化复杂的时候,数据量巨大,数组存不下,那么就要用到之前的方法,在Music数组前加上关键字code ,把数据存到flash里面,同时使数据变成只可读取,不可更改

你可能感兴趣的:(51单片机,51单片机,学习,嵌入式硬件,单片机)