1.蜂鸣器的原理
这里的“源”不是指电源。而是指震荡源。 也就是说,有源蜂鸣器内部带震荡源,所以只要一通电就会叫。 而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。必须用2K~5K的方波去驱动它。 有源蜂鸣器往往比无源的贵,就是因为里面含有震荡电路。
1.无源蜂鸣器原理
(1)早期的蜂鸣器都是无源的
(2)内部结构和材料
(3)发生原理:是利用了压电效应的原理
(4)控制信号,是高低电平相间
(5)电路图
(6)音调如何控制:音调受震动频率控制,就等于控制信号的频率。频率越高音调越高,听起来越刺耳。
(7)声音大小如何控制。由硬件决定的。无法写代码取控制声音大小。
2.有源蜂鸣器
1.无源蜂鸣器的缺陷
无源蜂鸣器只能在一定的范围内进行设置,太大或者太小都不行。【外部提供一个方波】
2.内置震荡电路后形成有源蜂鸣器
在内部添加震荡电路,不用使用外部的方波进行控制【但是内部的震荡电路是不可以改变的】
3.有源蜂鸣器也可以用频率信号驱动
可以直接加电也可以直接通过方波进行工作【有源蜂鸣器包含无源蜂鸣器】
4.三极管驱动
5.蜂鸣器发声音频率
2.让蜂鸣器响起来
1.接线确定
2.使用delay让蜂鸣器响起来
#include<reg51.h>
sbit BUZZER=P0^0; //buzzer的驱动引脚
void delay(void){
unsigned char i,j;
for(i=0;i<100;i++){
for(j=0;j<10;j++);
}
}
void main(){
while(1){
BUZZER=1;//将引脚变外高电平
delay();
BUZZER=0;
delay();
}
}
3.调节delay控制音乐的变化
控制delay中的i和j的大小,可以控制音调的振动频率,从而影响声音的尖锐。
如果i和j越大,则音调越小,声音越不尖锐
如果i和j越小,则音调越大,声音越尖锐
4.时钟周期的计算
3.用定时器控制蜂鸣器音调
1.上节驱动方法的问题
(1)不容易精确控制时间
(2)CPU控制蜂鸣器中不能做其他事
2.定时器控制蜂鸣器响
在一定时间后,通过定时器中的中断处理程序取减低蜂鸣器的电平,从控制蜂鸣器的响应
(1)10KHz=>1/10000s=>100us===>高电平+低电平的时间都为50us。所以要定的时间就是50us
(2)外部晶振12MHz+12T设置==>内部时钟频率1MHz===》1us===》TL0=255-50=205,TH0=255
3.注意点1:TL0和TH0的计算
我们在设置TH0和TL0应该将获得的时间取其补码
比如:我们是TL0=50; TH0=0;
则我们应该写入“TL0=205;TH0=255;"
TL0=205;
TH0=0;
TL0=205 % 256;//低位取余
TH0=255/ 256;//高位取商
4.注意点2:定时器的计算
(65536-50000)/256
计数器是16位的,由高8位TH0和低8位TL0组成,可以存储2^16=65536个数,例如当设定计算值为65536-50000=15536时,也就是计数器从15536开始计时,到65536溢出,产生中断,对于晶振频率为12MHz的单片机来说,执行一个机器周期时长为1us,所以这里计时50000us,15536(D)转换为16进制是3CB0(H),此时TH0=3C,TL0=B0分别装入定时器即可,为了免除这些计算步骤,很多编程者采用"TH0=(65536-50000)/256;TL0=(65536-50000)%256",那么为什么要介入256呢?我们可以做一下运算,256(D)=0100(H),512(D)=0200(H),512(D)有两个256,所以高8位就是02,那么15536有多少个256?就是15536/256个,就相当于高8位有多少数值,商存入高8位,剩下的不足一个256,存入低8位,15536%256。
直接使用宏定义
【注意点】我们51是加法计数器,所以是65535-US
//宏定义一个时钟频率
#define XKHZ 10 //10*10的三次方Hz 要定多少Khz,就定义在这里
//宏定义us
//这里我们除以2,是因为想要分给TH0和TL0
//1000---》1000ms
#define US (1000/XKHZ)/2
sbit BUZZER=P0^0; //buzzer的驱动引脚
//【注意点】因为51单片机是加法计数器,所以实际上我们要算的范围
//应该是65535-US,而不是0-US
#define N (65535 -US)
void delay(void){
unsigned char i,j;
for(i=0;i<100;i++){
for(j=0;j<10;j++);
}
}
void timer0_isr(void) interrupt 1 using 1{
//这里再一次赋值,是因为我们想要他循环,所以我们要在他每一次进来的时候重新赋值
TL0=N % 256;//低位取余
TH0=N / 256;//高位取商
BUZZER=!BUZZER;
}
4.蜂鸣器发出滴滴声音
通过count可以控制有声音和无声音的长短
#include<reg51.h>
/**
用定时器控制蜂鸣器音调
*/
//宏定义一个时钟频率
#define XKHZ 10 //10*10的三次方Hz 要定多少Khz,就定义在这里
#define US (1000/XKHZ)/2
sbit BUZZER=P0^0; //buzzer的驱动引脚
#define N (65535 -US)
//计数器
unsigned int count;
//判断此时是从”有声音“到"没声音”,还是从“没声音”到“有声音”
unsigned char flag=0; //flag=0表示有声音,flag=1表示没有声音
void delay(void){
unsigned char i,j;
for(i=0;i<100;i++){
for(j=0;j<10;j++);
}
}
void timer0_isr(void) interrupt 1 using 1{
//这里再一次赋值,是因为我们想要他循环,所以我们要在他每一次进来的时候重新赋值
TL0=N % 256;//低位取余
TH0=N / 256;//高位取商
if(count--==0){
//说明到了翻转的时候了
//count=5000;//记得重新赋值,要不然就只能响一次
if(flag==0){
//说明之前处于有声音的,说明本次是从有声音到无声音的翻转
flag=1;
//此时“无声音”比“有声音”的时间还长了3倍
count=600*3;
}else{
//说明之前没声音的,说明本次是没声音到有声音的翻转
flag=0;
//下面这句话,加上了,则表示在翻转的时候也会出现声音
BUZZER=!BUZZER;
count=600;
}
}else{//常规情况,也就是不翻转时候
if(flag==0){
BUZZER=!BUZZER; //4999次声音
}else{
//空的,因为不进行任何IO操作就是没有声音
}
}
}
void main(){
//【第一步】初始化:我们使用的是定时器T0
TMOD=0x01; //T0使用16位bit定时器
//********************************************************
TL0=N % 256;//低位取余
TH0=N / 256;//高位取商
//********************************************************
//打开计数器;TCON中的TR0【定时器T0的运行控制位】
TR0=1; //T0打开开始计数
//T0的中断溢出位,表示允许中断
ET0=1; //T0中断允许
EA=1; //打开中断允许
BUZZER=1;
//设置响和不响的周期时间
count=5000; //5000*100us=500ms
//初始化,有声音
flag=0;
}
5.让蜂鸣器唱歌
1.为什么蜂鸣器可以唱歌
(1)发声音频可变---》延迟函数(delay)
(2)发声音长度可变---》定时器
unsigned char i;
for(i=0;i<200;i++){//控制声音响应时间长短
Sound=~Sound;
DelayXms(1);//控制声音的不同
}
for(i=0;i<50;i++){
Sound=~Sound;
DelayXms(2);
}
2.分析写好的唱歌程序
(1)复制代码过去
(2)修改控制蜂鸣器的IO引脚定义
3.”code“关键字的使用
因为我们加入的歌曲的编码是固定不变的,但是51单片机的内存有限制,所以我们只能把歌曲的编码放在常量区中,才使得其不会占据内存。则加上“code”关键字。
unsigned char code music_tab[] =
{
0x18, 0x30, 0x1C , 0x10, //格式为: 频率常数, 节拍常数, 频率常数, 节拍常数,
0x20, 0x40, 0x1C , 0x10,
0x18, 0x10, 0x20 , 0x10,
0x1C, 0x10, 0x18 , 0x40,
0x1C, 0x20, 0x20 , 0x20,
0x1C, 0x20, 0x18 , 0x20,
0x20, 0x80, 0xFF , 0x20,
0x30, 0x1C, 0x10 , 0x18,
0x20, 0x15, 0x20 , 0x1C,
}
4.音节的构成
0x18, 0x30, 0x1C , 0x10,【2个一组】
(1)音调【振动频率决定】:0x18【奇数次】
(2)音长:0x30【偶数次】
注意点:
5.音频 VS 音调
(1)音节:定时器T0控制的是音乐的节拍(某一个音节持续时间)而不管音调(频率)
(2)音调【音频】:是直接使用delay做出来的,控制发出什么样子的声音
/************************************************************************
[文件名] C51音乐程序(八月桂花)
[功能] 通过单片机演奏音乐
/**********************************************************************/
#include <REG51.H>
//提供移位函数,可以省略
//#include <INTRINS.H>
//本例采用89C52, 晶振为11.0592MHZ
//关于如何编制音乐代码, 其实十分简单,各位可以看以下代码.
//频率常数即音乐术语中的音调,而节拍常数即音乐术语中的多少拍;
//所以拿出谱子, 试探编吧!
sbit Beep = P0^0 ; // 要根据实际的接线来修改
unsigned char n = 0; //n为节拍常数变量
unsigned char code music_tab[] =
{
0x18, 0x30, 0x1C , 0x10, //格式为: 频率常数, 节拍常数, 频率常数, 节拍常数,
0x20, 0x40, 0x1C , 0x10,
0x18, 0x10, 0x20 , 0x10,
0x1C, 0x10, 0x18 , 0x40,
0x1C, 0x20, 0x20 , 0x20,
0x1C, 0x20, 0x18 , 0x20,
0x20, 0x80, 0xFF , 0x20,
0x30, 0x1C, 0x10 , 0x18,
0x20, 0x15, 0x20 , 0x1C,
0x20, 0x20, 0x20 , 0x26,
0x40, 0x20, 0x20 , 0x2B,
0x20, 0x26, 0x20 , 0x20,
0x20, 0x30, 0x80 , 0xFF,
0x20, 0x20, 0x1C , 0x10,
0x18, 0x10, 0x20 , 0x20,
0x26, 0x20, 0x2B , 0x20,
0x30, 0x20, 0x2B , 0x40,
0x20, 0x20, 0x1C , 0x10,
0x18, 0x10, 0x20 , 0x20,
0x26, 0x20, 0x2B , 0x20,
0x30, 0x20, 0x2B , 0x40,
0x20, 0x30, 0x1C , 0x10,
0x18, 0x20, 0x15 , 0x20,
0x1C, 0x20, 0x20 , 0x20,
0x26, 0x40, 0x20 , 0x20,
0x2B, 0x20, 0x26 , 0x20,
0x20, 0x20, 0x30 , 0x80,
0x20, 0x30, 0x1C , 0x10,
0x20, 0x10, 0x1C , 0x10,
0x20, 0x20, 0x26 , 0x20,
0x2B, 0x20, 0x30 , 0x20,
0x2B, 0x40, 0x20 , 0x15,
0x1F, 0x05, 0x20 , 0x10,
0x1C, 0x10, 0x20 , 0x20,
0x26, 0x20, 0x2B , 0x20,
0x30, 0x20, 0x2B , 0x40,
0x20, 0x30, 0x1C , 0x10,
0x18, 0x20, 0x15 , 0x20,
0x1C, 0x20, 0x20 , 0x20,
0x26, 0x40, 0x20 , 0x20,
0x2B, 0x20, 0x26 , 0x20,
0x20, 0x20, 0x30 , 0x30,
0x20, 0x30, 0x1C , 0x10,
0x18, 0x40, 0x1C , 0x20,
0x20, 0x20, 0x26 , 0x40,
0x13, 0x60, 0x18 , 0x20,
0x15, 0x40, 0x13 , 0x40,
0x18, 0x80, 0x00
};
// T0定时控制的是音乐的节拍(某一个音节持续的时间)而不管音调(频率)
// 音调是直接使用delay做出来的。
void int0() interrupt 1 //采用中断0 控制节拍
{
TH0 = 0xd8;
TL0 = 0xef;
n--;
}
void delay (unsigned char m) //控制频率延时
{
unsigned i = 3 * m;
while (--i);
}
void delayms(unsigned char a) //豪秒延时子程序
{
while (--a); //采用while(--a) 不要采用while(a--); 各位可编译一下看看汇编结果就知道了!
}
void main()
{
unsigned char p, m; // m为频率常数变量
unsigned char i = 0;
//此处表示将:低4位留下了,高4位去除
TMOD &= 0x0f;
TMOD |= 0x01; // timer0 工作在模式1,16位定时器下
TH0 = 0xd8;
TL0 = 0xef; // 这个TH和TL的值合起来定了1个10ms左右的一个时间
IE = 0x82;
play:
while (1)
{
a: p = music_tab[i]; //表示指向第一个
if (p == 0x00) // 一遍播放完了,延时1s后自动开始下一遍
{
i=0;
delayms(1000); //表示播放完一次,延迟1s,接着下一次播放
//如果想要播放一次,则下面goto注释
goto play; //跳转接着播放
} //如果碰到结束符,延时1秒,回到开始再来一遍
else if (p == 0xff) //0xff:休止符
{
i = i + 1;//跳过这一组数据
delayms(100);
TR0 = 0; //TR0:关闭定时器
goto a;
} //若碰到休止符,延时100ms,继续取下一音符
else //常规情况p==正常情况
{
m = music_tab[i++];
n = music_tab[i++];
} // m取频率常数【A,B,C】 和 n取节拍常数 【1/2,1/3,1/4】
//打开开定时器1
TR0 = 1;
while (n != 0) //节拍不等于0
{
Beep = ~Beep; //修改蜂鸣器电平 【~】与【!】一样
delay(m); //等待节拍完成, 通过P1口输出音频(可多声道哦!)
}
TR0 = 0; //关定时器1
}
}
6.切歌,暂停功能
单片机应用番外篇——蜂鸣器的应用之可实现切歌、暂停功能的简单音乐盒_哔哩哔哩_bilibili
/**
使用中断处理程序控制音乐的暂停和播放,切换
*/
//外部中断初始化
void EX_init(){
IT0=1;//下降沿触发方式 INT0
IT1=1;//下降沿触发方式 INT1
EX0=1; //外部中断0中断允许位
EX1=1; //打开中断开关
EA=1;//打开总的中断开关
}
//暂停功能
void EX0_isr() interrupt 0
{
DelayXms(10);//消除抖动
if(INT0==0)//这里是INT1已经定义了,对应P3.2这个IO口,可以直接使用
{
//暂停通过定时计数器,则将计数器进行取反
TR0=~TR0;
TR1=~TR1;
}
}
//切歌功能
void EX1_isr() interrupt 2
{
DelayXms(10);//消除抖动
if(INT1==0)//这里是INT1已经定义了,对应P3.2这个IO口,可以直接使用
{
state++;
if(state==3){ //因为此时我们只有3曲歌
state=0;
}
}
}
unsigned char state=0;//控制切歌
void main(){
EX_init();
InitialSound();
while(1){
switch(state){ //这个切歌,通过外部中断1,则进行转换
case 0:
Play(Music_Girl,0,2,345);break;
case 1:
Play(Music_haw,0,23,543);break;
default:break;
}
}
}