news 2026/9/3 3:14:28

STM32F103蜂鸣器播放歌曲:定时器PWM控制频率实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103蜂鸣器播放歌曲:定时器PWM控制频率实战

很多人拿到 STM32F103 最小系统板和一颗蜂鸣器,第一反应是“给引脚输出高低电平,蜂鸣器就应该会响”。等接好线、烧完程序,发现蜂鸣器要么一直是一种刺耳的固定声音,要么只会“咔哒咔哒”响,完全没有旋律可言。问题出在两个地方:一是蜂鸣器类型选错,二是频率控制没有做到位。

这篇文章就来讲清楚 STM32F103 怎么用蜂鸣器播放歌曲。它会解释有源蜂鸣器和无源蜂鸣器的本质区别,说明为什么播放旋律必须用无源蜂鸣器,再给出完整的定时器 PWM 驱动方案。按照文章里的代码,你可以让蜂鸣器播放《小星星》这类简单歌曲,也能套用同样的数据结构换成任何你会哼的旋律。

先给一个明确判断:蜂鸣器播放歌曲这个需求,技术难度不高,但它是一个典型的“原理清楚、接线简单、代码细节决定成败”的小项目。真正值得花时间理解的不是“怎么让蜂鸣器响”,而是“怎么把一个音符表达成频率,把一段乐谱表达成数据,再把频率交给定时器输出”。

1. 这篇文章真正要解决的问题

很多初学者会在蜂鸣器播放音乐时遇到下面几种情况:

第一种,买的蜂鸣器是有源蜂鸣器,上电就响,给高电平也响,给低电平也响,完全无法改变音调。第二种,用的是无源蜂鸣器,但程序只做 GPIO 翻转,CPU 写HAL_GPIO_TogglePin翻转频率不稳定,声音偶尔能听出音高,但一旦加其他逻辑就变调。第三种,蜂鸣器接在 PA6 上直接驱动,结果声音很轻,或者蜂鸣器工作电流太大,导致单片机复位。

这些问题的源头可以总结成两句话:

  1. 播放旋律需要“可控频率”的方波,普通 GPIO 延时翻转做不到稳定、精确的频率。
  2. 蜂鸣器是感性或容性负载,需要有正确的驱动方式和足够的电流,不能简单把它当 LED 灯来点亮。

所以本文会按下面这条线展开:

  • 先分辨蜂鸣器类型,确定项目是否选对硬件。
  • 说明声音频率和音符时长如何表达成程序数据。
  • 使用 STM32F103 的定时器 PWM 生成稳定方波。
  • 用一段可复制的 HAL 库代码完成蜂鸣器单音、歌曲播放。
  • 最后总结调试方法、常见问题和工程化注意事项。

读完这篇文章,你能完成从“硬件接线”到“跑通一首歌”的完整闭环。即便你后续把蜂鸣器换成其他发声器件,换用 FreeRTOS 做任务调度,核心的“频率-时长”模型仍然可以直接迁移。

2. 蜂鸣器类型与核心原理

2.1 有源蜂鸣器和无源蜂鸣器不是一个东西

很多采购页面上写的“有源”“无源”会把人绕晕。这里的源,指的是“振荡源”,不是“电源”。

有源蜂鸣器内部自带振荡电路,只要给它一个额定直流电压,它自己就能产生固定频率的声音。它对单片机的控制要求很低,给高电平就响,给低电平就停。但问题也很明显:内部振荡频率是固定的,通常只有 2.3kHz 到 3kHz 左右,你没有办法通过改变 GPIO 输出频率来改变它的音调。

无源蜂鸣器内部没有振荡源,它本质上是一个电磁线圈,或者一片压电陶瓷片。你给它一个方波信号,它才会按照这个方波的频率振动发声。方波频率高,音调就高;方波频率低,音调就低。想播放旋律,就必须用无源蜂鸣器。

使用前的区分方法很简单:

类型固定声音可变音调需要 PWM/方波上电是否自响适合场景
有源蜂鸣器不需要报警、提示音
无源蜂鸣器需要不会播放音乐、和弦场景

这里有一个容易被忽视的坑:如果你项目中已经有现成的三脚蜂鸣器模块,不要默认它就是无源蜂鸣器。三脚封装里也可能是内部带三极管驱动的有源蜂鸣器。以实物上的丝印和数据手册为准,最稳妥的办法是看商家标注“自激式”还是“他激式”,或者直接测试:给蜂鸣器供电,如果上电就一直响,就是有源。

2.2 音高本质是方波频率

人耳能听到的频率范围一般是 20Hz 到 20kHz,蜂鸣器播放音乐时,我们关心的不是 20kHz 以上的超声,而是中频段的几百赫兹到几千赫兹。

在单片机中,无源蜂鸣器播放一个音,最简单的表达方式是:

  • GPIO 输出高低电平,形成方波。
  • 方波频率为 500Hz 时,声音接近 B4 附近的音高。
  • 方波频率为 1000Hz 时,音高比 500Hz 高一倍。

频率和音高不是线性关系,而是指数关系。以十二平均律为例,A4 的标准频率是 440Hz,每升高一个半音,频率乘以 2 的 1/12 次方。所以音阶 C4、D4、E4、F4、G4、A4、B4 的频率大约分别是 262、294、330、349、392、440、494Hz。

实际代码里不需要每次都去算指数,最常用的做法是把需要的音符频率做成一个宏表,或者在乐谱数组里直接填写频率数字。单片机世界里的“乐谱”,本质上就是一组“频率 + 时长”的序列。

2.3 定时器 PWM 是更好的方波来源

有人会用 GPIO 翻转加延时来产生方波,比如:

while (1) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6); HAL_Delay(1); }

这段代码能跑,但有两个问题。第一,HAL_Delay的精度受系统时钟和中断影响,无法稳定产生 262Hz、294Hz 这样的精确频率。第二,如果程序里同时要处理按键、屏幕刷新、传感器读取,整个方波周期会变得不可控,音调就会跑偏。

使用定时器 PWM 之后,方波的频率完全由硬件定时器决定,CPU 不需要一直翻转引脚,只需要在切换音符时修改定时器的自动重载值和比较值。

以 TIM3_CH1 为例,它默认会映射到 PA6。PWM 模式下,定时器计数器从 0 向上计数到自动重载值,再回到 0。比较寄存器 CCR 决定了引脚电平何时翻转。如果把比较值设为自动重载值的一半,就得到占空比约 50% 的方波,这也是驱动蜂鸣器最常用的占空比。

3. 乐谱数据怎么设计

3.1 用频率和节拍表达一首歌曲

写代码前,先把“歌曲”这个抽象概念拆开。

一首歌由一串音符组成,每个音符有两个属性:音高和时值。音高对应 PWM 方波的频率,时值对应这个频率持续多长时间。

举个例子,《小星星》的开头是:

C C G G A A G

在 C 大调里,这些音对应的频率是:

音名频率(Hz)
C4262
D4294
E4330
F4349
G4392
A4440
B4494

时值方面,可以用“十六分音符”作为最小单位。在 4/4 拍中,一个全音符等于 16 个十六分音符,二分音符等于 8 个,四分音符等于 4 个,八分音符等于 2 个。这样定义之后,一个音符的时长就可以用整数表达,避免

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

STM32开源项目源码+原理图到手后,如何精准跑通与改造

如果你做嵌入式开发,一定见过这样的标题:开源、STM32家居环境监测系统、源码加原理图都放出来了。标题很实在,资源包也很好下载,但很多人真正卡住的地方,不是“没找到资料”,而是下载之后的那个晚上。以为打…

作者头像 李华
网站建设 2026/9/3 3:12:35

涂胶机运动控制与Halcon视觉集成实战:C#上位机与九点标定轨迹详解

简介:面向涂胶机智能控制场景的C#与Halcon联合开发资源包,适合工控自动化、机器视觉方向初中级开发者。资源围绕运动控制与数据采集两大核心,示范了上位机与PLC/运动控制卡通信、精确轨迹控制、相机图像获取、视觉定位与涂胶质量检测的完整链…

作者头像 李华
网站建设 2026/9/3 3:11:58

AD9361开发环境搭建:CS_Installer v211安装与避坑指南

简介:ADI公司针对AD9361射频收发器推出的评估软件安装包,面向使用AD936x系列进行无线通信、软件定义无线电(SDR)及测试测量开发的工程师和研究人员,用于解决芯片参数配置、调试与FPGA协同控制等问题,适合具…

作者头像 李华
网站建设 2026/9/3 3:11:22

Matlab实现禁忌搜索算法求解0-1背包问题:原理、代码与调参实战

简介:本资源是一套基于MATLAB实现的禁忌搜索算法求解0-1背包问题的完整代码包,面向算法初学者、优化方向研究者及智能计算课程实践者,聚焦经典组合优化场景下的启发式求解方法。压缩包共含4个文件(3个MATLAB脚本1个Excel数据表&am…

作者头像 李华
网站建设 2026/9/3 3:10:24

从趋化到活化:云克隆基于Luminex技术检测MIP-1α(CCL3)、RANTES(CCL5)、IL-8、G-CSF、GM-CSF、VEGF、IL-2、IL-4、IL-5、IL-13、IL-17A

在免疫学与肿瘤微环境研究的前沿阵地,科研工作者面临的早已不是“有没有”某个细胞因子的问题,而是“如何在同一份微量样本中同步看清”整个免疫效应网络的全貌。MIP-1α(CCL3)、RANTES(CCL5)、IL-8(CXCL8)、G-CSF、GM-CSF、VEGF、IL-2、IL-4、IL-5、IL-…

作者头像 李华
网站建设 2026/9/3 3:09:34

取消构建牛至沙漠lap4+P:10星高难关卡的机制拆解与设计分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华