STM32 蜂鸣器与音乐播放:PWM 驱动与延时发音实战指南
👁 6 阅读 · 2026-08-16 · STM32 PWM
本文深入讲解如何利用 STM32 的定时器 PWM 输出驱动无源蜂鸣器,并基于延时方式实现简易音乐播放。内容涵盖 PWM 原理、频率与音调映射、定时器配置步骤、按键控制示例以及延时播放的注意事项,帮助嵌入式开发者快速掌握蜂鸣器发声的核心技术。
## 一、原理:为什么用 PWM 驱动无源蜂鸣器?
无源蜂鸣器内部没有振荡源,必须施加特定频率的方波才能发声。而 STM32 的定时器 PWM 模式可以精确产生所需频率的方波,且不占用 CPU 忙等。
- **频率决定音调**:每个音符对应一个基频,例如 A4(中音 la)为 440Hz。
- **占空比影响音量**:通常占空比 50% 时响度最大,但也可通过调节占空比改变音色。
- **PWM 输出**:通过定时器自动翻转 GPIO 电平,无需中断干预,适合播放连续音符。
## 二、STM32 PWM 配置步骤(以 TIM2 CH1 为例)
假设主频 72MHz,使用 PB0 作为 PWM 输出引脚,我们配置 TIM2 产生 1kHz 方波。
### 1. 使能 GPIO 和定时器时钟
```c
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
```
### 2. 配置 GPIO 复用推挽输出
```c
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
```
### 3. 配置定时器时基与 PWM 模式
```c
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 自动重装载值,决定频率
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频:72MHz/72=1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500; // 占空比 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_Cmd(TIM2, ENABLE);
```
> 频率计算:`Fpwm = 72MHz / ((Prescaler+1)*(Period+1))`。本例中 = 72e6 / (72 * 1000) = 1000Hz。
## 三、音符频率与延时播放原理
音乐播放的关键是将每个音符的持续时间映射为延时。首先定义一个音符频率表:
```c
// 低音 4~7、中音 1~7、高音 1~7 的频率(Hz)
const uint16_t tone_table[] = {
262, 294, 330, 349, 392, 440, 494, // C4~B4
523, 587, 659, 698, 784, 880, 988, // C5~B5
1047, 1175, 1319, 1397, 1568, 1760, 1976 // C6~B6
};
```
播放时,先更新定时器周期和占空比,使 PWM 输出目标频率,然后延时对应节拍时间,最后停止输出(占空比设为 0)。
### 更新频率函数
```c
void Buzzer_Set_Freq(uint16_t freq) {
if (freq == 0) {
TIM_SetCompare1(TIM2, 0); // 静音
return;
}
uint32_t period = 1000000 / freq; // 计数器频率为 1MHz,故周期值 = 1e6/freq
TIM_SetAutoreload(TIM2, period - 1);
TIM_SetCompare1(TIM2, (period - 1) / 2); // 50% 占空比
}
```
## 四、完整代码示例:播放《小星星》
以下代码演示通过延时方式播放一段简单旋律,每段音符播放后均停止发声。
```c
#include "stm32f10x.h"
#include "delay.h"
void Buzzer_Init(void) {
// 略:RCC、GPIO、TIM2 PWM 初始化,同上述步骤
}
void Buzzer_Set_Freq(uint16_t freq) {
if (freq == 0) {
TIM_SetCompare1(TIM2, 0);
return;
}
uint32_t period = 1000000 / freq;
TIM_SetAutoreload(TIM2, period - 1);
TIM_SetCompare1(TIM2, (period - 1) / 2);
}
// 音符定义:0 为休止符
#define N_C4 262
#define N_D4 294
#define N_E4 330
#define N_F4 349
#define N_G4 392
#define N_A4 440
#define N_B4 494
#define N_C5 523
// 乐谱:音符、节拍(单位:延时基础长度)
const uint16_t score[][2] = {
{N_C4, 1}, {N_C4, 1}, {N_G4, 1}, {N_G4, 1},
{N_A4, 1}, {N_A4, 1}, {N_G4, 2},
{N_F4, 1}, {N_F4, 1}, {N_E4, 1}, {N_E4, 1},
{N_D4, 1}, {N_D4, 1}, {N_C4, 2},
{0, 1}
};
void Play_Music(void) {
uint8_t i;
for (i = 0; i < sizeof(score)/sizeof(score[0]); i++) {
Buzzer_Set_Freq(score[i][0]);
delay_ms(400 * score[i][1]); // 每个节拍 400ms
Buzzer_Set_Freq(0); // 短暂停顿增加颗粒感
delay_ms(50);
}
}
int main(void) {
delay_init();
Buzzer_Init();
while (1) {
Play_Music();
delay_ms(2000);
}
}
```
## 五、延时播放的注意事项
- **延时阻塞 CPU**:使用 `delay_ms` 播放期间无法响应其他任务,适合简单应用;若需并发,应改用定时器中断或 RTOS。
- **频率切换瞬间**:改变自动重装载值时,可能会产生短暂毛刺,建议先停止定时器或设置预装载,再修改值。
- **音准误差**:定时器计数频率和预分频值需根据实际系统时钟调整,确保 1MHz 计数频率准确。
- **音量控制**:若需要调节音量,可修改 `TIM_SetCompare1` 的值,但注意过小占空比可能导致蜂鸣器驱动不足。
- **长时播放发热**:持续高音量发声会加速蜂鸣器老化,可考虑降低占空比或加入间歇。
## 六、扩展:非阻塞播放思路
若需在播放音乐的同时控制 LED 或读取按键,可借助定时器中断:每 10ms 中断一次,在中断中计数节拍时间并更新 PWM 频率。这样主循环仅负责状态切换,实现伪多任务。
```c
// 伪代码示意:定时器中断服务函数
void TIM3_IRQHandler(void) {
if (TIM_GetITStatus(TIM3, TIM_IT_Update)) {
// 处理音符时长计数,并调用 Buzzer_Set_Freq()
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
```
## 七、总结
通过 STM32 定时器的 PWM 输出配合延时控制,可以轻松实现无源蜂鸣器的音乐播放。核心在于掌握频率与音符的换算关系,以及合理更新定时器参数。延时播放虽然简单直观,但适用于对实时性要求不高的场景;更高阶的应用应使用中断或 DMA 驱动。希望本文能帮助你在项目中快速实现蜂鸣器发声功能。