赞
踩
由原理图可知,蜂鸣器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里面,同时使数据变成只可读取,不可更改
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。