由原理图可知,蜂鸣器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模块,接下来的目标是实现发出不同音调的声响
先来看用定时器实现的蜂鸣器发生,每次中断就翻转一次Buzzer,中断每1ms执行一次,所以翻转周期是2ms,音调一样
#include <REGX52.H>
#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;
}
只要更改中断函数中的定时初值,翻转需要的的时长就会变化,从而改变声音的周期进而改变音调
比如中央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 <REGX52.H>
#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 <REGX52.H>
#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 <REGX52.H>
#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里面,同时使数据变成只可读取,不可更改