51单片机学习笔记12-蜂鸣器

目录

[12.1 蜂鸣器简介](#12.1 蜂鸣器简介)

[12.2 蜂鸣器「三极管或集成电路驱动」](#12.2 蜂鸣器「三极管或集成电路驱动」)

[12.3 无源蜂鸣器完整驱动代码](#12.3 无源蜂鸣器完整驱动代码)

[✅Buzzer.c:核心是电平翻转 + 固定延时生成方波;](#✅Buzzer.c:核心是电平翻转 + 固定延时生成方波;)

✅Buzzer.h

[12.4 项目示例1:蜂鸣器播放提示音](#12.4 项目示例1:蜂鸣器播放提示音)

✅项目功能:

✅项目架构:

✅main.c主函数:

[12.5 项目示例2:蜂鸣器播放音乐](#12.5 项目示例2:蜂鸣器播放音乐)

✅项目功能:

✅项目架构:

✅main.c主函数:


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口电平,高低电平交替形成方波,驱动无源蜂鸣器发声
	}
}
相关推荐
麦托团子14 小时前
51单片机学习笔记15-PWM脉冲编码调制
51单片机
麦托团子1 天前
51单片机学习笔记10-点阵屏
51单片机
恶魔泡泡糖2 天前
51单片机外部中断
c语言·单片机·嵌入式硬件·51单片机
项目題供诗2 天前
51单片机入门(五)
单片机·嵌入式硬件·51单片机
A-code2 天前
嵌入式UI刷新:观察者模式实战
stm32·单片机·mcu·物联网·51单片机
项目題供诗3 天前
51单片机入门(四)
单片机·嵌入式硬件·51单片机
项目題供诗3 天前
51单片机入门(三)
单片机·嵌入式硬件·51单片机
电子工程师成长日记-C513 天前
51单片机16路抢答器
单片机·嵌入式硬件·51单片机
项目題供诗3 天前
51单片机入门(二)
单片机·嵌入式硬件·51单片机