一、蜂鸣器的分类及工作原理

1. 蜂鸣器的种类

结构上可分为压电式蜂鸣器电磁式蜂鸣器

        ① 压电式蜂鸣器是通过压电陶瓷的压电效应原理工作的。当加有交变电压时,压电陶瓷产生机械振动,使蜂鸣器发声。这种蜂鸣器通常采用一体化结构,具有频率稳定、音色优美、体积小、重量轻、耗电量低等优点。

        ② 电磁式蜂鸣器则是利用电磁感应原理工作的。它主要由线圈和磁铁组成,当电流通过线圈时产生磁场,与磁铁相互作用,使蜂鸣器发声。这种蜂鸣器通常采用分体结构,具有声音大、频率范围宽、重量重等优点。

按照驱动方式分为有源蜂鸣器无源蜂鸣器:(这里的“源”指的是“振荡源”)

        ① 有源蜂鸣器内部包含一个振荡电路,能将恒定的直流电转化为一定频率的脉冲信号,从而实现磁场交变,带动蜂鸣器振动发音。它不需要额外的驱动电路,只要接通直流电源即可工作。有源蜂鸣器工作原理是靠压电效应,即利用压电材料(如压电陶瓷、压电薄膜等)在受到外力作用时产生电压的特性,将电能转化为声能。
        有源蜂鸣器的优点是程序控制方便,接通直流电源就可以工作,程序简单。但由于内部包含振荡电路,因此成本相对较高,且声音频率固定

        ② 无源蜂鸣器内部不带振荡源,因此不能直接将直流电转化为声能。它需要接收 2K~5K 的方波信号才能工作,通过外部电路产生振荡信号来驱动蜂鸣器发音。通过调整输入方波信号的占空比,可以控制蜂鸣器的音量;改变输入方波信号的频率,可以控制蜂鸣器的音调(音量指声音的强弱,即声音的振动幅度;音调指声音的高低,即声音的振动频率)
        无源蜂鸣器的优点是价格相对较低,且声音频率可控,可以实现音乐中的基本七音符效果。但由于需要额外的驱动电路,因此在使用上相对较复杂。

2. 驱动电路

        在蜂鸣器电路中,蜂鸣器电流通常相对较大,这就意味着直接将蜂鸣器连接到单片机的输出引脚可能会超过设备的最大驱动能力,从而导致单片机的输出引脚过载,因此可能会导致设备损坏或者蜂鸣器无法正常工作。为了解决这个问题,一般常用“三极管驱动”“集成电路ULN2003驱动”的方式进行实现。

        ① 三极管驱动

22b053511f434243853a0f91df2be29e.png

        三极管具有放大电流的能力,它可以将单片机或其他控制设备的微小电流放大到足以驱动蜂鸣器的电流。这样,既可以保护单片机或其它控制设备不被过大的电流损坏,也可以保证蜂鸣器能够正常工作。此外,还加了一个100欧姆的电阻作为限流电阻,用来限制电流的大小,防止由于电流过大而可能对电路中的其他元件造成损害。

        此外还加了一个 D4二极管,这个二极管称为续流二极管。蜂鸣器是感性器件,当三极管导通给蜂鸣器供电时,就会有导通电流流过蜂鸣器。电感的一个特点就是电流不能突变,导通时电流是逐渐加大的,这点没有问题,但当关断时,“电源-三极管-蜂鸣器-地”这条回路就截断了,过不了任何电流,那么储存的电流往哪儿去呢,就是经过D4 和蜂鸣器自身的环路来消耗掉了,从而就避免了关断时由于电感电流造成的反向冲击。接续关断时的电流,这就是续流二极管名称的由来。

        ② 集成电路ULN2003驱动

aaad1eb1fcfa4733be03c28b761972e6.jpeg

  ULN2003是一个单片高电压、高电流的达林顿晶体管阵列集成电路,可以直接驱动蜂鸣器、继电器等负载。它是由7对NPN达林顿管组成的,单个达林顿对的集电极电流为500mA,达林顿管并联还可以承受更大的电流。该电路具有高电压输出特性,最高输出电压可达50V。同时,电路中的阴极钳位二极管可以转换感应负载,并且吸收从感应负载流出的反向电流,防止晶体管受损。此电路主要应用于继电器驱动器、字锤驱动器、灯驱动器、显示驱动器(LED气体放电)、线路驱动器和逻辑缓冲器。每对达林顿管都有一个2.7kΩ的串联电阻,可以直接和TTL或5V CMOS装置相连(达林顿管的基极足够灵敏,可以响应来自开关或直接来自TTL或5V CMOS逻辑门的任何小输入电流)。

        达林顿管,也被称为复合管,是一种电子元件,由两个晶体管以串联的方式连接而成,形成一个等效的新晶体管。这种等效三极管的放大倍数是原始两个三极管的乘积,因此它具有超高放大倍数的特性。达林顿管的作用通常是在高灵敏度放大电路(例如大功率开关电路)中放大非常小的信号。在电子电路设计中,达林顿连接常用于功率放大器和稳压电源。

二、相关问题

1. 如何发出对应音符的声音(只针对无源蜂鸣器)

        通过学习上面的内容我们知道,声音的高低对应不同的震动频率,即不同的音符对应不同的输入信号频率,我们可以通过改变外部输入的方波信号的频率来实现不同声音的播放

2. 每个声音保持的时间

        为了后续编程方便,我们将每个声音保持的最小时长称为一个节拍,按照节拍数对声音的保持时间进行设置。

3. 如何实现音乐的播放

        对于我们这些对音乐一窍不通的人来说可以利用相关软件工具,Music Encode软件会根据输入的歌曲音符自动生成相应的代码程序。用蜂鸣器来输出音乐,仅仅是好玩而已,应用很少,里面包含了音阶、乐谱的相关内容,只做简单的了解即可。

三、代码示例

        1. 报警提示

//按键按下,响0.5s,停0.5s,循环5次。

#include <reg52.h>

sbit buzz=P2^2;   //将P2端口的第2位定义为名为buzz的位变量,并将其与蜂鸣器输入引脚相连 
sbit key1=P2^7;   //将P2端口的第7位定义为名为key1的位变量,并将其与按键引脚相连

bit start;   //定义一个名为start的位变量,用于控制报警的触发,为1时进行触发

unsigned char num;   //响停循环的次数

//单位延时函数 延时1ms
void Delay(unsigned int xms)		//@12.0000MHz
{
	unsigned char i, j;
	while(xms)
	{	//_nop_();
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}

void baojing()
{
	for(num=10;num>0;num--)   //实现响停循环5次
	{
		buzz=~buzz;   //通过按键的取反,实现响停的控制

		Delay(500);   //期间停0.5s   
	}
}

void main()
{
	buzz=1;
	while(1)
	{
		if(!key1)
		{
			Delay(10);
			if(key1==0)   //判断按键按下
				start=1;
		}
		if(start)
		{
			baojing();
			start=0;
		}
	}
}

       

        2. 播放7个基本音符

#include <reg52.h>

sbit buzz=P2^2;   //将P2端口的第2位定义为名为buzz的位变量   蜂鸣器输入方波信号信号
sbit key1=P2^7;   //将P2端口的第7位定义为名为key1的位变量   连接按键作为开始开关
bit start;   //定义一个名为start的位变量  表示按键状态的中间变量
    
unsigned char num;   //3×7=21个音的计数   
unsigned int timer;   //定时器初值的设置变量

//7个基本音在低音、中音、高音下对应的频率
unsigned int pinlv[]={466,523,587,659,698,784,880,   //低音
	                932,1047,1175,1319,1397,1568,1760,   //中音
                  1865,2094,2351,2633,2792,3134,3517};   //高音

//延时子程序 用于按键的消抖
void Delay1ms(unsigned int ms)		//@12.000MHz
{
	unsigned char i, j;
	while(ms)
	{	//_nop_();
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		ms--;
	}
}

//按节拍数调用的延时子程序 一个节拍250ms
void Delay250ms(unsigned char t)		//@12.000MHz
{
	unsigned char data i, j, k;
    while(t)
    {
	    //_nop_();
	    i = 2;
	    j = 231;
	    k = 91;
	    do
	    {
		    do
		    {
			    while (--k);
		    } while (--j);
	    } while (--i);
        t--;
    }
}

//声音播放					
void play()
{
	for(num=0;num<21;num++)   //按照顺序播放21个y音
	{
        TR0=1;   //定时器T0开始工作

        //定时器T0的内部中断信号的频率为12MHz
		timer=65536-(12000000/12)/(pinlv[num]*2);   //数组中第num个频率对应的初值

		TH0=timer/256;
		TL0=timer%256;   //对定时器赋初值
	
		Delay250ms(1);   //进行一节拍的延时
        //在此期间timer的值保持不变,始终以当前的timer值进行中断
	}	
	TR0=0;   //21个音播放完,使定时器停止工作
}

void timer0() interrupt 1
{	
	TH0=timer/256;
	TL0=timer%256;   //在节拍延时期间,用同一timer对定时器赋初值

	buzz=~buzz;   //通过对buzz取反,实现高低电平的切换,近似认为是方波信号	
}

void main()
{
	buzz=1;   //输入信号初始化

	TMOD=0x01;   //定时器初始化

	EA=1;   //闭合中断总开关
	ET0=1;   //闭合定时器T0的中断总开关

	while(1)
	{
		if(!key1)   //检测按键是否按下
		{
			Delay1ms(10);   //按键消抖
			if(key1==0)
				start=1;   //如果按键按下,将start置1
		}
		if(start)   
		{
			play();   //如果按键按下,执行“播放声音”子程序
			start=0;   //并将按键状态变量进行清0,防止持续执行“播放声音”子程序
		}
	}
}

        语句“timer=65536-(12000000/12)/(pinlv[num]*2);”解释:若pinlv[num]=1000Hz,表明每秒触发1000次,由于是通过对buzz状态的取反表示方波信号,所以每秒对buzz取反1000×2=2000次;12000000÷12是因为内部分频器会对12MHz进行12分频(记住就行);计数器加1用时(12/12000000)s,目标用时为(1/2000),所以对应初值(1/2000)/(12/12000000),即对buzz实现一次状态取反计数器需要加1的次数。

        3. 播放音乐

为了方便大家直接进行粘贴试验,将全部代码程序放在了一个.c文件中。

完全理解下面的程序之后,可以自己动手尝试对下面程序中的“InitTimer、Delay1ms、led、delay、play”函数进行模块化。

//三个按键分别实现“暂停/继续”、“下一首”、“模式切换”等功能,LED还会配合声音进行相应的显示。

#include <reg52.h>

sbit key1=P3^2;   //将P3端口的第2位定义为名为key1的位变量,Int0,状态切换:暂停与播放
sbit key2=P3^3;   //将P3端口的第3位定义为名为key2的位变量,Int1,下一首
sbit key3=P3^5;   //将P3端口的第5位定义为名为key1的位变量,T1,模式切换:单曲循环和顺序播放

sbit buzz=P2^2;   //将P2端口的第2位定义为名为buzz的位变量,蜂鸣器

bit xhmode=0;   //定义一个名为mode的位变量,作为“模式切换”的控制变量 0循环播放 1单曲循环
bit zanting;   //定义一个名为mode的位变量,作为“状态切换”的控制变量 0播放 1暂停

//beat变量表示没饿音符对应的节拍数;num变量作为“下一首”功能的中间控制变量
unsigned char beat=1,num=1;  
//k变量表示歌曲数组中的每个音符频率对应的7个基本音符频率;timer变量用于定时器初值的设置变量 
unsigned int k=0,timer;   
unsigned long index=0;   //index变量作为3个歌曲频率数组中对应数据的索引变量

//《我和我的祖国》数据表
code unsigned char zuguo[]={125,126,125,124,123,122,161,165,121,
123,221,127,136,113,165,165,126,127,126,125,124,123,162,
166,127,126,125,125,131,112,163,163,125,126,125,124,123,
122,161,165,121,123,221,127,232,211,166,166,221,127,126,
165,126,125,124,163,147,126,145,122,161,161,221,222,223,
222,221,126,127,136,113,165,165,221,222,223,222,221,126,
127,135,113,166,166,125,124,123,162,127,116,116,125,163,
164,142,121,161,141,120,0};

//《孤勇者》数据表
code unsigned char guyongzhe[]={120,116,117,211,212,117,211,221,
211,117,211,212,117,211,221,211,212,213,212,213,212,223,
213,212,223,225,223,116,117,211,212,117,211,221,211,117,
211,212,117,211,221,211,212,213,212,213,212,223,213,212,
223,225,223,225,233,215,233,215,213,215,216,213,225,225,
233,215,233,215,213,215,216,213,225,215,215,213,212,222,
222,211,213,213,212,222,222,211,211,146,110,215,215,213,
212,222,222,211,213,213,212,222,222,211,211,146,140,0};

//《少年》数据表
code unsigned char shaonian[]={120,221,222,221,225,225,225,213,
215,225,223,223,222,222,222,222,211,213,242,222,223,126,
221,221,116,116,126,221,222,221,224,224,224,223,224,223,
222,221,225,225,225,213,215,225,223,223,222,222,222,222,
211,213,242,222,223,126,221,221,126,116,221,211,221,140,0};

code unsigned int pinlv[]={466,523,587,659,698,784,880,   //低音
	                932,1047,1175,1319,1397,1568,1760,   //中音
                  1865,2094,2351,2633,2792,3134,3517};   //高音

//中断配置函数
void InitTimer()
{
	TMOD=0x61;   //0110 0001 T1用作计数器,工作模式为模式2;T0用作定时器,工作模式为模式1

	TH1=0xff;   //1111 1111 对应十进制数65535,接收到一个下降沿引发中断
	TL1=0xff;   //对计数器T1进行赋初值操作

    //闭合中断总开关,闭合Int0、Timer0、Int1、Timer1中断开关
	IE=0x8f;   //1000 1111 EA=1;ET1=1;EX1=1;ET0=1;EX0=1; 

	IT0=1;
	IT1=1;   //两外部中断Int0和Int1的触发方式为下降沿触发

	PT0=1;   //提高定时器T0的优先级,应为T0要实现方波信号的模拟输入

	TR1=1;   //计数器T1开始工作
}

//延时函数 实现1ms的延时,用于按键的消抖 需放置在子函数“int0、int1、timer1”之前
void Delay1ms(unsigned int xms)		//@12.0000MHz
{
	unsigned char i, j;
	while(xms)
	{	//_nop_();
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}

//实现无源蜂鸣器所需频率的高低电平切换
void timer0() interrupt 1   
{
	TH0=timer>>8;
	TL0=timer;
                  
	buzz=~buzz;   //通过对buzz取反,进行高低电平的切换,实现方波信号的模拟输入
}

//对应按键key1,实现“暂停和播放”功能
void int0() interrupt 0   
{
	EX0=0;   //避免极短时间内产生多个中断,在进入中断服务程序之后,就关闭当前中断允许   

	Delay1ms(10);   //按键消抖

	if(key1==0)
		zanting=~zanting;   //如果按键key1按下,对状态控制变量进行取反	

	EX0=1;   //中断服务程序执行完毕,重新开启当前中断允许
}

//对应按键key2,实现“下一首”功能
void int1() interrupt 2   
{
	EX1=0;   //避免极短时间内产生多个中断,在进入中断服务程序之后,就关闭当前中断允许

	Delay1ms(10);   //按键消抖
	if(key2==0)   
	{
		num++;   //若按键被按下,num加1,开始播放下一首

		index=0;   //如果按键key2按下,对index赋0,确保在演奏下一首歌曲时从头开始播放

		if(num==4)
			num=1;   //播放完第3首,下次再播放第1首
	}

	EX1=1;   //中断服务程序执行完毕,重新开启当前中断允许
}

//对应按键key3,实现“单曲循环和顺序播放”模式切换功能
//用作计数器,所设初值满足接收到一次有效信号就会引发中断,且工作在模式2下不用重复进行赋初值操作
void timer1() interrupt 3   
{
	ET1=0;   //避免极短时间内产生多个中断,在进入中断服务程序之后,就关闭当前中断允许

	Delay1ms(10);   //按键消抖

	if(key3==0)
		xhmode=~xhmode;   //若按键key3被按下,对状态变量xhmode进行取反

	ET1=1;   //中断服务程序执行完毕,重新开启当前中断允许
}

//显示子程序,需放置在子函数“play”之前
void led(unsigned char LD)
{
	P0 = 0xff;   //赋初值

	switch (LD){
		case 0:P0 = 0xff;break;
		case 1:P0 = 0xfe;break;
		case 2:P0 = 0xfc;break;
		case 3:P0 = 0xf8;break;
		case 4:P0 = 0xf0;break;
		case 5:P0 = 0xe0;break;
		case 6:P0 = 0xc0;break;
		case 7:P0 = 0x80;break;   //7个灯对应7个基本音调
	}
}

//节拍延时子程序,需放置在子函数“play”之前
void delay (unsigned char t)
{
	unsigned long i;
	char j;
	for(j=t;j>0;j--)
		for(i=0;i<4000;i++);

	TR0=0;
}

//播放子程序
void play(unsigned char gequ[])   //定义一个形参字符数组,实参数组为3个歌曲数组
{
    //循环播放的实现
	if(gequ[index]==0)   //歌曲对应的频率数组中最后一位都是0,开始对播放模式进行检测
	{
		TR0=0;   //歌曲在切换期间,使定时器T0停止运行,即停止高低电平的输送

		index=0;   //对歌曲数据索引变量赋0,确保在演奏下一首歌曲时从头开始播放

        //如果xhmode为0,num加1,循环播放;如果xhmode不为0,num保持不变,单曲循环
		if(!xhmode)   
		{
			num++;   

			if(num==4)
				num=1;   //播放完第3首,下次再播放第1首
		}
	}
	else
	{

//“后面两首歌曲对应的频率数组中第一位”和“三首歌曲对应的频率数组中的倒数第二位”都是10的倍数,
//让定时器T0停止工作,使得“新歌曲”播放之前会有一段“无声”的时间,作用是便于区分不同歌曲的播放。
		if(gequ[index]%10==0)   

			TR0=0;   //定时器T0停止工作

		else	
		{
            //歌曲数组中的每个音符频率对应的7个基本音符频率
			k=gequ[index]%10+(gequ[index]/100-1)*7-1;   

			timer=65536-(12000000/12)/(pinlv[k]*2);   //pinlv数组中第k个频率对应的初值

			TH0=timer>>8;
			TL0=timer;   //对定时器T0赋初值

            //“暂停”和“播放”的判断
			while(zanting);   //当变量zanting为1时(处于暂停状态),持续循环等待直到暂停为0

			TR0=1;   //定时器T0开始工作
		}

        //知道beat变量代表什么就行,无需知道相应音符对应的节拍数为什么要这样计算
		beat=gequ[index]/10%10;   //通过每个音符对应的频率计算该音符保持的节拍数
		
		led(gequ[index]%10);   

		delay(beat);   //进行相应节拍数的延时

		index++;   //按照顺序依次播放歌曲数组的数据
	}
}
								
void main()
{
	InitTimer();   //执行中断配置函数

	while(1)
	{
		if(num==1)   //前面num初始化为1,上电后就开始播放《我和我的祖国》
			play(zuguo);

		if(num==2)
			play(guyongzhe);

		if(num==3)
			play(shaonian);
	}			
}

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐