跳到主要内容

【三】蜂鸣器驱动实验

1. 项目概述

本项目演示如何使用 STM32F103C8T6 微控制器驱动有源蜂鸣器,通过GPIO推挽输出模式控制蜂鸣器的开关,实现简单的间歇鸣叫功能。系统以500ms为周期,蜂鸣器交替响和停,产生"滴-滴-滴"的报警音效。

技术要点

  • ✅ GPIO推挽输出模式配置
  • ✅ 有源蜂鸣器驱动
  • ✅ 软件延时实现(循环延时)
  • ✅ 硬件延时实现(SysTick定时器)
  • ✅ 蜂鸣器开关控制
  • ✅ 蜂鸣器状态翻转

应用场景

  1. 🔔 报警提示音
  2. 🔔 按键反馈音
  3. 🔔 状态指示音
  4. 🔔 定时提醒
  5. 🔔 错误警告
  6. 🔔 设备启动音

2. 硬件平台

主控芯片

  • 型号: STM32F103C8T6
  • 内核: ARM Cortex-M3
  • 主频: 72 MHz
  • Flash: 64 KB
  • SRAM: 20 KB

外设资源

蜂鸣器模块

功能引脚说明
BuzzerPC13蜂鸣器控制引脚
VCC3.3V/5V电源正极
GNDGND电源地

蜂鸣器类型

有源蜂鸣器 vs 无源蜂鸣器:

┌────────────────────────────────────────────────────┐
│ 有源蜂鸣器(本项目使用) │
├────────────────────────────────────────────────────┤
│ • 内部集成振荡电路 │
│ • 直接给电就能发声 │
│ • 频率固定(通常2-4KHz) │
│ • 音调单一 │
│ • 驱动简单(GPIO高低电平即可) │
│ • 价格稍高 │
│ • 适合简单报警音 │
└────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────┐
│ 无源蜂鸣器 │
├────────────────────────────────────────────────────┤
│ • 内部无振荡电路 │
│ • 需要提供特定频率的方波 │
│ • 频率可变(可发出不同音调) │
│ • 音调丰富 │
│ • 驱动复杂(需要PWM信号) │
│ • 价格较低 │
│ • 适合音乐播放 │
└────────────────────────────────────────────────────┘

硬件连接

STM32F103C8T6              有源蜂鸣器
PC13 ----------> I/O (控制端)
VCC ----------> VCC (电源+)
GND ----------> GND (电源-)

蜂鸣器驱动电路(简化):
VCC
|
╱ 蜂鸣器
|
PC13 ┤
|
GND

工作原理:
- PC13输出高电平(3.3V)→ 蜂鸣器响
- PC13输出低电平(0V)→ 蜂鸣器停

注意事项:

  1. 有源蜂鸣器直接用GPIO控制,无需PWM
  2. 蜂鸣器电流较大(10-30mA),建议使用三极管驱动
  3. PC13引脚驱动能力有限,大功率蜂鸣器需外加驱动电路
  4. 注意蜂鸣器极性(有正负极)

3. 项目结构

3_Buzzer蜂鸣器/
├── Libraries/ # STM32标准外设库
│ ├── CMSIS/ # Cortex微控制器软件接口标准
│ └── STM32F10x_StdPeriph_Driver/ # STM32F10x标准外设驱动
├── MDK/ # Keil工程文件
│ ├── TEST_CODE.uvproj # Keil项目文件
│ ├── TEST_CODE.uvopt # 项目配置选项
│ ├── Output/ # 编译输出目录
│ └── list/ # 列表文件目录
├── Readme/ # 说明文档目录
└── User/ # 用户代码目录
├── main.c # 主程序入口
├── TF_Buzzer.c # 蜂鸣器驱动实现
├── TF_Buzzer.h # 蜂鸣器驱动头文件
├── TF_Delay.c # 延时函数实现
└── TF_Delay.h # 延时函数头文件

4. 功能说明

系统工作流程

系统上电

蜂鸣器初始化
(配置PC13为推挽输出)

主循环开始

蜂鸣器开启
(PC13输出高电平)

延时500ms

蜂鸣器关闭
(PC13输出低电平)

延时500ms

循环继续

功能特点

  1. 简单控制:

    • 只需GPIO高低电平切换
    • 无需复杂的PWM配置
    • 适合初学者学习
  2. 间歇鸣叫:

    • 500ms响,500ms停
    • 周期为1秒
    • 产生"滴-滴-滴"的节奏
  3. 推挽输出:

    • 驱动能力强
    • 高低电平明确
    • 适合驱动蜂鸣器
  4. 多种延时:

    • 软件循环延时
    • 硬件SysTick延时
    • 可根据需求选择

时序图

蜂鸣器控制时序:
时间 →
0ms 500ms 1000ms 1500ms 2000ms 2500ms
↓ ↓ ↓ ↓ ↓ ↓
PC13 ┌──────┐ ┌──────┐ ┌──────┐
│ │ │ │ │ │
│ └──────┘ └──────┘ └──────
高电平 低电平 高电平 低电平 高电平 低电平

声音 ♪♪♪♪♪♪ ♪♪♪♪♪♪ ♪♪♪♪♪♪
响 停 响 停 响 停
↑←500ms→↑←500ms→↑←500ms→↑

5. 蜂鸣器原理

有源蜂鸣器结构

有源蜂鸣器内部结构:
┌─────────────────────────────────────┐
│ 有源蜂鸣器 │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 振荡电路 │──→ │ 压电片 │ │
│ │ (内置) │ │ │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ ↓ ↓ │
│ ┌─────────────────────────┐ │
│ │ I/O VCC GND │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────┘

工作原理:
1. 给I/O引脚高电平
2. 内部振荡电路启动
3. 产生固定频率的振荡信号(2-4KHz)
4. 驱动压电片振动
5. 压电片振动产生声音

声音产生原理

压电效应:
┌─────────────────────────────────────┐
│ 压电陶瓷片 │
│ │
│ 施加电压 → 机械形变 → 振动 → 声音 │
│ │
│ 特点: │
│ • 频率高(KHz级别) │
│ • 体积小 │
│ • 功耗低 │
│ • 响应快 │
└─────────────────────────────────────┘

频率与音调:
频率越高 → 音调越高(尖锐)
频率越低 → 音调越低(低沉)

有源蜂鸣器:固定频率(通常2-4KHz)
无源蜂鸣器:频率可变(可发出不同音调)

驱动电路

基本驱动电路(直接驱动):
VCC
|
╱ 蜂鸣器
|
PC13 ┤
|
GND

优点:电路简单
缺点:GPIO驱动能力有限(最大25mA)

三极管驱动电路(推荐):
VCC
|
╱ 蜂鸣器
|
C
┌────┤ NPN三极管
│ E
│ |
R GND
|
PC13 ┤

优点:
- 驱动能力强(可驱动大功率蜂鸣器)
- 保护GPIO
- 电流可控

元件选择:
- 三极管:S8050、2N2222等
- 电阻R:1-10KΩ(限流电阻)

6. 代码详解

6.1 主程序 (main.c)

完整代码

#include "TF_Buzzer.h"
#include "TF_Delay.h"

int main(void)
{
Buzzer_Init(); // 蜂鸣器初始化

while(1)
{
Buzzer_On(); // 蜂鸣器开启
Delay_nms(500); // 延时500ms
Buzzer_Off(); // 蜂鸣器关闭
Delay_nms(500); // 延时500ms
}
}

主函数要点:

  1. 初始化:

    Buzzer_Init();  // 配置GPIO为推挽输出
  2. 主循环:

    while(1)
    {
    Buzzer_On(); // 开启(高电平)
    Delay_nms(500); // 延时500ms
    Buzzer_Off(); // 关闭(低电平)
    Delay_nms(500); // 延时500ms
    }
  3. 时序控制:

    • 响500ms,停500ms
    • 周期为1秒
    • 占空比50%

6.2 蜂鸣器驱动 (TF_Buzzer)

TF_Buzzer.h - 头文件

#ifndef __TF_Buzzer_H__
#define __TF_Buzzer_H__

#include "stm32f10x.h"
#include "stm32f10x_conf.h"

// 蜂鸣器引脚定义
#define Buzzer_Pin GPIO_Pin_13 // 蜂鸣器引脚
#define Buzzer_Port GPIOC // 蜂鸣器所在的端口

// 函数声明
extern void Buzzer_Init(void); // 初始化
extern void Buzzer_On(void); // 开启
extern void Buzzer_Off(void); // 关闭
extern void Buzzer_Toggle(void); // 翻转

#endif

蜂鸣器初始化

void Buzzer_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

// 使能GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

// 配置蜂鸣器引脚
GPIO_InitStructure.GPIO_Pin = Buzzer_Pin; // PC13
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 速度50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_Init(Buzzer_Port, &GPIO_InitStructure);

// 初始化为低电平,关闭蜂鸣器
GPIO_ResetBits(Buzzer_Port, Buzzer_Pin);
}

初始化要点:

  1. GPIO模式选择:

    GPIO_Mode_Out_PP  // 推挽输出模式

    特点:
    - 可输出高电平(VCC)和低电平(GND)
    - 驱动能力强(最大25mA)
    - 高低电平明确
    - 适合驱动蜂鸣器
  2. GPIO速度:

    GPIO_Speed_50MHz  // 50MHz速度

    说明:
    - 蜂鸣器不需要高速切换
    - 2MHz或10MHz也可以
    - 速度越高,功耗越大
  3. 初始状态:

    GPIO_ResetBits(Buzzer_Port, Buzzer_Pin);  // 低电平

    作用:
    - 初始化时关闭蜂鸣器
    - 避免上电时蜂鸣器响

蜂鸣器控制函数

// 开启蜂鸣器
void Buzzer_On(void)
{
GPIO_SetBits(Buzzer_Port, Buzzer_Pin); // 输出高电平
}

// 关闭蜂鸣器
void Buzzer_Off(void)
{
GPIO_ResetBits(Buzzer_Port, Buzzer_Pin); // 输出低电平
}

// 翻转蜂鸣器状态
void Buzzer_Toggle(void)
{
GPIO_WriteBit(Buzzer_Port, Buzzer_Pin,
(BitAction)(1 - GPIO_ReadOutputDataBit(Buzzer_Port, Buzzer_Pin)));
}

控制函数说明:

  1. Buzzer_On():

    GPIO_SetBits(Buzzer_Port, Buzzer_Pin);

    功能:
    - 设置PC13为高电平(3.3V)
    - 蜂鸣器内部振荡电路启动
    - 蜂鸣器发声
  2. Buzzer_Off():

    GPIO_ResetBits(Buzzer_Port, Buzzer_Pin);

    功能:
    - 设置PC13为低电平(0V)
    - 蜂鸣器内部振荡电路停止
    - 蜂鸣器停止发声
  3. Buzzer_Toggle():

    // 读取当前状态
    current_state = GPIO_ReadOutputDataBit(Buzzer_Port, Buzzer_Pin);

    // 翻转状态
    new_state = 1 - current_state;

    // 写入新状态
    GPIO_WriteBit(Buzzer_Port, Buzzer_Pin, new_state);

    功能:
    - 如果当前是高电平,则变为低电平
    - 如果当前是低电平,则变为高电平
    - 用于实现蜂鸣器闪烁

6.3 延时函数 (TF_Delay)

项目提供了多种延时实现方式:

方法1:软件循环延时

// 微秒级延时
void Delay_nus(unsigned long n)
{
unsigned long j;
while(n--)
{
j = 12; // 循环12次约1us(72MHz主频)
while(j--);
}
}

// 毫秒级延时
void Delay_nms(unsigned long n)
{
while(n--)
{
Delay_nus(1030); // 调用微秒延时
}
}

软件延时特点:

  • ✅ 实现简单
  • ✅ 无需硬件支持
  • ❌ 占用CPU(阻塞)
  • ❌ 精度受主频影响
  • ❌ 不同优化等级结果不同

方法2:SysTick硬件延时

static __IO uint32_t TimingDelay;

// 毫秒级延时
void Delay_ms(u16 nms)
{
TimingDelay = nms;
SysTick_Config(72); // 72MHz主频,1ms触发一次中断
while(TimingDelay);
SysTick->CTRL = 0x00; // 关闭SysTick
}

// 微秒级延时
void Delay_us(u32 nus)
{
TimingDelay = nus;
SysTick_Config(72); // 72MHz主频,1us触发一次中断
while(TimingDelay);
SysTick->CTRL = 0x00;
}

// SysTick中断服务函数(需在stm32f10x_it.c中实现)
void SysTick_Handler(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}

SysTick延时特点:

  • ✅ 精度高
  • ✅ 不受编译优化影响
  • ✅ 基于硬件定时器
  • ❌ 仍然占用CPU(阻塞)
  • ❌ 需要中断支持

方法3:通用延时函数

void Delayms(__IO uint32_t nTime) 
{
while(SysTick_Config(SystemCoreClock / 1000)); // 1ms触发一次
TimingDelay = nTime;
while(TimingDelay != 0);
SysTick->CTRL = 0x00; // 关闭计数器
SysTick->VAL = 0X00; // 清空计数器
}

void Delayus(__IO uint32_t nTime)
{
while(SysTick_Config(SystemCoreClock / 1000000)); // 1us触发一次
TimingDelay = nTime;
while(TimingDelay != 0);
SysTick->CTRL = 0x00;
SysTick->VAL = 0X00;
}

7. GPIO推挽输出原理

推挽输出结构

推挽输出(Push-Pull)电路结构:
VCC
|
┌┴┐ P-MOS
│ │ (上管)
┌───┤ ├───┐
│ └┬┘ │
│ | │
控制 ┤ ├────┤ 输出引脚
│ | │
│ ┌┴┐ │
└───┤ ├───┘
│ │ N-MOS
└┬┘ (下管)
|
GND

工作原理:
1. 输出高电平:
- P-MOS导通,N-MOS截止
- 输出引脚连接到VCC
- 输出电压≈VCC(3.3V)

2. 输出低电平:
- P-MOS截止,N-MOS导通
- 输出引脚连接到GND
- 输出电压≈0V

推挽输出 vs 开漏输出

特性推挽输出开漏输出
输出高电平可以需要外部上拉
输出低电平可以可以
驱动能力弱(取决于上拉电阻)
速度慢(RC充电)
应用驱动LED、蜂鸣器I2C、多设备共享总线

推挽输出特点

优点:

  • ✅ 驱动能力强(可输出25mA)
  • ✅ 高低电平明确
  • ✅ 速度快
  • ✅ 无需外部上拉电阻
  • ✅ 适合驱动负载

缺点:

  • ❌ 不能多个输出连接在一起(会短路)
  • ❌ 功耗稍高

8. 延时实现方式对比

三种延时方式对比

特性软件循环延时SysTick延时RTOS延时
实现复杂度简单中等复杂
精度
CPU占用100%(阻塞)100%(阻塞)0%(非阻塞)
受优化影响
适用场景简单延时精确延时多任务系统

软件循环延时

void Delay_nms(unsigned long n)
{
while(n--)
{
Delay_nus(1030);
}
}

优点:
- 实现简单
- 无需硬件支持
- 适合初学者

缺点:
- 精度低
- 受编译优化影响
- 占用CPU
- 不同主频需要调整参数

使用场景:
- 简单的延时需求
- 对精度要求不高
- 单任务程序

SysTick硬件延时

void Delay_ms(u16 nms)
{
TimingDelay = nms;
SysTick_Config(72);
while(TimingDelay);
SysTick->CTRL = 0x00;
}

优点:
- 精度高(基于硬件定时器)
- 不受编译优化影响
- 可移植性好

缺点:
- 仍然占用CPU(阻塞)
- 需要中断支持
- 稍复杂

使用场景:
- 需要精确延时
- 单任务程序
- 对时间精度要求高

RTOS延时(非阻塞)

// FreeRTOS示例
void Task_Buzzer(void *pvParameters)
{
while(1)
{
Buzzer_On();
vTaskDelay(500); // 非阻塞延时
Buzzer_Off();
vTaskDelay(500);
}
}

优点:
- 非阻塞(CPU可执行其他任务)
- 精度高
- 适合多任务

缺点:
- 需要RTOS支持
- 实现复杂
- 占用更多资源

使用场景:
- 多任务系统
- 复杂应用
- 需要并发处理

9. 使用说明

9.1 硬件连接

连接步骤

  1. 连接蜂鸣器

    蜂鸣器 I/O → STM32 PC13
    蜂鸣器 VCC → 3.3V/5V(根据蜂鸣器要求)
    蜂鸣器 GND → GND
  2. 连接调试器

    J-Link/ST-Link:
    SWDIO → PA13
    SWCLK → PA14
    GND → GND
    VCC → 3.3V

9.2 编译与下载

编译工程

  1. 打开 MDK/TEST_CODE.uvproj
  2. 选择目标芯片:STM32F103C8
  3. 编译:Project → Build Target (F7)
  4. 检查编译结果:0 Error(s), 0 Warning(s)

下载程序

  1. 配置调试器:Options → Debug → 选择 J-Link/ST-Link
  2. 下载程序:Flash → Download (F8)
  3. 复位运行

9.3 运行效果

预期现象:

  • 蜂鸣器以1秒为周期鸣叫
  • 响500ms,停500ms
  • 产生"滴-滴-滴"的节奏
  • 持续循环

9.4 测试方法

测试1:基本功能

步骤:
1. 上电复位
2. 观察蜂鸣器是否鸣叫
3. 用示波器观察PC13波形

预期结果:
- 蜂鸣器间歇鸣叫
- PC13输出方波(周期1秒,占空比50%)

测试2:修改频率

// 修改main.c中的延时时间
while(1)
{
Buzzer_On();
Delay_nms(100); // 改为100ms
Buzzer_Off();
Delay_nms(100); // 改为100ms
}

预期结果:
- 鸣叫频率加快(5/秒)
- 声音变得急促

测试3:长鸣

// 修改main.c
Buzzer_Init();
Buzzer_On(); // 一直开启
while(1)
{
// 不做任何事
}

预期结果:
- 蜂鸣器持续鸣叫
- 不间断

10. 常见问题

问题1:蜂鸣器不响

可能原因:

  1. ❌ 蜂鸣器连接错误
  2. ❌ 蜂鸣器极性接反
  3. ❌ GPIO配置错误
  4. ❌ 蜂鸣器损坏

解决方案:

// 调试1:测试GPIO输出
void Test_GPIO(void)
{
Buzzer_Init();

while(1)
{
GPIO_SetBits(GPIOC, GPIO_Pin_13);
Delay_nms(1000);
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
Delay_nms(1000);
}
}

// 调试2:用LED替代蜂鸣器测试
// 将LED连接到PC13,观察是否闪烁

// 调试3:用万用表测量PC13电压
// 应该在0V和3.3V之间切换

问题2:蜂鸣器声音很小

原因: GPIO驱动能力不足

解决方案:

方案1:使用三极管驱动
VCC
|
╱ 蜂鸣器
|
C
┌────┤ S8050
│ E
│ |
1K GND
|
PC13 ┤

方案2:使用专用驱动芯片
- ULN2003
- L293D
等大电流驱动芯片

方案3:降低蜂鸣器功率
- 选择小功率蜂鸣器
- 或使用5V供电改为3.3V供电

问题3:蜂鸣器一直响

原因: 程序逻辑错误或GPIO状态异常

解决方案:

// 检查初始化
void Buzzer_Init(void)
{
// ...
GPIO_ResetBits(Buzzer_Port, Buzzer_Pin); // 确保初始为低电平
}

// 检查主循环
while(1)
{
Buzzer_On();
Delay_nms(500);
Buzzer_Off(); // 确保有关闭操作
Delay_nms(500);
}

问题4:延时不准确

原因: 软件延时受编译优化影响

解决方案:

// 方案1:使用SysTick硬件延时
void Delay_ms(u16 nms)
{
TimingDelay = nms;
SysTick_Config(72);
while(TimingDelay);
SysTick->CTRL = 0x00;
}

// 方案2:调整软件延时参数
// 根据实际测量结果调整循环次数

// 方案3:使用定时器
// 配置TIM2/TIM3等定时器实现精确延时

问题5:蜂鸣器频率不对

原因: 有源蜂鸣器频率固定,无法改变

解决方案:

如果需要不同频率:
1. 更换不同频率的有源蜂鸣器
2. 使用无源蜂鸣器 + PWM驱动

无源蜂鸣器PWM驱动示例:
- 使用TIM2生成PWM
- 频率可调(100Hz-10KHz)
- 可发出不同音调

11. 扩展应用

11.1 报警音效

// 快速报警音(紧急)
void Alarm_Fast(void)
{
for(int i = 0; i < 10; i++)
{
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
Delay_nms(100);
}
}

// 慢速报警音(警告)
void Alarm_Slow(void)
{
for(int i = 0; i < 5; i++)
{
Buzzer_On();
Delay_nms(500);
Buzzer_Off();
Delay_nms(500);
}
}

// 连续报警音(危险)
void Alarm_Continuous(void)
{
Buzzer_On();
Delay_nms(3000); // 持续3秒
Buzzer_Off();
}

11.2 按键反馈音

// 按键按下提示音
void Beep_KeyPress(void)
{
Buzzer_On();
Delay_nms(50); // 短促的"滴"声
Buzzer_Off();
}

// 按键确认音
void Beep_Confirm(void)
{
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
Delay_nms(50);
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
}

// 按键错误音
void Beep_Error(void)
{
for(int i = 0; i < 3; i++)
{
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
Delay_nms(50);
}
}

11.3 定时提醒

// 整点报时
void Beep_HourChime(uint8_t hour)
{
for(uint8_t i = 0; i < hour; i++)
{
Buzzer_On();
Delay_nms(200);
Buzzer_Off();
Delay_nms(300);
}
}

// 倒计时提醒
void Beep_Countdown(uint8_t seconds)
{
for(uint8_t i = seconds; i > 0; i--)
{
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
Delay_nms(900); // 每秒响一次
}

// 倒计时结束,长鸣
Buzzer_On();
Delay_nms(1000);
Buzzer_Off();
}

11.4 状态指示

// 系统启动音
void Beep_Startup(void)
{
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
Delay_nms(50);
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
Delay_nms(50);
Buzzer_On();
Delay_nms(200);
Buzzer_Off();
}

// 系统关闭音
void Beep_Shutdown(void)
{
Buzzer_On();
Delay_nms(200);
Buzzer_Off();
Delay_nms(50);
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
Delay_nms(50);
Buzzer_On();
Delay_nms(100);
Buzzer_Off();
}

// 成功提示音
void Beep_Success(void)
{
Buzzer_On();
Delay_nms(50);
Buzzer_Off();
Delay_nms(50);
Buzzer_On();
Delay_nms(150);
Buzzer_Off();
}

11.5 PWM驱动无源蜂鸣器

// 使用TIM2生成PWM驱动无源蜂鸣器
void Buzzer_PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

// 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// 配置GPIO(PA0 - TIM2_CH1)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 配置TIM2
TIM_TimeBaseStructure.TIM_Period = 999; // ARR
TIM_TimeBaseStructure.TIM_Prescaler = 71; // PSC
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

// 配置PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500; // CCR(占空比50%)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);

TIM_Cmd(TIM2, ENABLE);
}

// 设置频率
void Buzzer_SetFrequency(uint16_t freq)
{
uint16_t arr = 72000000 / 72 / freq - 1;
TIM_SetAutoreload(TIM2, arr);
TIM_SetCompare1(TIM2, arr / 2); // 占空比50%
}

// 播放音符
void Play_Note(uint16_t freq, uint16_t duration)
{
Buzzer_SetFrequency(freq);
TIM_Cmd(TIM2, ENABLE);
Delay_nms(duration);
TIM_Cmd(TIM2, DISABLE);
}

// 播放简单旋律
void Play_Melody(void)
{
// C D E F G A B C
uint16_t notes[] = {262, 294, 330, 349, 392, 440, 494, 523};

for(int i = 0; i < 8; i++)
{
Play_Note(notes[i], 500);
Delay_nms(100);
}
}

11.6 蜂鸣器音乐播放

// 音符频率定义
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_B4 494
#define NOTE_C5 523

// 音符时长定义
#define WHOLE 1000
#define HALF 500
#define QUARTER 250
#define EIGHTH 125

// 乐谱结构
typedef struct {
uint16_t freq;
uint16_t duration;
} Note_t;

// 《小星星》乐谱
Note_t twinkle_star[] = {
{NOTE_C4, QUARTER}, {NOTE_C4, QUARTER}, {NOTE_G4, QUARTER}, {NOTE_G4, QUARTER},
{NOTE_A4, QUARTER}, {NOTE_A4, QUARTER}, {NOTE_G4, HALF},
{NOTE_F4, QUARTER}, {NOTE_F4, QUARTER}, {NOTE_E4, QUARTER}, {NOTE_E4, QUARTER},
{NOTE_D4, QUARTER}, {NOTE_D4, QUARTER}, {NOTE_C4, HALF},
// ...
};

// 播放音乐
void Play_Music(Note_t *music, uint16_t length)
{
for(uint16_t i = 0; i < length; i++)
{
Play_Note(music[i].freq, music[i].duration);
Delay_nms(50); // 音符间隔
}
}

11.7 非阻塞蜂鸣器控制

// 使用状态机实现非阻塞控制
typedef enum {
BUZZER_IDLE,
BUZZER_ON,
BUZZER_OFF
} BuzzerState_t;

typedef struct {
BuzzerState_t state;
uint32_t start_time;
uint16_t on_time;
uint16_t off_time;
uint16_t count;
uint16_t total_count;
} BuzzerControl_t;

BuzzerControl_t buzzer_ctrl;

// 初始化蜂鸣器控制
void Buzzer_Control_Init(void)
{
buzzer_ctrl.state = BUZZER_IDLE;
buzzer_ctrl.count = 0;
}

// 启动蜂鸣器(非阻塞)
void Buzzer_Start(uint16_t on_ms, uint16_t off_ms, uint16_t times)
{
buzzer_ctrl.on_time = on_ms;
buzzer_ctrl.off_time = off_ms;
buzzer_ctrl.total_count = times;
buzzer_ctrl.count = 0;
buzzer_ctrl.state = BUZZER_ON;
buzzer_ctrl.start_time = GetTick();
Buzzer_On();
}

// 蜂鸣器状态机(在主循环或定时器中调用)
void Buzzer_Process(void)
{
uint32_t current_time = GetTick();

switch(buzzer_ctrl.state)
{
case BUZZER_IDLE:
// 空闲状态,什么都不做
break;

case BUZZER_ON:
if(current_time - buzzer_ctrl.start_time >= buzzer_ctrl.on_time)
{
Buzzer_Off();
buzzer_ctrl.state = BUZZER_OFF;
buzzer_ctrl.start_time = current_time;
}
break;

case BUZZER_OFF:
if(current_time - buzzer_ctrl.start_time >= buzzer_ctrl.off_time)
{
buzzer_ctrl.count++;

if(buzzer_ctrl.count >= buzzer_ctrl.total_count)
{
buzzer_ctrl.state = BUZZER_IDLE;
}
else
{
Buzzer_On();
buzzer_ctrl.state = BUZZER_ON;
buzzer_ctrl.start_time = current_time;
}
}
break;
}
}

// 使用示例
int main(void)
{
Buzzer_Init();
Buzzer_Control_Init();

// 启动蜂鸣器:响100ms,停200ms,重复5次
Buzzer_Start(100, 200, 5);

while(1)
{
Buzzer_Process(); // 处理蜂鸣器状态

// 可以执行其他任务
Other_Task();
}
}