目录
[12.1 蜂鸣器简介](#12.1 蜂鸣器简介)
[12.2 蜂鸣器「三极管或集成电路驱动」](#12.2 蜂鸣器「三极管或集成电路驱动」)
[12.3 无源蜂鸣器完整驱动代码](#12.3 无源蜂鸣器完整驱动代码)
[✅Buzzer.c:核心是电平翻转 + 固定延时生成方波;](#✅Buzzer.c:核心是电平翻转 + 固定延时生成方波;)
[12.4 项目示例1:蜂鸣器播放提示音](#12.4 项目示例1:蜂鸣器播放提示音)
[12.5 项目示例2:蜂鸣器播放音乐](#12.5 项目示例2:蜂鸣器播放音乐)
12.1 蜂鸣器简介

12.2 蜂鸣器「三极管或集成电路驱动」
51 单片机的 IO 口的输出电流很小(约 10mA),而蜂鸣器的工作电流需要 20~30mA,如果直接把蜂鸣器接在单片机 IO 口和 GND 之间,IO 口的电流不足以驱动蜂鸣器,会出现「蜂鸣器不响 / 声音极小」的问题。

✅ 解决方案:常用 NPN 三极管做电流放大(最常用:S8050 三极管),单片机 IO 口控制三极管的导通 / 截止,三极管再给蜂鸣器提供足够的工作电流。
✨ 接线说明:
限流电阻(1kΩ)必须加,作用是保护单片机 IO 口,防止三极管导通时,大电流倒灌烧毁 IO 口;
三极管推荐用 S8050,价格 0.1 元,电流放大能力足够驱动蜂鸣器;
IO 口可以自定义:P0/P1/P2/P3 的任意空闲引脚都可以,代码里对应修改即可;
有源 / 无源蜂鸣器的接线完全一样,区别只在软件驱动代码!
12.3 无源蜂鸣器完整驱动代码
|---|------------------------------------------------------------------------------------------------------------------------------------------------------|
| | 
|
✅Buzzer.c:核心是电平翻转 + 固定延时生成方波;
cpp
#include <REGX52.H> // 51单片机寄存器定义头文件,必备
#include "Delay.h" // 外部延时函数头文件(本代码内未用到,保留不影响编译)
#include <INTRINS.h> // 包含_nop_()空操作函数的头文件,延时函数必须用到
sbit Buzzer = P1^5; // 蜂鸣器引脚定义:P1口的第5位,可自行修改为其他IO口
/**
* @brief 蜂鸣器专用精准延时函数:延时500微秒(500us)
* @param 无
* @retval 无
* @note 晶振频率固定为12MHz,此延时参数为精准匹配值,不可随意修改
*/
void Buzzer_Delay500us() //@12.000MHz
{
_nop_(); // 执行一个空操作,占1个机器周期,用于精准延时校准
unsigned char i; // 定义局部延时变量
i = 247; // 延时初值,搭配while循环实现精准500us延时
while (--i); // 变量自减循环,循环结束刚好延时500微秒
}
/**
* @brief 无源蜂鸣器发声控制函数,指定发声时长
* @param xms 蜂鸣器需要发声的总时长,单位:毫秒(ms)
* @retval 无
* @note 核心原理:通过引脚电平翻转+固定延时,生成方波信号驱动无源蜂鸣器
*/
void Buzzer_Time(unsigned char xms)
{
unsigned int i; // 定义循环计数变量,用于控制发声时长
// 循环次数 = xms*2:因为每次循环内部是【翻转电平+500us延时】,2次500us = 1ms
for(i=0;i<xms*2;i++)
{
Buzzer = !Buzzer; // 蜂鸣器引脚电平取反(高→低 / 低→高),核心:生成高低交替的方波
Buzzer_Delay500us();// 延时500us,维持当前电平状态,保证方波的固定频率
}
}
✅Buzzer.h
cpp
#ifndef _BUZZER_H_
#define _BUZZER_H_
void Buzzer_Time(unsigned char xms);
#endif
12.4 项目示例1:蜂鸣器播放提示音
✅项目功能:
上电后数码管第 1 位显示数字0,程序循环扫描按键;当按下任意按键时,蜂鸣器嘀一声(响 100ms),同时数码管第 1 位立刻显示出当前按下的按键编号;松开按键后,数码管保持显示最后一次按下的按键号,等待下一次按键触发。
✅项目架构:

✅main.c主函数:
cpp
#include <REGX52.H> // 51单片机寄存器定义头文件,51程序必备核心头文件
#include "NiXie.h" // 包含数码管显示驱动头文件 (NiXie = 逆显/数码管显示,自定义驱动)
#include "Key.h" // 包含按键扫描驱动头文件,用于读取按键编号
#include "Buzzer.h" // 包含蜂鸣器驱动头文件,调用蜂鸣器发声函数
unsigned char KeyNum; // 全局变量:存储读取到的按键编号,0=无按键按下,非0=对应按键号(1/2/3/4...)
/**
* @brief 主函数,程序入口函数
* @param 无
* @retval 无
* @note 程序核心逻辑:上电初始化数码管,循环扫描按键,按键按下则蜂鸣器提示+数码管显示按键号
*/
void main()
{
NiXie(1,0); // 上电初始化:调用数码管显示函数,在第1位数码管显示数字0(也可理解为清屏/熄灭)
while(1) // 死循环,程序上电后一直循环执行,永不退出
{
KeyNum=Key(); // 调用按键扫描函数,读取当前是否有按键按下,结果存入KeyNum
if(KeyNum) // 按键扫描判断:KeyNum≠0 代表有按键按下(Key()返回0表示无按键)
{
Buzzer_Time(100); // 有按键按下 → 蜂鸣器发声100ms,短促提示音,按键反馈
NiXie(1,KeyNum); // 调用数码管显示函数 → 在第1位数码管上,显示当前按下的按键编号
}
}
}
12.5 项目示例2:蜂鸣器播放音乐
✅项目功能:
51 单片机 定时器 0 中断精准驱动无源蜂鸣器播放完整音乐
✅项目架构:

✅main.c主函数:
cpp
#include <REGX52.H> // 51单片机寄存器定义头文件,程序必备
#include "Delay.h" // 延时函数头文件,用于控制音符节拍时长与音符间隔
#include "Timer0.h" // 定时器0初始化头文件,用于生成精准频率方波驱动蜂鸣器
//蜂鸣器端口定义:指定蜂鸣器接在P1口第5位IO上
sbit Buzzer=P1^5;
//播放速度宏定义,值为【四分音符】的标准持续时长,单位:毫秒(ms)
//数值越大,音乐播放越慢;数值越小,播放越快,推荐500,可调范围400~600
#define SPEED 500
//音符与频率表索引的宏定义映射表【核心别名定义,方便编写乐谱】
//命名规则:P=休止符(无声音),L=低音区,M=中音区,H=高音区,下划线_=对应音符升半音(简谱#)
#define P 0 // 休止符,对应频率表下标0,无声音
#define L1 1 // 低音1
#define L1_ 2 // 低音1升半音
#define L2 3 // 低音2
#define L2_ 4 // 低音2升半音
#define L3 5 // 低音3
#define L4 6 // 低音4
#define L4_ 7 // 低音4升半音
#define L5 8 // 低音5
#define L5_ 9 // 低音5升半音
#define L6 10 // 低音6
#define L6_ 11 // 低音6升半音
#define L7 12 // 低音7
#define M1 13 // 中音1
#define M1_ 14 // 中音1升半音
#define M2 15 // 中音2
#define M2_ 16 // 中音2升半音
#define M3 17 // 中音3
#define M4 18 // 中音4
#define M4_ 19 // 中音4升半音
#define M5 20 // 中音5
#define M5_ 21 // 中音5升半音
#define M6 22 // 中音6
#define M6_ 23 // 中音6升半音
#define M7 24 // 中音7
#define H1 25 // 高音1
#define H1_ 26 // 高音1升半音
#define H2 27 // 高音2
#define H2_ 28 // 高音2升半音
#define H3 29 // 高音3
#define H4 30 // 高音4
#define H4_ 31 // 高音4升半音
#define H5 32 // 高音5
#define H5_ 33 // 高音5升半音
#define H6 34 // 高音6
#define H6_ 35 // 高音6升半音
#define H7 36 // 高音7
//索引与定时器初值(频率)对照表 【12MHz晶振专用,不可随意修改】
//数组下标 = 上面的音符宏定义值,数组元素 = 对应音符的定时器0十六位重装初值
//原理:不同初值→定时器溢出频率不同→蜂鸣器翻转频率不同→发出不同音调;数值越大→音调越低,数值越小→音调越高
//下标0对应休止符,值为0,无音调
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,
};
//乐谱存储数组 【code关键字:将数组存入单片机ROM只读区,节省RAM空间,标准优化写法】
//乐谱存储规则【固定格式,严格遵守】:{音符宏定义, 节拍时值, 音符宏定义, 节拍时值, ...... , 0xFF}
//节拍时值说明:4=标准四分音符(基准时长=SPEED),2=八分音符(时长=四分音符/2),8=二分音符(时长=四分音符*2)
//组合时值:4+2=附点四分音符,4+4+4=附点二分音符,时值越大音符持续越久
//0xFF:乐谱播放终止标志位,程序读到该值则停止播放
unsigned char code Music[]={
//音符,时值,
//乐谱第1段
P, 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,
//乐谱第2段
M6, 4+2,
M5, 2,
M6, 4,
H1, 4,
M5, 4+4+4,
M3, 4,
M4, 4+2,
M3, 2,
M4, 4,
H1, 4,
//乐谱第3段
M3, 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,
//乐谱第4段
H1, 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,
//乐谱第5段
M5, 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,
//乐谱第6段
H1, 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,
//乐谱第7段
H2, 4+4+4,
M5, 2,
M5, 2,
H1, 4+2,
M7, 2,
H1, 4,
H3, 4,
H3, 4+4+4+4,
//乐谱第8段
M6, 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,
//乐谱第9段
H3, 4+4+4,
H3, 4,
H6, 4+4,
H5, 4,
H5, 4,
H3, 2,
H2, 2,
H1, 4+4,
P, 2,
H1, 2,
//乐谱第10段
H2, 4,
H1, 2,
H2, 2,
H2, 4,
H5, 4,
H3, 4+4+4,
H3, 4,
H6, 4+4,
H5, 4+4,
//乐谱第11段
H3, 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; // 频率选择索引:存储当前要播放的音符,对应FreqTable数组的下标
unsigned char MusicSelect; // 乐谱读取索引:存储当前读取到的Music数组下标,用于遍历乐谱
/**
* @brief 主函数,程序入口函数
* @param 无
* @retval 无
* @note 核心功能:初始化定时器0 → 循环解析乐谱数组 → 按顺序播放每个音符 → 播放完毕停止
*/
void main()
{
Timer0Init(); // 初始化定时器0,开启定时器中断,为蜂鸣器生成方波做准备
while(1) // 死循环,持续解析乐谱并播放音乐,永不退出循环
{
if(Music[MusicSelect]!=0xFF) // 判断是否读取到乐谱终止标志,非0xFF则继续播放音符
{
FreqSelect=Music[MusicSelect]; // 读取当前要播放的【音符】,赋值给频率索引变量
MusicSelect++; // 乐谱索引自增,指向当前音符对应的【节拍时值】
Delay(SPEED/4*Music[MusicSelect]); // 延时控制音符播放时长,节拍时值×速度系数,精准匹配音符时长
MusicSelect++; // 乐谱索引自增,指向下一个音符,准备播放下一个
TR0=0; // 关闭定时器0,蜂鸣器停止电平翻转,当前音符播放结束
Delay(5); // 音符之间短暂延时5ms,形成静音间隔,分隔音符避免旋律粘连,提升播放效果
TR0=1; // 重新开启定时器0,准备播放下一个音符
}
else // 如果读取到乐谱终止标志0xFF,音乐播放完毕
{
TR0=0; // 关闭定时器0,蜂鸣器彻底停止发声
while(1); // 程序卡死在此处,播放完毕后不再重复播放
}
}
}
/**
* @brief 定时器0中断服务函数 【蜂鸣器核心发声函数,中断号固定为1】
* @param 无
* @retval 无
* @interrupt 1 定时器0的中断编号,51单片机固定写法
* @note 定时器溢出触发中断 → 重装定时初值 → 翻转蜂鸣器IO口 → 生成固定频率方波 → 无源蜂鸣器发声
*/
void Timer0_Routine() interrupt 1
{
if(FreqTable[FreqSelect]) // 判断当前是否为休止符,若值为0则是休止符,不执行发声逻辑
{
/* 给定时器0重装当前音符对应的定时初值,保证溢出频率不变,音调不变 */
TL0 = FreqTable[FreqSelect]%256; // 定时器低8位初值 = 频率值对256取余
TH0 = FreqTable[FreqSelect]/256; // 定时器高8位初值 = 频率值对256取商
Buzzer=!Buzzer; // 翻转蜂鸣器IO口电平,高低电平交替形成方波,驱动无源蜂鸣器发声
}
}