【三】蜂鸣器驱动实验
1. 项目概述
本项目演示如何使用 STM32F103C8T6 微控制器驱动有源蜂鸣器,通过GPIO推挽输出模式控制蜂鸣器的开关,实现简单的间歇鸣叫功能。系统以500ms为周期,蜂鸣器交替响和停,产生"滴-滴-滴"的报警音效。
技术要点
- ✅ GPIO推挽输出模式配置
- ✅ 有源蜂鸣器驱动
- ✅ 软件延时实现(循环延时)
- ✅ 硬件延时实现(SysTick定时器)
- ✅ 蜂鸣器开关控制
- ✅ 蜂鸣器状态翻转
应用场景
- 🔔 报警提示音
- 🔔 按键反馈音
- 🔔 状态指示音
- 🔔 定时提醒
- 🔔 错误警告
- 🔔 设备启动音
2. 硬件平台
主控芯片
- 型号: STM32F103C8T6
- 内核: ARM Cortex-M3
- 主频: 72 MHz
- Flash: 64 KB
- SRAM: 20 KB
外设资源
蜂鸣器模块
| 功能 | 引脚 | 说明 |
|---|---|---|
| Buzzer | PC13 | 蜂鸣器控制引脚 |
| VCC | 3.3V/5V | 电源正极 |
| GND | GND | 电源地 |
蜂鸣器类型
有源蜂鸣器 vs 无源蜂鸣器:
┌────────────────────────────────────────────────────┐
│ 有源蜂鸣器(本项目使用) │
├────────────────────────────────────────────────────┤
│ • 内部集成振荡电路 │
│ • 直接给电就能发声 │
│ • 频率固定(通常2-4KHz) │
│ • 音调单一 │
│ • 驱动简单(GPIO高低电平即可) │
│ • 价格稍高 │
│ • 适合简单报警音 │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ 无源蜂鸣器 │
├────────────────────────────────────────────────────┤
│ • 内部无振荡电路 │
│ • 需要提供特定频率的方波 │
│ • 频率可变(可发出不同音调) │
│ • 音调丰富 │
│ • 驱动复杂(需要PWM信号) │
│ • 价格较低 │
│ • 适合音乐播放 │
└────────────────────────────────────────────────────┘
硬件连接
STM32F103C8T6 有源蜂鸣器
PC13 ----------> I/O (控制端)
VCC ----------> VCC (电源+)
GND ----------> GND (电源-)
蜂鸣器驱动电路(简化):
VCC
|
╱ 蜂鸣器
|
PC13 ┤
|
GND
工作原理:
- PC13输出高电平(3.3V)→ 蜂鸣器响
- PC13输出低电平(0V)→ 蜂鸣器停
注意事项:
- 有源蜂鸣器直接用GPIO控制,无需PWM
- 蜂鸣器电流较大(10-30mA),建议使用三极管驱动
- PC13引脚驱动能力有限,大功率蜂鸣器需外加驱动电路
- 注意蜂鸣器极性(有正负极)
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
↓
循环继续
功能特点
-
简单控制:
- 只需GPIO高低电平切换
- 无需复杂的PWM配置
- 适合初学者学习
-
间歇鸣叫:
- 500ms响,500ms停
- 周期为1秒
- 产生"滴-滴-滴"的节奏
-
推挽输出:
- 驱动能力强
- 高低电平明确
- 适合驱动蜂鸣器
-
多种延时:
- 软件循环延时
- 硬件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
}
}
主函数要点:
-
初始化:
Buzzer_Init(); // 配置GPIO为推挽输出 -
主循环:
while(1)
{
Buzzer_On(); // 开启(高电平)
Delay_nms(500); // 延时500ms
Buzzer_Off(); // 关闭(低电平)
Delay_nms(500); // 延时500ms
} -
时序控制:
- 响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);
}
初始化要点:
-
GPIO模式选择:
GPIO_Mode_Out_PP // 推挽输出模式
特点:
- 可输出高电平(VCC)和低电平(GND)
- 驱动能力强(最大25mA)
- 高低电平明确
- 适合驱动蜂鸣器 -
GPIO速度:
GPIO_Speed_50MHz // 50MHz速度
说明:
- 蜂鸣器不需要高速切换
- 2MHz或10MHz也可以
- 速度越高,功耗越大 -
初始状态:
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)));
}
控制函数说明:
-
Buzzer_On():
GPIO_SetBits(Buzzer_Port, Buzzer_Pin);
功能:
- 设置PC13为高电平(3.3V)
- 蜂鸣器内部振荡电路启动
- 蜂鸣器发声 -
Buzzer_Off():
GPIO_ResetBits(Buzzer_Port, Buzzer_Pin);
功能:
- 设置PC13为低电平(0V)
- 蜂鸣器内部振荡电路停止
- 蜂鸣器停止发声 -
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 硬件连接
连接步骤
-
连接蜂鸣器
蜂鸣器 I/O → STM32 PC13
蜂鸣器 VCC → 3.3V/5V(根据蜂鸣器要求)
蜂鸣器 GND → GND -
连接调试器
J-Link/ST-Link:
SWDIO → PA13
SWCLK → PA14
GND → GND
VCC → 3.3V
9.2 编译与下载
编译工程
- 打开
MDK/TEST_CODE.uvproj - 选择目标芯片:STM32F103C8
- 编译:Project → Build Target (F7)
- 检查编译结果:0 Error(s), 0 Warning(s)
下载程序
- 配置调试器:Options → Debug → 选择 J-Link/ST-Link
- 下载程序:Flash → Download (F8)
- 复位运行
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:蜂鸣器不响
可能原因:
- ❌ 蜂鸣器连接错误
- ❌ 蜂鸣器极性接反
- ❌ GPIO配置错误
- ❌ 蜂鸣器损坏
解决方案:
// 调试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();
}
}