STM32 | 有源蜂鸣器响,无源蜂鸣器播音乐_有源蜂鸣器声效
目录
Overview
有源蜂鸣器
无源蜂鸣器
有源蜂鸣器控制
GPIO配置
控制程序
无源蜂鸣器控制
反转GPIO控制
GPIO配置
控制接口
PWM控制
GPIO配置
控制函数
改变频率播音乐
原理
1. 频率决定音调
2. 占空比决定音量
GPIO初始化
结构体定义和音符频率表
播放接口
播放生日快乐歌
Overview
有源蜂鸣器
【结构与工作原理】
内部集成了振荡电路,只需提供直流电源(通常为 3-5V)即可发声,频率固定(常见为 2-4kHz)。
【驱动电路】
驱动简单,直接通过 GPIO 控制通断即可。需注意添加限流电阻(约 100Ω)保护 IO 口。
无源蜂鸣器
【结构与工作原理】
内部不含振荡电路,需要外部提供 PWM(脉冲宽度调制)信号驱动,通过调整频率和占空比可发出不同音调。
【驱动电路】
需要 PWM 信号驱动,需配置 STM32 的定时器产生特定频率的方波。例如,若需发出 4kHz 的声音,则 PWM 频率应设为 4kHz。
有源蜂鸣器控制
GPIO配置
我的这个有源蜂鸣器模块带三个引脚,分别是VCC,I/O和GND,I/O口输入高电平时,蜂鸣器响。
(也有一些有源蜂鸣器只有两个引脚,当电压差满足驱动就会响)
- 连接I/O引脚的GPIO引脚,配置为简单的输出即可。
// 外部有源蜂鸣器void ExtActiveBuzzerInit(){ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //使能GPIOE端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度为50MHz GPIO_Init(GPIOE, &GPIO_InitStructure); //根据参数初始化 GPIO_ResetBits(GPIOE, GPIO_Pin_2);//输出0,关闭蜂鸣器输出}
控制程序
- 一秒响,一秒不响
void task1_task(void *pvParameters){ExtActiveBuzzerInit();while(1){SetExtActiveBuzzer(1);printf(\"SetExtActiveBuzzer(1)\\r\\n\");delay_ms(1000);SetExtActiveBuzzer(0);printf(\"SetExtActiveBuzzer(0)\\r\\n\");delay_ms(1000);}}
无源蜂鸣器控制
无源蜂鸣器内部不含振荡电路,需要外部提供 PWM(脉冲宽度调制)信号驱动,通过调整频率和占空比可发出不同音调。
不使用 PWM 的情况下,也可以通过 GPIO 软件翻转来模拟 PWM 信号驱动无源蜂鸣器
反转GPIO控制
因为想观察不同的频率,所以我配置了两个GPIO,以不同频率做电平翻转
GPIO配置
void ExtPassiveBuzzerInit(){GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //使能GPIOE端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //BEEP 端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度为50MHz GPIO_Init(GPIOE, &GPIO_InitStructure); //根据参数初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOE, &GPIO_InitStructure);}
控制接口
一个基础的循环周期为100us
- 对于PE1,每100us后,电平翻转一次,所以方波周期是200us,频率就是5KHz
- 对于PE3,每1000us(1ms)后,电平翻转一次,所以方波周期是2ms,频率就是500Hz
频率决定音调的高低,而更高的频率通常会让人感觉 “更刺耳”
void task1_task(void *pvParameters){int i = 0;int bE1Set = 0;int bE3Set = 0;ExtPassiveBuzzerInit();while (1){i++;if (i >= 10000) {printf(\"i = %d \\r\\n\",i);i = 0;}if ( i % 2 == 0){if (bE1Set == 1){GPIO_ResetBits(GPIOE, GPIO_Pin_1);bE1Set = 0;}else{GPIO_SetBits(GPIOE, GPIO_Pin_1);bE1Set = 1;}}if ( i % 20 == 0){if (bE3Set == 1){GPIO_ResetBits(GPIOE, GPIO_Pin_3);bE3Set = 0;}else{GPIO_SetBits(GPIOE, GPIO_Pin_3);bE3Set = 1;}}// 半个周期100usdelay_us(100);}}
PWM控制
使用PA6,复用TIM3的通道1
GPIO配置
void ExtPassiveBuzzerPWMInit(){ GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure; // 使用PA6,做TIM3_CH1 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //设置该引脚为复用输出功能,输出TIM3 CH1的PWM脉冲波形GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM_CH1GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO //初始化TIM3TIM_TimeBaseStructure.TIM_Period = 35999; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//初始化TIM3 Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIM3}
控制函数
占空比会用于实现音量高低变化
void task1_task(void *pvParameters){ExtPassiveBuzzerPWMInit(); while(1) { printf(\"Beep --- High Volume\\r\\n\"); // 高音量 (占空比约75%) TIM_SetCompare1(TIM3, 27000); // 36000 * 0.75 = 27000 delay_ms(1000); printf(\"Beep --- Low Volume\\r\\n\"); // 低音量 (占空比约10%) TIM_SetCompare1(TIM3, 3600); delay_ms(1000); }}
改变频率播音乐
原理
PWM(脉冲宽度调制)通过改变输出信号的频率和占空比,可以驱动无源蜂鸣器播放音乐。
如何理解频率和占空比的作用:
1. 频率决定音调
-
频率:指 PWM 信号每秒的周期数(单位:Hz)。
-
音调:不同频率对应不同音符,例如:
-
Do (C4) = 262 Hz
-
Re (D4) = 294 Hz
-
Mi (E4) = 330 Hz
-
通过快速切换 PWM 频率,可以组合出音乐旋律。
2. 占空比决定音量
-
占空比:指高电平时间占整个周期的比例(通常用百分比表示)。
-
音量:对于无源蜂鸣器,50% 占空比通常能产生最大音量,因为此时信号对称,能充分驱动蜂鸣器振动。
GPIO初始化
和上面的函数一样,但是留出了控制频率的参数输入
void ExtPassiveBuzzerAdvancePWMInit(uint32_t frequency){ GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure; // 计算自动重装载值和预分频值 uint32_t timer_clock = 72000000; // 72MHz uint16_t psc = 1; uint16_t arr; // 使用PA6,做TIM3_CH1 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //设置该引脚为复用输出功能,输出TIM3 CH1的PWM脉冲波形GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //TIM_CH1GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO //初始化TIM3 // 计算自动重装载值,确保不超过16位 arr = timer_clock / (frequency * psc); if (arr > 65535) { psc = timer_clock / (65535 * frequency) + 1; arr = timer_clock / (frequency * psc); } TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler = psc-1; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//初始化TIM3 Channel1 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIM3 // 设置初始占空比为50% TIM_SetCompare1(TIM3, arr / 2);}
结构体定义和音符频率表
// 音乐结构定义typedef struct { const uint8_t* melody; // 音符数组 const uint8_t* duration; // 节拍数组 uint8_t length; // 音符长度} Music_t;// 音符频率表 (Hz)const uint16_t notes[] = { 0, // 休止符 262, // C4 (Do) 294, // D4 (Re) 330, // E4 (Mi) 349, // F4 (Fa) 392, // G4 (Sol) 440, // A4 (La) 494, // B4 (Si) 523 // C5 (Do高八度)};
播放接口
beat节拍,每一拍100ms
// 播放单个音符void PlayNote(uint8_t note_index, uint8_t beat){ uint16_t frequency; if (note_index > 8) return; // 确保索引有效 frequency = notes[note_index]; if (frequency > 0) { // 初始化PWM频率 ExtPassiveBuzzerAdvancePWMInit(frequency); } else { // 休止符: 关闭PWM输出 TIM_SetCompare1(TIM3, 0); } // 播放指定节拍 (1拍 = 100ms) delay_ms(beat * 100);}// 播放音乐void PlayMusic(const Music_t* music){ uint8_t i; for (i = 0; i length; i++) { PlayNote(music->melody[i], music->duration[i]); } // 播放结束,关闭蜂鸣器 TIM_SetCompare1(TIM3, 0);}
播放生日快乐歌
音符(控制PWM频率)和节拍数(控制音符对于频率PWM持续时长)
void task1_task(void *pvParameters){ // 定义任务局部的音乐结构体指针 static Music_t birthday; // 示例音乐:生日快乐歌 static const uint8_t birthday_melody[] = {5, 5, 6, 5, 1, 7, 5, 5, 6, 5, 2, 1, 5, 5, 5, 3, 1, 7, 6, 4, 4, 3, 1, 2, 1}; static const uint8_t birthday_duration[] = {2, 2, 4, 4, 4, 8, 2, 2, 4, 4, 4, 8, 2, 2, 2, 4, 4, 4, 8, 2, 2, 4, 4, 4, 8}; // 在任务启动时初始化结构体 birthday.melody = birthday_melody; birthday.duration = birthday_duration; birthday.length = sizeof(birthday_melody); while(1) { // 循环播放生日快乐歌 PlayMusic(&birthday); delay_ms(1000); // 播放间隔 }}