蜂鸣器驱动——支持连续、单次与模式蜂鸣的通用方案

蜂鸣器驱动——支持连续、单次与模式蜂鸣的通用方案

问题背景

蜂鸣器是嵌入式项目中最常用的声音反馈器件。看似简单的"响一下、停一下",在实际产品中往往需要多种模式:开机长鸣、按键短促提示、报警间歇响、故障连续报警。直接用 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); } }

注意事项

  1. buzzer_update必须在主循环或定时器中断中周期性调用(建议 10~50Hz),否则定时自动关闭和模式切换不会生效

  2. active_level字段区分高电平有效和低电平有效的硬件设计

  3. 模式播放时beep_count参数内部会转换为总开关次数(count × 2 - 1),外部调用时只需传入"响几声"

  4. 所有 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; } ​