news 2026/9/4 21:31:06

STM32F103驱动无源蜂鸣器播放歌曲:频率、PWM与电路实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103驱动无源蜂鸣器播放歌曲:频率、PWM与电路实现

“蜂鸣器怎么播放歌曲”这个问题,看起来像是嵌入式初学者的玩具题,但它背后藏着 STM32F103 入门阶段最值得掌握的一组能力:理解信号频率与声音的关系、看懂蜂鸣器类型与驱动电路、学会用定时器/PWM 精确产生方波,以及用数据结构把乐谱翻译成机器能执行的指令。

先说一个明确判断:蜂鸣器播放歌曲并不需要复杂的音频解码算法。STM32F103 要做的,只是按照乐谱的时间轴,在蜂鸣器引脚上输出“对应音符频率的方波”。难点不在能不能唱,而在“频率是否足够准确”“每个音持续多久”“换音时有没有杂音”“驱动电路能不能稳定工作”。把这四条搞明白,不止能放《小星星》,以后放什么歌都只是换数组的问题。

这篇文章会用偏实战的方式展开:先带你避开有源蜂鸣器这个最常见的坑,再说清楚驱动电路为什么不能只靠 GPIO 直推,然后给出定时器中断翻转 IO、PWM 输出两种播放方案,最后补充验证步骤、常见问题和工程建议。

1. 蜂鸣器播放歌曲到底难在哪里

很多初学者在第一次拿到蜂鸣器时,会以为“播放歌曲”等于“给单片机上写一段旋律”。等代码写完了,板子却只发出刺耳的“滴——滴——”,完全听不出任何曲调。原因通常不在代码,而在三个地方:

第一,选错了蜂鸣器。有源蜂鸣器内部自带振荡电路,通电就响,但频率固定,无法改变音高。第二,驱动电路不够。蜂鸣器工作电流可能超过 STM32F103 GPIO 的可靠输出能力,直接接引脚容易导致输出电压跌落、响度不足,甚至长期使用可能损坏 IO。第三,没有任何“时间轴”概念。声音由频率决定音高,但只输出频率还不够,每个音符必须持续正确的时长,节拍乱了,旋律也就散了。

换个角度理解:MCU 播放音乐,本质上是一个“频率调度器”加一个“节拍管理器”。STM32F103 主频 72MHz,输出常见音符频率 200Hz 到 4kHz 的方波绰绰有余,甚至不会占用太多 CPU。真正需要训练的是你对定时器分频、自动重装载值、PWM 通道和中断回调的理解。这也是为什么这个项目很适合作为单片机进阶练习:它把硬件、外设和软件模型全部串了起来。

所以,如果你正在学 STM32F103,或者准备做带提示音、报警音、简单音乐效果的物联网小项目,本文的内容可以直接复用。如果你只想听到一段现成旋律,不关心原理,也不用急着收藏,因为下面的分析会比最终那段代码更值钱。

2. 有源蜂鸣器与无源蜂鸣器的本质区别

2.1 两种蜂鸣器的命名很容易误会

“有源”和“无源”并不是指“有没有电源”,而是指“内部有没有自带振荡源”。常见表述为:

  • 有源蜂鸣器:内部集成振荡电路,只要在正负极之间加上额定直流电压,它就能自行产生固定频率的声音。你给它高电平,它就响;给低电平,它就停。节奏可以控制,但音高不能控制。
  • 无源蜂鸣器:内部没有振荡源,必须由外部提供一定频率的方波或脉冲信号才能发声。外部方波的频率决定了它发出的音高。
对比项有源蜂鸣器无源蜂鸣器
内部振荡器
输入直流电平是否发声会发声通常不发声或只发出很小的咔哒声
音高是否可控不可控,频率固定可控,由外部方波频率决定
典型应用报警、按键提示音播放旋律、音乐提示、频率输出
控制复杂度需要定时器或 PWM
适合播放歌曲不适合适合

2.2 为什么播放歌曲必须用无源蜂鸣器

歌曲旋律的本质是不同音高的音符按时间顺序排列。比如《小星星》开头是“1 1 5 5 6 6 5”,每个数字对应的是一组特定频率。要让蜂鸣器“唱”出这些音,就必须随时改变它的发声频率。有源蜂鸣器做不到这一点,它只能从固定频率和静音之间二选一。而无源蜂鸣器不内置振荡源,外部给多少 Hz 的方波,它就发多少 Hz 的声音,这正好符合需求。

实际项目里,无源蜂鸣器还可以再细分为电磁式和压电式。电磁式蜂鸣器一般音量大,适合报警和旋律通知;压电式蜂鸣器更省电,但需要的驱动电压较高,声音偏尖。选择时注意蜂鸣器额定电压,常见有 3.3V、5V、12V 等型号。STM32F103 的 GPIO 输出 3.3V,所以用 5V 蜂鸣器时通常不能直接靠 IO 驱动,这一点在下一节展开。

3. 驱动电路:为什么不能直接把蜂鸣器接到 GPIO

3.1 GPIO 直驱的常见问题

网上很多最小系统板集成的是 3.3V 有源蜂鸣器,这种板子内部已经做了三极管驱动,可以直接由 MCU 引脚控制。但如果你是在面包板上自己搭电路,把无源蜂鸣器两个引脚直接接到 STM32F103 的 GPIO 和 GND,很容易遇到问题。

STM32F103 的 GPIO 输出能力有限,不同封装和供电条件下的驱动能力并不一致。工程师习惯上不会让单个 GPIO 长时间输出超过 20mA 左右的电流。很多蜂鸣器工作电流在 20mA 到 40mA,甚至更高。如果直接驱动,GPIO 电压会被拉低,蜂鸣器响度不足;如果蜂鸣器是感性负载,断电瞬间还会产生反电动势,对 GPIO 存在潜在威胁。

所以更稳妥的方案是让 GPIO 只负责输出控制信号,由三极管、MOS 管或达林顿驱动芯片去驱动蜂鸣器这个大电流负载。这也是“GPIO 控制外设”最标准的工程做法。

3.2 使用 NPN 三极管低边驱动

以 S8050 这类 NPN 三极管为例,电路连接如下:

  • 蜂鸣器正极接电源 VCC,常见为 5V。
  • 蜂鸣器负极接三极管集电极。
  • 三极管发射极接 GND。
  • 三极管基极串一个 1kΩ 到 4.7kΩ 的电阻再接 STM32F103 GPIO。

当 GPIO 输出高电平时,三极管导通,蜂鸣器通电;GPIO 输出低电平时,三极管截止,蜂鸣器断电。基极电阻限制基极电流,避免三极管过驱动。

如果是电磁式蜂鸣器,建议在蜂鸣器两端反向并联一个续流二极管,二极管的阴极接电源正极,阳极接三极管集电极。这样在三极管关断瞬间,线圈中的电流可以通过二极管续流,保护三极管不被反向电动势击穿。压电式蜂鸣器电容性更强,通常不像继电器那样强调续流,但加一个反向二极管在很多电磁式蜂鸣器应用里属于标准设计。

3.3 使用 ULN2003APG 达林顿驱动

你在检索“蜂鸣器播放歌曲”时,可能会看到 ULN2003APG 连接蜂鸣器的视频或文章。ULN2003APG 是一类达林顿晶体管阵列,常见型号内部有 7 路达林顿管,每路输出都是集电极开路结构,输出端可以接到地,适合驱动继电器、蜂鸣器、小电机等负载。

使用 ULN2003APG 驱动一路蜂鸣器的接线方式为:

  • STM32F103 GPIO 接 ULN2003APG 的输入引脚,比如 1B。
  • ULN2003APG 对应输出引脚 1C 接蜂鸣器负极。
  • 蜂鸣器正极接电源 VCC。
  • STM32F103 和蜂鸣器电源之间必须共地。

ULN2003APG 内部已经为每一路输入集成了基极电阻,因此不需要像三极管电路那样额外串电阻。它的输出电流能力远高于单个三极管方案,驱动蜂鸣器非常从容。不过要注意,ULN2003APG 属于集电极开路输出,导通时输出低电平,逻辑上要注意控制电平的“有效”方向。

3.4 三类驱动方案对比

驱动方式优点缺点适合场景
GPIO 直驱电路最简单驱动能力弱,可能烧 IO只适合低压微功耗蜂鸣器,不推荐
NPN 三极管成本低,电路常见需要计算基极电阻入门和大多数项目
ULN2003APG驱动能力强,内部带电阻占用体积大,芯片成本高于三极管同时驱动多路蜂鸣器/继电器/电机

本文后续代码示例中,蜂鸣器控制引脚默认为 PA0,也就是 TIM2_CH1。只要你的硬件驱动电路在前面三级基础上任选一种,软件部分都通用。若使用 ULN2003APG,输入高电平时蜂鸣器导通,GPIO 输出高电平与 PWM 高电平语义一致,软件不需要额外反转。

4. 从音符频率到定时器 PWM

4.1 频率决定音高,时值决定节奏

要让无源蜂鸣器发出某个音,只需让引脚输出一个该频率的方波。例如 A4 的标准频率是 440Hz,那就让 GPIO 以 440Hz 的周期翻转高低电平。占空比保持在 50% 左右时,蜂鸣器声音通常最清晰。

但音乐不是一串独立的频率,每个音符还要持续一定时间。我们把“持续多久”叫时值。BPM(Beats Per Minute)表示每分钟有多少个四分音符。BPM=120 时,一个四分音符持续 60000 / 120 = 500ms。八分音符持续 250ms,二分音符持续 1000ms。因此,一首歌曲的播放程序可以简化成循环处理一个数组:

第 1 步:从数组中取出音符频率。
第 2 步:配置定时器/PWM,让输出频率等于音符频率。
第 3 步:按 BPM 计算该音符的持续时间,并延时。
第 4 步:切换到下一个音符,直到数组结束。

4.2 常用音符频率表

下面是一张 C 调常用音符频率表,频率单位是 Hz。实际使用中可以保留整数,因为蜂鸣器振动单元对频率误差并不敏感,几十赫兹以内的误差人耳很难察觉。

音名C4D4E4F4G4A4B4C5
频率 Hz262294330349392440494523

这张表是后续歌曲数组的基础。如果用数字简谱“1 2 3 4 5 6 7”,在 C 调下可以分别映射到 C4、D4、E4、F4、G4、A4、B4。

4.3 STM32F103 定时器与 PWM 频率公式

STM32F103 定时器在向上计数模式下,PWM 输出频率的计算公式是:

PWM_Freq = TIM_CLK / ((PSC + 1) * (ARR + 1))
  • TIM_CLK:定时器输入时钟。
  • PSC:预分频值。
  • ARR:自动重装载值。

很多网上教程把 PSC 设置为 71,因为如果 TIM_CLK 是 72MHz,那么 (PSC+1)=72,计数时钟变成 1MHz。这时每个计数周期是 1μs,ARR 与输出周期直接对应:

PWM_Freq = 1MHz / (ARR + 1)

所以需要输出 freq Hz 时:

ARR + 1 = 1000000 / freq

例如 freq=262Hz 时,ARR = 1000000 / 262 - 1 ≈ 3816。这样设置后,定时器以 1MHz 的计数频率数完 3817 个周期,输出一个 262Hz 的方波,实际频率误差远小于 1%。

这里需要注意:如果你的系统主频不是 72MHz,或者定时器所在总线时钟不是 72MHz,PSC 和 ARR 计算必须基于实际时钟。CubeMX 工程中,建议通过 HAL_RCC_GetPCLK1Freq 或 HAL_RCC_GetPCLK2Freq 获取总线时钟,而不是把 72MHz 写死在代码里。

5. 方案一:定时器中断翻转 GPIO 最小验证

在进入 PWM 播放歌曲之前,先介绍一种更直观的思路:让定时器产生固定周期的更新中断,每次中断翻转一次蜂鸣器 IO。这样软件亲手“拼接”出一个方波。

以 1kHz 方波为例,它的周期是 1ms,半周期是 0.5ms。定时器每 0.5ms 产生一次更新中断,中断里翻转 IO,输出就是 1kHz 方波。用 HAL 库实现时,核心代码如下:

// TIM2 更新中断回调 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); } }

主程序中启动定时器更新中断:

HAL_TIM_Base_Start_IT(&htim2);

配置 TIM2 时,如果计数时钟是 1MHz,则要得到 0.5ms 更新周期,需要设置:

htim2.Init.Prescaler = 72 - 1; // 计数时钟 1MHz htim2.Init.Period = 500 - 1; // 0.5ms

这种方案的优点是逻辑简单,很容易理解“方波频率由定时器周期决定”。缺点是每个音符都要靠中断翻转 IO,如果要输出较高频率,中断频率会成倍增加。比如播放 4kHz 音高,中断频率就是 8kHz,CPU 负担不小。而且中断服务函数里每条指令都会影响占空比精度,所以这个方案适合教学验证,不适合追求稳定旋律播放的工程实现。

如果你在实验板上跑通了“每 0.5ms 翻转一次,蜂鸣器发出 1kHz 声音”,说明驱动电路和声音通路已经正常。下一步就可以换成 PWM 方案了。

6. 方案二:PWM 模式完整播放《小星星》

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 21:26:01

任务依赖实战:从“双数组先做”到自动化状态检查

如果团队里出现一句“双数组任务先做,34 号完成了我们再动手”,这表面上是排期沟通,实际上已经点出了一个多任务并行开发中非常核心的问题:任务之间存在前置依赖,而依赖关系如果不拆清楚,后面要么有人空等&…

作者头像 李华
网站建设 2026/9/4 21:19:11

工业传感器与变送器详解:16 从传感器波形到智能诊断

第16章 从传感器波形到智能诊断 ——建立工业感知系统的全链路诊断模型 在前面的章节中,我们分别学习了传感器原理、信号调理电路、ADC数字采集、智能变送器软件、4–20 mA/HART、PROFIBUS PA、FOUNDATION Fieldbus、Modbus/CAN/工业以太网以及故障诊断方法。 但对于真实工…

作者头像 李华
网站建设 2026/9/4 21:10:05

痛风外用药哪个品牌认可度高?从配方与剂型角度聊聊外用选择

摘要:痛风反复发作带来的关节肿痛,让不少患者会寻找合适的外用产品做局部辅助舒缓。网上不少人会发问痛风外用药哪个品牌认可度高,市面上外用产品品类繁杂,配方、剂型差异很大。本文基于公开药品备案资料,从产品资质、…

作者头像 李华