蜂鸣器模块
1.蜂鸣器介绍(我们这个开发板是无源蜂鸣器)
蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号
蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器
有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定
无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音
2.驱动电路
三极管驱动:左图NPN型给高电平(1)导通给低电平(0)断开,右图PNP型给低电平(0)导通给高电平(1)断开。数字电路R1限流电阻,让电路饱和就行了要求不高1KΩ足够
集成电路驱动:我们的开发板就是这种驱动方式。因为单片机的I/O不能直接驱动蜂鸣器,所以要经过ULN2003(驱动芯片)
3.ULN2003
给1输出0,这个0才有驱动能力,所以这个ULN2003芯片逻辑是取反的
4.键盘与音符对照
C~B、c~b、c1~b1、c2~c3分为这四组;不同组相差8度,例如:c1到c2升高8度,c1到c降低8度;从上面看白黑、白黑、白白……每相邻的两个键是相差半音的关系;从下面看C到D这样相邻的两个键是全音的关系;白键(音高)表示:简谱中的1对应中央C1,简谱升高8度为i表示C2,简谱升高两个8度上面加两个点,简谱一个降低8度下面加一个点,简谱降低2个8度下面加两个点;黑键表示:升高半音的符号#,降低半音的符号b,白键升高半音或着降低半音就可以到黑键
1=C四分之四,C调,从下往上读,以四分音符为一拍,每小节有四拍;-表示时长,665-中的5-表示5占2个拍子的时长,221-中的1-表示占了2个拍子的时长,意思是1-这个音的时间是2这个音的两倍
音符:以四分音符为标准500ms,那么半音符就是1s,全音符就是2s,八分音符是250ms。例如:2表示四分音符,2-表示半音符,2----表示全音符。2加下划线表示八分音符,十六音符和三十二音符以八音符类推
1=D四分之四,D调,从下往上读,以四分音符为一拍,每小节有四拍;音符i. 表示500+250s。第14节的圆弧表示,延音,按下去不放
5.音符与频率对照表(不完整)
虽然,不完整,但足以实现我们的蜂鸣器播放音乐功能,完整的可以自行查找
C调全白键,其他调有黑键;中音1表示c1,中音1#表示c1右上角的黑键,以此类推;这个表代表钢琴键盘的c到c3的黑白键盘
我们以a的频率,也就是低音6,当做一个基准频率。从基准频率,开始往上每个数除以下面一个数的2^12次方,往下就是乘以每个数的上一个数的2^12次方,这就是12频分(这是乐理知识,了解即可)。周期=1/频率*1000000。第二张图是重装载值表。
6.蜂鸣器波播发提示音代码
第一步:
延迟模块、数码管(8字灯)、按键模块复制粘贴到现在的工程
实现
第二步:
蜂鸣器模块化代码
最终代码:
模块:
Buzzer.c
#include <REGX52.H>
#include <INTRINS.H>//蜂鸣器端口:
sbit Buzzer=P1^5;/*** @brief 蜂鸣器私有延时函数,延时500us* @param 无* @retval 无*/
void Buzzer_Delay500us() //@12.000MHz
{unsigned char i;_nop_();i = 247;while (--i);
}/*** @brief 蜂鸣器发声* @param ms 发声的时长,范围:0~32767* @retval 无*/
void Buzzer_Time(unsigned int ms)
{unsigned int i;for(i=0;i<ms*2;i++){Buzzer=!Buzzer;Buzzer_Delay500us();}
}
Buzzer.h
#ifndef __BUZZER_H__
#define __BUZZER_H__void Buzzer_Time(unsigned int ms);#endif
main.c
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Buzzer.h"unsigned char KeyNum;void main()
{Nixie(1,0);while(1){KeyNum=Key();if(KeyNum){Buzzer_Time(100);Nixie(1,KeyNum);}}
}
7.音乐代码
小星星代码
第一步:
定时器模块、延迟模块复制粘贴到项目工程
第二步:
在定时器模块中的TL0和TH0只有第一次的时间,也就是只中断第一次。在这个工程里面无关紧要
第三步:
这里蜂鸣器不模块化,单片机性能有限,干了这个就干不了别的了
第四步:
在定时器模块内复制中断函数到主函数,将蜂鸣器的端口翻转加入定时器模块
第五步:
在中断函数里面的TL0和TH0才是这个工程的关键,才是决定以后的第一次以后的中断时间,改这里的值。中断TL0\TH0计数来一个脉冲就计数,时钟,一个为结束标志位,决定中断时间的,时间一到就中断了;20行代码,半个周期翻转一次
第六步:
定义一个数组,将重装载值加进去;64580重装值,到时间从新中断计数,上面一个例子的钢琴键图下面的图中有计算
第七步:
显示第一个低音
Music数组索引表
第八步:
我们根据小星星的乐谱和重装载值数值,再看Music数组索引表定义一个数组里面放小星星的音谱的Music数组,再定义一个变量MusicSelect表示Music的索引;主函数里面,引用数组,按Music依次取出;为了将每个音符分开,就每个音符发出到下一个音符就关闭打开定时器
第九步:
修改,以1/16音符为1,1/4音符为4,八分音符为4+4(或8)
第十步:
我们以125ms乘Music数组里面的4和4+4的元素,这样就可以达到,乐谱里面的拍子。逻辑是怎样的,就第一次循环而言,重装载值数组取12,++后,Delay取4,这样一直循环就完成了一个曲子
第十一步:
定义一个宏,放入Dealy里面,放变改变频率。我们还要在重装载值数组里面定义一个休止符0
因为重装载值数组改动了,所以Music数组也要改动。加入一个结束标志,让音乐停止,不然就数组越界了。也可以在主函数最后,归零重新播放
优化:将重装载值表的低音、中音、高音定义宏,休止符定义宏为P,以后看其他谱子写入Music数组会方便很多
天空之城代码:
天空之城代码和小星星代码,逻辑是一样的。把定义的宏改成500,然后,看音谱重新填入code Music数组(因为Music数组内存不够,所以要存入Flash里面,Flash有8K,但是只能读不能改)即可
最终代码:
模块:
main.c
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"//蜂鸣器端口定义
sbit Buzzer=P1^5;//播放速度,值为四分音符的时长(ms)
#define SPEED 500//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P 0
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36//索引与频率对照表
unsigned int FreqTable[]={0,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 code Music[]={//音符,时值,//1P, 4,P, 4,P, 4,M6, 2,M7, 2,H1, 4+2,M7, 2,H1, 4,H3, 4,M7, 4+4+4,M3, 2,M3, 2,//2M6, 4+2,M5, 2,M6, 4,H1, 4,M5, 4+4+4,M3, 4,M4, 4+2,M3, 2,M4, 4,H1, 4,//3M3, 4+4,P, 2,H1, 2,H1, 2,H1, 2,M7, 4+2,M4_,2,M4_,4,M7, 4,M7, 8,P, 4,M6, 2,M7, 2,//4H1, 4+2,M7, 2,H1, 4,H3, 4,M7, 4+4+4,M3, 2,M3, 2,M6, 4+2,M5, 2,M6, 4,H1, 4,//5M5, 4+4+4,M2, 2,M3, 2,M4, 4,H1, 2,M7, 2+2,H1, 2+4,H2, 2,H2, 2,H3, 2,H1, 2+4+4,//6H1, 2,M7, 2,M6, 2,M6, 2,M7, 4,M5_,4,M6, 4+4+4,H1, 2,H2, 2,H3, 4+2,H2, 2,H3, 4,H5, 4,//7H2, 4+4+4,M5, 2,M5, 2,H1, 4+2,M7, 2,H1, 4,H3, 4,H3, 4+4+4+4,//8M6, 2,M7, 2,H1, 4,M7, 4,H2, 2,H2, 2,H1, 4+2,M5, 2+4+4,H4, 4,H3, 4,H3, 4,H1, 4,//9H3, 4+4+4,H3, 4,H6, 4+4,H5, 4,H5, 4,H3, 2,H2, 2,H1, 4+4,P, 2,H1, 2,//10H2, 4,H1, 2,H2, 2,H2, 4,H5, 4,H3, 4+4+4,H3, 4,H6, 4+4,H5, 4+4,//11H3, 2,H2, 2,H1, 4+4,P, 2,H1, 2,H2, 4,H1, 2,H2, 2+4,M7, 4,M6, 4+4+4,P, 4,0xFF //终止标志
};unsigned char FreqSelect,MusicSelect;void main()
{Timer0Init();while(1){if(Music[MusicSelect]!=0xFF) //如果不是停止标志位{FreqSelect=Music[MusicSelect]; //选择音符对应的频率MusicSelect++;Delay(SPEED/4*Music[MusicSelect]); //选择音符对应的时值MusicSelect++;TR0=0;Delay(5); //音符间短暂停顿TR0=1;}else //如果是停止标志位{TR0=0;while(1);}}
}void Timer0_Routine() interrupt 1
{if(FreqTable[FreqSelect]) //如果不是休止符{/*取对应频率值的重装载值到定时器*/TL0 = FreqTable[FreqSelect]%256; //设置定时初值TH0 = FreqTable[FreqSelect]/256; //设置定时初值Buzzer=!Buzzer; //翻转蜂鸣器IO口}
}