一、蜂鸣器应用场景
蜂鸣器把电信号转换成声音信号,电子产品用来做状态提示:按键滴声、报警、故障提醒。
空调、电水壶、风扇、单片机开发板上都可以见到蜂鸣器。
二、有源蜂鸣器 vs 无源蜂鸣器
⚠️这里的"源"指振荡源,不是电源!
| 类型 | 有源蜂鸣器(Active) | 无源蜂鸣器(Passive) |
|---|---|---|
| 内部结构 | 自带振荡电路 | 无振荡源,类似小喇叭 |
| 驱动条件 | 施加直流电压就发声 | 必须输入一定频率的方波脉冲才发声 |
| 音调 | 频率固定,不能变调 | 修改脉冲频率,输出不同音调,可以播放音乐 |
| 识别引脚 | 顶部印有+标识;长引脚为正极 |
同样长脚正极,外观很难区分,只能看手册 |
- 有源蜂鸣器:IO直接输出高低电平控制通电断电即可响,代码简单,只能一种音调。
- 无源蜂鸣器:需要单片机输出高低交替方波;可以演奏音乐,是本章实验对象。
💡想让单片机"唱歌",必须使用无源蜂鸣器。
三、硬件驱动电路解析
STC89C51单片机IO口驱动电流很小,不足以直接驱动蜂鸣器,开发板使用NPN三极管做开关放大电流,配合续流二极管保护器件。
开发板硬件:
- 蜂鸣器控制引脚:BUZZE → P2.1
- 三极管FMMT491做开关;R48、R47基极限流电阻;D25为续流二极管,吸收关断瞬间反向电动势,保护三极管。
电路逻辑:
- P2.1输出高电平,三极管导通,无源蜂鸣器得到脉冲信号发声。
- P2.1输出低电平,三极管截止,蜂鸣器停止。
注意:无源蜂鸣器只给恒定高电平是不会响的,需要不断翻转IO输出方波。
四、基础代码实验
示例1:软件延时翻转IO,无源蜂鸣器发声(12M晶振)
循环翻转P2.1引脚,生成方波,蜂鸣器发出滴滴声。
#include<reg52.h>
sbit speak = P2^1; //蜂鸣器控制引脚P2.1
//毫秒延时函数 @12MHz
void delay_ms(unsigned int xms)
{
unsigned int i, j;
for(i=xms;i>0;i--)
{
for(j=124;j>0;j--);
}
}
void main(void)
{
while(1)
{
speak = 1;
delay_ms(1);
speak = 0;
delay_ms(1);
}
}
缺点:软件延时占用CPU,程序卡在延时中,无法处理其他业务;实际项目优先用定时器中断翻转IO。
示例2:有源蜂鸣器简单提示音
如果你硬件是有源蜂鸣器,不需要方波,直接控制电平持续时间:
#include<reg52.h>
sbit speak = P2^1;
void delay_ms(unsigned int xms)
{
unsigned int i, j;
for(i=xms;i>0;i--)
for(j=124;j>0;j--);
}
void main(void)
{
while(1)
{
speak = 1; //响
delay_ms(500);
speak = 0; //停
delay_ms(500);
}
}
五、进阶实战:无源蜂鸣器定时器中断播放音乐《Happy New Year》
原理:使用定时器0中断,定时翻转蜂鸣器IO,产生不同频率方波;数组存储每一个音符对应的定时器初值与节拍时长;主循环遍历乐曲数组播放歌曲。
#include <reg52.h>
#define uchar unsigned char
sbit speak = P2 ^ 1;
bit flag = 0; //电平翻转标志
uchar ptr = 0x00; //歌曲数组索引
uchar high; //定时器初值高8位
uchar low; //定时器初值低8位
//乐曲数组:Happy New Year
//格式:[TH0,TL0,节拍时长],0xff休止符,0x00歌曲结束
uchar code music[] = {
0xfc, 0x44, 0x7f, 0xfc, 0x44, 0x7f, 0xfc, 0x44, 0xff, 0xfa, 0x68, 0xff,
0xfd, 0x23, 0x7f, 0xfd, 0x23, 0x7f, 0xfd, 0x23, 0xff, 0xfc, 0x44, 0xff,
0xfc, 0x44, 0x7f, 0xfd, 0x23, 0x7f, 0xfd, 0x82, 0xff, 0xfd, 0x82, 0xff,
0xfd, 0x23, 0x7f, 0xfd, 0x23, 0x7f, 0xfc, 0xac, 0xff, 0xff, 0xff, 0xff,
0xfc, 0xac, 0x7f, 0xfd, 0x23, 0x7f, 0xfd, 0x34, 0xff, 0xfd, 0x34, 0xff,
0xfd, 0x23, 0x7f, 0xfc, 0xac, 0x7f, 0xfd, 0x23, 0xff, 0xfc, 0x44, 0xff,
0xfc, 0x44, 0x7f, 0xfd, 0x23, 0x7f, 0xfc, 0xac, 0xff, 0xfa, 0x68, 0xff,
0xfc, 0x0c, 0x7f, 0xfc, 0xac, 0x7f, 0xfc, 0x44, 0xff, 0xff, 0xff, 0xff,
0x00
};
//毫秒延时 @12MHz
void delay_ms(unsigned int xms)
{
unsigned int i, j;
for (i = xms; i > 0; i--)
{
for (j = 124; j > 0; j--)
{
}
}
}
//定时器0中断:翻转蜂鸣器IO产生方波
void Count1(void) interrupt 1
{
TH0 = high;
TL0 = low;
if (flag == 0)
{
speak = 0;
flag = 1;
}
else
{
speak = 1;
flag = 0;
}
}
//定时器初始化
void Init(void)
{
TMOD = 0x01; //定时器0模式1 16位
EA = 1; //总中断打开
ET0 = 1; //开启定时器0中断
}
void main(void)
{
uchar time;
Init();
TH0 = high;
TL0 = low;
while (1)
{
if (music[ptr] != 0xff && music[ptr] != 0x00)
{
TR0 = 0; //关闭定时器,停止发声
speak = 1;
delay_ms(10);
high = music[ptr]; //取出音符TH0
low = music[ptr + 1]; //取出音符TL0
time = music[ptr + 2]; //取出节拍时长
TR0 = 1; //开启定时器,播放当前音符
delay_ms(time);
ptr += 3;
}
else if (music[ptr] == 0xff) //休止符,静音
{
time = music[ptr + 2];
delay_ms(time);
ptr += 3;
}
else //0x00歌曲播放完毕,停顿2秒从头循环
{
TR0 = 0;
speak = 1;
delay_ms(2000);
ptr = 0;
}
}
}
代码逻辑说明
- 定时器0中断负责定时翻转P2.1引脚电平,生成方波信号;不同初值对应不同频率,对应不同音符。
music[]数组存储整首歌,每3个字节一组:TH0初值、TL0初值、节拍时间。0xff代表休止符,不发声;0x00代表歌曲结束,停顿2秒,索引置0循环重播。- 播放音符时打开定时器;休止/结束关闭定时器。
六、开发踩坑总结
- 有源无源千万不要搞混:无源蜂鸣器恒定高电平不会响,必须输入方波;有源蜂鸣器给方波声音异常。
- 单片机IO不能直接驱动蜂鸣器,必须依靠三极管放大电流;开发板已经做好外围电路,直接操作IO引脚。
- 续流二极管不能省略,感性负载关断会产生反向高压,容易损坏三极管和单片机。
- 软件延时生成方波会阻塞CPU;音乐项目推荐定时器中断翻转IO。
- 晶振改变,定时器初值需要重新调整,音调才准确。
七、本章小结
- 有源蜂鸣器:直流电平控制,音调固定;无源蜂鸣器需要方波脉冲,可以播放音乐。
- 三极管做开关放大电流,解决单片机IO驱动能力不足的问题。
- 无源蜂鸣器可以软件延时翻转IO发声;高级用法使用定时器中断生成不同频率方波播放乐曲。
- 乐曲数组保存音符定时器初值与节拍,实现循环播放歌曲。