From b7f2539321065bee0e13b445a6ed3a7633a22377 Mon Sep 17 00:00:00 2001 From: Robofish <1683502971@qq.com> Date: Thu, 23 Oct 2025 19:46:14 +0800 Subject: [PATCH] buzzer --- device/buzzer.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++-- device/buzzer.h | 103 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 213 insertions(+), 11 deletions(-) diff --git a/device/buzzer.c b/device/buzzer.c index 013c1a8..cfaf777 100644 --- a/device/buzzer.c +++ b/device/buzzer.c @@ -1,4 +1,6 @@ #include "device/buzzer.h" +#include "bsp/time.h" +#include /* USER INCLUDE BEGIN */ @@ -8,6 +10,85 @@ /* USER DEFINE END */ +#define MUSIC_DEFAULT_VOLUME 0.5f +#define MUSIC_A4_FREQ 440.0f // A4音符频率 + +/* USER MUSIC MENU BEGIN */ +// RM音乐 +const Tone_t RM[] = { + {NOTE_B, 5, 200}, + {NOTE_G, 4, 200}, + {NOTE_B, 5, 400}, + {NOTE_G, 4, 200}, + {NOTE_B, 5, 400}, + {NOTE_G, 4, 200}, + {NOTE_D, 5, 400}, + {NOTE_G, 4, 200}, + {NOTE_C, 5, 200}, + {NOTE_C, 5, 200}, + {NOTE_G, 4, 200}, + {NOTE_B, 5, 200}, + {NOTE_C, 5, 200} +}; + +// Nokia 经典铃声音符 +const Tone_t NOKIA[] = { + {NOTE_E, 5, 125}, {NOTE_D, 5, 125}, {NOTE_FS, 4, 250}, {NOTE_GS, 4, 250}, + {NOTE_CS, 5, 125}, {NOTE_B, 4, 125}, {NOTE_D, 4, 250}, {NOTE_E, 4, 250}, + {NOTE_B, 4, 125}, {NOTE_A, 4, 125}, {NOTE_CS, 4, 250}, {NOTE_E, 4, 250}, + {NOTE_A, 4, 500} +}; +/* USER MUSIC MENU END */ + +static void BUZZER_Update(BUZZER_t *buzzer){ + buzzer->header.online = true; + buzzer->header.last_online_time = BSP_TIME_Get_ms(); +} + +// 根据音符和八度计算频率的辅助函数 +static float BUZZER_CalcFreq(NOTE_t note, uint8_t octave) { + if (note == NOTE_REST) { + return 0.0f; // 休止符返回0频率 + } + + // 将音符和八度转换为MIDI音符编号 + int midi_num = (int)note + (int)((octave + 1) * 12); + + // 使用A4 (440Hz) 作为参考,计算频率 + // 公式: freq = 440 * 2^((midi_num - 69)/12) + float freq = 440.0f * powf(2.0f, ((float)midi_num - 69.0f) / 12.0f); + + return freq; +} + +// 播放单个音符 +static int8_t BUZZER_PlayTone(BUZZER_t *buzzer, NOTE_t note, uint8_t octave, uint16_t duration_ms) { + if (buzzer == NULL || !buzzer->header.online) + return DEVICE_ERR; + + float freq = BUZZER_CalcFreq(note, octave); + + if (freq > 0.0f) { + // 播放音符 + if (BUZZER_Set(buzzer, freq, MUSIC_DEFAULT_VOLUME) != DEVICE_OK) + return DEVICE_ERR; + + if (BUZZER_Start(buzzer) != DEVICE_OK) + return DEVICE_ERR; + } else { + // 休止符,停止播放 + BUZZER_Stop(buzzer); + } + + // 等待指定时间 + BSP_TIME_Delay_ms(duration_ms); + + // 停止当前音符,为下一个音符做准备 + BUZZER_Stop(buzzer); + BSP_TIME_Delay_ms(20); // 短暂间隔 + + return DEVICE_OK; +} int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel) { if (buzzer == NULL) return DEVICE_ERR; @@ -23,7 +104,7 @@ int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel) { int8_t BUZZER_Start(BUZZER_t *buzzer) { if (buzzer == NULL || !buzzer->header.online) return DEVICE_ERR; - + BUZZER_Update(buzzer); return (BSP_PWM_Start(buzzer->channel) == BSP_OK) ? DEVICE_OK : DEVICE_ERR; } @@ -31,7 +112,7 @@ int8_t BUZZER_Start(BUZZER_t *buzzer) { int8_t BUZZER_Stop(BUZZER_t *buzzer) { if (buzzer == NULL || !buzzer->header.online) return DEVICE_ERR; - + BUZZER_Update(buzzer); return (BSP_PWM_Stop(buzzer->channel) == BSP_OK) ? DEVICE_OK : DEVICE_ERR; } @@ -41,7 +122,7 @@ int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle) { return DEVICE_ERR; int result = DEVICE_OK ; - + BUZZER_Update(buzzer); if (BSP_PWM_SetFreq(buzzer->channel, freq) != BSP_OK) result = DEVICE_ERR; @@ -51,6 +132,40 @@ int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle) { return result; } +int8_t BUZZER_PlayMusic(BUZZER_t *buzzer, MUSIC_t music) { + if (buzzer == NULL || !buzzer->header.online) + return DEVICE_ERR; + + const Tone_t *melody = NULL; + size_t melody_length = 0; + + // 根据音乐类型选择对应的音符数组 + switch (music) { + case MUSIC_RM: + melody = RM; + melody_length = sizeof(RM) / sizeof(Tone_t); + break; + case MUSIC_NOKIA: + melody = NOKIA; + melody_length = sizeof(NOKIA) / sizeof(Tone_t); + break; + default: + return DEVICE_ERR; + } + + // 播放整首音乐 + for (size_t i = 0; i < melody_length; i++) { + if (BUZZER_PlayTone(buzzer, melody[i].note, melody[i].octave, melody[i].duration_ms) != DEVICE_OK) { + BUZZER_Stop(buzzer); // 出错时停止播放 + return DEVICE_ERR; + } + } + + // 音乐播放完成后停止 + BUZZER_Stop(buzzer); + return DEVICE_OK; +} + /* USER FUNCTION BEGIN */ /* USER FUNCTION END */ diff --git a/device/buzzer.h b/device/buzzer.h index f743dff..0c3a593 100644 --- a/device/buzzer.h +++ b/device/buzzer.h @@ -1,3 +1,11 @@ +/** + * @file buzzer.h + * @brief 蜂鸣器设备驱动头文件 + * @details 提供蜂鸣器音频播放功能,支持单音符播放和预设音乐播放 + * @author Generated by STM32CubeMX + * @date 2025年10月23日 + */ + #pragma once #ifdef __cplusplus @@ -5,9 +13,10 @@ extern "C" { #endif /* Includes ----------------------------------------------------------------- */ -#include "device.h" -#include "bsp/pwm.h" -#include +#include "bsp/pwm.h" // PWM底层硬件抽象层 +#include "device.h" // 设备通用头文件 +#include // 标准定义 +#include // 标准整型定义 /* USER INCLUDE BEGIN */ @@ -20,9 +29,55 @@ extern "C" { /* USER DEFINE END */ /* Exported types ----------------------------------------------------------- */ + +/** + * @brief 音符枚举类型 + * @details 定义标准十二平均律音符,用于音乐播放 + */ +typedef enum { + NOTE_C = 0, ///< Do音符 + NOTE_CS = 1, ///< Do#音符 (升Do) + NOTE_D = 2, ///< Re音符 + NOTE_DS = 3, ///< Re#音符 (升Re) + NOTE_E = 4, ///< Mi音符 + NOTE_F = 5, ///< Fa音符 + NOTE_FS = 6, ///< Fa#音符 (升Fa) + NOTE_G = 7, ///< Sol音符 + NOTE_GS = 8, ///< Sol#音符 (升Sol) + NOTE_A = 9, ///< La音符 + NOTE_AS = 10, ///< La#音符 (升La) + NOTE_B = 11, ///< Si音符 + NOTE_REST = 255 ///< 休止符 (无声音) +} NOTE_t; + +/** + * @brief 音调结构体 + * @details 定义一个完整的音调信息,包括音符、八度和持续时间 + */ typedef struct { - DEVICE_Header_t header; - BSP_PWM_Channel_t channel; + NOTE_t note; ///< 音符名称 (使用NOTE_t枚举) + uint8_t octave; ///< 八度 (0-8,通常使用3-7) + uint16_t duration_ms; ///< 持续时间,单位毫秒 +} Tone_t; + +/** + * @brief 预设音乐枚举类型 + * @details 定义可播放的预设音乐类型 + */ +typedef enum { + /* USER MUSIC MENU BEGIN */ + MUSIC_RM, ///< RM战队音乐 + MUSIC_NOKIA, ///< 诺基亚经典铃声 + /* USER MUSIC MENU END */ +} MUSIC_t; + +/** + * @brief 蜂鸣器设备结构体 + * @details 蜂鸣器设备的完整描述,包含设备头信息和PWM通道 + */ +typedef struct { + DEVICE_Header_t header; ///< 设备通用头信息 (在线状态、时间戳等) + BSP_PWM_Channel_t channel; ///< PWM输出通道 } BUZZER_t; /* USER STRUCT BEGIN */ @@ -31,17 +86,49 @@ typedef struct { /* Exported functions prototypes -------------------------------------------- */ +/** + * @brief 初始化蜂鸣器设备 + * @param buzzer 蜂鸣器设备结构体指针 + * @param channel PWM输出通道 + * @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败 + * @note 初始化后蜂鸣器处于停止状态 + */ int8_t BUZZER_Init(BUZZER_t *buzzer, BSP_PWM_Channel_t channel); - +/** + * @brief 启动蜂鸣器播放 + * @param buzzer 蜂鸣器设备结构体指针 + * @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败 + * @note 需要先调用BUZZER_Set设置频率和占空比 + */ int8_t BUZZER_Start(BUZZER_t *buzzer); - +/** + * @brief 停止蜂鸣器播放 + * @param buzzer 蜂鸣器设备结构体指针 + * @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败 + */ int8_t BUZZER_Stop(BUZZER_t *buzzer); - +/** + * @brief 设置蜂鸣器频率和占空比 + * @param buzzer 蜂鸣器设备结构体指针 + * @param freq 频率 (Hz),通常范围20Hz-20kHz + * @param duty_cycle 占空比 (0.0-1.0),影响音量大小 + * @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败 + * @note 设置后需要调用BUZZER_Start才能听到声音 + */ int8_t BUZZER_Set(BUZZER_t *buzzer, float freq, float duty_cycle); +/** + * @brief 播放预设音乐 + * @param buzzer 蜂鸣器设备结构体指针 + * @param music 音乐类型 (使用MUSIC_t枚举) + * @return int8_t 返回值:DEVICE_OK(0) 成功,DEVICE_ERR(-1) 失败 + * @note 这是一个阻塞函数,会播放完整首音乐后返回 + */ +int8_t BUZZER_PlayMusic(BUZZER_t *buzzer, MUSIC_t music); + /* USER FUNCTION BEGIN */ /* USER FUNCTION END */