问题背景
蜂鸣器是嵌入式项目中最常用的声音反馈器件。看似简单的"响一下、停一下",在实际产品中往往需要多种模式:开机长鸣、按键短促提示、报警间歇响、故障连续报警。直接用 HAL 库写会导致主循环中充斥时间判断逻辑。
本文给出一个结构体驱动的蜂鸣器模块,抽象出"连续模式"、"单次蜂鸣"和"模式播放"三种工作模式,减少调用方的状态管理负担。
驱动设计
三种工作模式
| 模式 | 枚举值 | 行为 |
|---|---|---|
| 连续模式 | BUZZER_MODE_CONTINUOUS | 调用buzzer_on/off手动控制,驱动不干预 |
| 单次蜂鸣 | BUZZER_MODE_BEEP | 调用buzzer_beep(handle, 200)响 200ms 后自动关闭 |
| 模式播放 | BUZZER_MODE_PATTERN | 调用buzzer_beep_pattern(handle, 3, 100, 100)响 3 声,每声 100ms,间隔 100ms |
状态机核心逻辑
buzzer_update是驱动的核心,需要在主循环或定时器中周期性调用。它根据当前模式自动切换蜂鸣器开关状态:
BEEP 模式:打开蜂鸣器,到达
beep_duration后自动关闭,计数清零PATTERN 模式:在开/关状态之间按
beep_duration/beep_interval交替切换,直到beep_count耗尽
数据结构
typedef struct { uint8_t is_initialized; buzzer_gpio_t buzzer_pin; // GPIO 配置(端口、引脚、有效电平、写函数) buzzer_data_t data; // 运行状态(模式、计时、计数) uint32_t (*get_tick)(void); // 系统时钟获取函数指针 } buzzer_handle_t;通过get_tick函数指针解耦系统时钟来源(HAL_GetTick、定时器、RTOS tick 均可)。
API 参考
| 函数 | 说明 |
|---|---|
buzzer_init(handle) | 初始化句柄,默认关闭 |
buzzer_on(handle) | 打开蜂鸣器(连续模式) |
buzzer_off(handle) | 关闭蜂鸣器 |
buzzer_toggle(handle) | 翻转蜂鸣器状态 |
buzzer_beep(handle, duration_ms) | 单次蜂鸣指定时长 |
buzzer_beep_pattern(handle, count, duration_ms, interval_ms) | 模式蜂鸣 |
buzzer_update(handle) | 状态更新,需在主循环/定时器中周期调用 |
buzzer_get_state(handle) | 获取当前开关状态 |
使用示例
#include "driver_buzzer.h" buzzer_handle_t buzzer; int main(void) { // 初始化蜂鸣器,GPIOB_PIN_0,高电平有效 buzzer.buzzer_pin.port = GPIOB; buzzer.buzzer_pin.pin = GPIO_PIN_0; buzzer.buzzer_pin.active_level = 1; buzzer.buzzer_pin.write_pin = (void(*)(GPIO_TypeDef*,uint16_t,GPIO_PinState))HAL_GPIO_WritePin; buzzer.get_tick = (uint32_t(*)(void))HAL_GetTick; buzzer_init(&buzzer); // 1. 连续模式——手动控制 buzzer_on(&buzzer); HAL_Delay(1000); buzzer_off(&buzzer); // 2. 单次蜂鸣 200ms buzzer_beep(&buzzer, 200); // 3. 三短声报警模式 buzzer_beep_pattern(&buzzer, 3, 100, 100); while (1) { buzzer_update(&buzzer); // 必须在主循环中周期调用 HAL_Delay(10); } }注意事项
buzzer_update必须在主循环或定时器中断中周期性调用(建议 10~50Hz),否则定时自动关闭和模式切换不会生效active_level字段区分高电平有效和低电平有效的硬件设计模式播放时
beep_count参数内部会转换为总开关次数(count × 2 - 1),外部调用时只需传入"响几声"所有 API 都做了空指针检查,传入 NULL 会静默返回
总结
本驱动通过一个简洁的状态机,把蜂鸣器的三种典型使用场景封装到位。连续模式交给调用方自由控制,单次和模式蜂鸣则由buzzer_update自动管理时序,大幅简化主循环逻辑。
完整源码
driver_buzzer.h
/** * @file driver_buzzer.h * @brief 蜂鸣器驱动头文件 * @author 江 * @version V1.0.0 * @date 2026.03.19 */ #ifndef __DRIVER_BUZZER_H #define __DRIVER_BUZZER_H #include "stm32f1xx_hal.h" // 蜂鸣器状态定义 typedef enum { BUZZER_OFF = 0, // 蜂鸣器关闭 BUZZER_ON = 1 // 蜂鸣器开启 } buzzer_state_t; // 蜂鸣器模式定义 typedef enum { BUZZER_MODE_CONTINUOUS = 0, // 连续模式 BUZZER_MODE_BEEP = 1, // 蜂鸣模式 BUZZER_MODE_PATTERN = 2 // 模式播放 } buzzer_mode_t; // GPIO引脚配置结构体 typedef struct { GPIO_TypeDef* port; // GPIO端口 uint16_t pin; // GPIO引脚 uint8_t active_level; // 有效电平(0-低电平有效,1-高电平有效) void (*write_pin)(GPIO_TypeDef* port, uint16_t pin, GPIO_PinState state); // 写引脚函数指针 } buzzer_gpio_t; // 蜂鸣器数据结构体 typedef struct { buzzer_state_t state; // 当前状态 buzzer_mode_t mode; // 当前模式 uint16_t beep_duration; // 蜂鸣持续时间(ms) uint16_t beep_interval; // 蜂鸣间隔时间(ms) uint8_t beep_count; // 蜂鸣次数 uint32_t last_toggle_time; // 上次切换时间 } buzzer_data_t; /** * @brief 蜂鸣器句柄结构体 */ typedef struct { uint8_t is_initialized; // 是否已初始化 buzzer_gpio_t buzzer_pin; // 蜂鸣器GPIO配置 buzzer_data_t data; // 蜂鸣器数据 uint32_t (*get_tick)(void); // 获取系统时间函数指针 } buzzer_handle_t; // 函数声明 void buzzer_init(buzzer_handle_t* handle); void buzzer_on(buzzer_handle_t* handle); void buzzer_off(buzzer_handle_t* handle); void buzzer_toggle(buzzer_handle_t* handle); void buzzer_beep(buzzer_handle_t* handle, uint16_t duration_ms); void buzzer_beep_pattern(buzzer_handle_t* handle, uint8_t count, uint16_t duration_ms, uint16_t interval_ms); void buzzer_update(buzzer_handle_t* handle); buzzer_state_t buzzer_get_state(const buzzer_handle_t* handle); uint8_t buzzer_is_initialized(const buzzer_handle_t* handle); #endif /* __DRIVER_BUZZER_H */ driver_buzzer.c
/** * @file driver_buzzer.c * @brief 蜂鸣器驱动程序 * @author 江 * @version V1.0.0 * @date 2026.03.19 */ #include "driver_buzzer.h" /** * @brief 初始化蜂鸣器 * @param handle: 蜂鸣器句柄指针 * @retval 无 */ void buzzer_init(buzzer_handle_t* handle) { if (handle == NULL) { return; } // 初始化数据结构 handle->data.state = BUZZER_OFF; handle->data.mode = BUZZER_MODE_CONTINUOUS; handle->data.beep_duration = 0; handle->data.beep_interval = 0; handle->data.beep_count = 0; handle->data.last_toggle_time = 0; handle->is_initialized = 1; // 初始化时关闭蜂鸣器 buzzer_off(handle); } /** * @brief 开启蜂鸣器 * @param handle: 蜂鸣器句柄指针 * @retval 无 */ void buzzer_on(buzzer_handle_t* handle) { if (handle == NULL || !handle->is_initialized) { return; } if (handle->buzzer_pin.active_level) { handle->buzzer_pin.write_pin(handle->buzzer_pin.port, handle->buzzer_pin.pin, GPIO_PIN_SET); } else { handle->buzzer_pin.write_pin(handle->buzzer_pin.port, handle->buzzer_pin.pin, GPIO_PIN_RESET); } handle->data.state = BUZZER_ON; } /** * @brief 关闭蜂鸣器 * @param handle: 蜂鸣器句柄指针 * @retval 无 */ void buzzer_off(buzzer_handle_t* handle) { if (handle == NULL || !handle->is_initialized) { return; } if (handle->buzzer_pin.active_level) { handle->buzzer_pin.write_pin(handle->buzzer_pin.port, handle->buzzer_pin.pin, GPIO_PIN_RESET); } else { handle->buzzer_pin.write_pin(handle->buzzer_pin.port, handle->buzzer_pin.pin, GPIO_PIN_SET); } handle->data.state = BUZZER_OFF; } /** * @brief 切换蜂鸣器状态 * @param handle: 蜂鸣器句柄指针 * @retval 无 */ void buzzer_toggle(buzzer_handle_t* handle) { if (handle == NULL || !handle->is_initialized) { return; } if (handle->data.state == BUZZER_ON) { buzzer_off(handle); } else { buzzer_on(handle); } } /** * @brief 蜂鸣器单次蜂鸣 * @param handle: 蜂鸣器句柄指针 * @param duration_ms: 蜂鸣持续时间(毫秒) * @retval 无 */ void buzzer_beep(buzzer_handle_t* handle, uint16_t duration_ms) { if (handle == NULL || !handle->is_initialized || duration_ms == 0) { return; } handle->data.mode = BUZZER_MODE_BEEP; handle->data.beep_duration = duration_ms; handle->data.beep_count = 1; handle->data.last_toggle_time = handle->get_tick(); buzzer_on(handle); } /** * @brief 蜂鸣器模式蜂鸣 * @param handle: 蜂鸣器句柄指针 * @param count: 蜂鸣次数 * @param duration_ms: 每次蜂鸣持续时间(毫秒) * @param interval_ms: 蜂鸣间隔时间(毫秒) * @retval 无 */ void buzzer_beep_pattern(buzzer_handle_t* handle, uint8_t count, uint16_t duration_ms, uint16_t interval_ms) { if (handle == NULL || !handle->is_initialized || count == 0 || duration_ms == 0) { return; } handle->data.mode = BUZZER_MODE_PATTERN; handle->data.beep_duration = duration_ms; handle->data.beep_interval = interval_ms; handle->data.beep_count = count * 2 - 1; // 计算总的开关次数 handle->data.last_toggle_time = handle->get_tick(); buzzer_on(handle); } /** * @brief 蜂鸣器状态更新(需要在主循环或定时器中调用) * @param handle: 蜂鸣器句柄指针 * @retval 无 */ void buzzer_update(buzzer_handle_t* handle) { if (handle == NULL || !handle->is_initialized) { return; } uint32_t current_time = handle->get_tick(); uint32_t elapsed_time = current_time - handle->data.last_toggle_time; switch (handle->data.mode) { case BUZZER_MODE_BEEP: if (handle->data.state == BUZZER_ON && elapsed_time >= handle->data.beep_duration) { buzzer_off(handle); handle->data.beep_count = 0; } break; case BUZZER_MODE_PATTERN: if (handle->data.beep_count > 0) { if (handle->data.state == BUZZER_ON && elapsed_time >= handle->data.beep_duration) { buzzer_off(handle); handle->data.last_toggle_time = current_time; handle->data.beep_count--; } else if (handle->data.state == BUZZER_OFF && elapsed_time >= handle->data.beep_interval) { if (handle->data.beep_count > 0) { buzzer_on(handle); handle->data.last_toggle_time = current_time; handle->data.beep_count--; } } } else { // 模式播放完成,确保蜂鸣器关闭并切换到连续模式 if (handle->data.state == BUZZER_ON) { buzzer_off(handle); } handle->data.mode = BUZZER_MODE_CONTINUOUS; } break; case BUZZER_MODE_CONTINUOUS: default: // 连续模式不需要自动更新 break; } } /** * @brief 获取蜂鸣器当前状态 * @param handle: 蜂鸣器句柄指针 * @retval buzzer_state_t: 蜂鸣器状态 */ buzzer_state_t buzzer_get_state(const buzzer_handle_t* handle) { if (handle == NULL || !handle->is_initialized) { return BUZZER_OFF; } return handle->data.state; } /** * @brief 检查蜂鸣器是否已初始化 * @param handle: 蜂鸣器句柄指针 * @retval uint8_t: 1-已初始化,0-未初始化 */ uint8_t buzzer_is_initialized(const buzzer_handle_t* handle) { if (handle == NULL) { return 0; } return handle->is_initialized; }