目錄
?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; //速度為50MHzGPIO_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; //速度為50MHzGPIO_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_CH1RCC_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指定的參數初始化外設TIM3TIM_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 = 27000delay_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; // 72MHzuint16_t psc = 1;uint16_t arr;// 使用PA6,做TIM3_CH1RCC_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指定的參數初始化外設TIM3TIM_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 < music->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); // 播放間隔}
}