通过原理图确定蜂鸣器引脚


电路理解
这里Q4 S8050是一个NPN型三极管, 原理图如下

它在这里的作用就是开关并且放大电流. 从芯片的BEEP GPIO口拉高时出来的是20mA左右的小电流, 然而蜂鸣器需要的电流是30~50mA, 所以这里需要三极管将BEEP引脚输出的电流放大.
大电流需要外部独立的电源供电, 所以蜂鸣器的上方接3.3V电源, 用来给蜂鸣器供电.
三极管的特性
基极(2)小电流流过时, 三极管导通, 大电流从集电极(3)--流向-->发射极(1), 这时蜂鸣器响基极(2)关闭时, 三极管截止, 不导通, 这时蜂鸣器不响.
所以, BEEP引脚输出高电平时, 蜂鸣器响, 低电平时, 蜂鸣器关闭
在这里我要通过软件频繁反转BEEP引脚产生高低电频变化, 实现模拟的PWM方波, 然后利用这个方波, 形成音调, 最终能够通过蜂鸣器播放一段简单的音乐
通过CubeMX配置BEEP引脚为推挽输出

生成的代码如下:
c
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOE, LED_R_Pin|LED_G_Pin|LED_B_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : BEEP_Pin */
GPIO_InitStruct.Pin = BEEP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(BEEP_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : LED_R_Pin LED_G_Pin LED_B_Pin */
GPIO_InitStruct.Pin = LED_R_Pin|LED_G_Pin|LED_B_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}
其中增加了BEEP_Pin相关的硬件初始化
集成更加精准的delay函数
具体的函数实现参考文章 <<STM32L475实现精度更好的delay函数>>
编写PWM方波模拟函数, 及乐谱, 实现生日快乐歌
c
#include "beep_music.h"
#include "main.h"
#include "delay.h"
/************************** 8位机音乐 标准音调频率表(含功夫专用升调,完美适配) **************************/
#define NOTE_REST 0 // 休止符,静音
#define NOTE_C4 262 // 中音1 (基准音)
#define NOTE_D4 294 // 中音2
#define NOTE_E4 330 // 中音3
#define NOTE_F4 349 // 中音4
#define NOTE_G4 392 // 中音5
#define NOTE_A4 440 // 中音6
#define NOTE_B4 494 // 中音7
#define NOTE_C5 523 // 高音1
#define NOTE_D5 587 // 高音2
#define NOTE_E5 659 // 高音3
#define NOTE_F5 698 // 高音4
#define NOTE_G5 784 // 高音5
/************************** 节拍定义 **************************/
#define BPM 120 // 每分钟120拍
#define QUARTER_BEAT (60000 / BPM) // 四分音符 = 500ms
#define BEAT_1_4 QUARTER_BEAT // 四分音符
#define BEAT_1_8 (QUARTER_BEAT / 2)// 八分音符
#define BEAT_3_8 (BEAT_1_8 * 3) // 附点八分音符 (生日快乐歌的核心节奏)
#define BEAT_1_16 (QUARTER_BEAT / 4)// 十六分音符
#define BEAT_1_2 (QUARTER_BEAT * 2)// 二分音符
#define BEAT_3_4 (QUARTER_BEAT * 3)// 附点二分音符 (小节末尾的长音)
#define BEAT_PAUSE 40 // 增加一点间隙感
// 音乐单元:音符 + 节拍(节拍对应持续时间)
typedef struct
{
uint16_t note; // 音符(如NOTE_E5、NOTE_REST)
uint16_t beat; // 节拍(如BEAT_1_8、BEAT_1_4)
} Music_Note_t;
// 生日快乐歌完整音乐序列
// 此处感谢妈妈耐心的乐理知识指导, 拥抱
Music_Note_t Birthday_Music[] = {
// 第一句
{NOTE_REST, BEAT_1_4}, {NOTE_REST, BEAT_1_4}, {NOTE_G4, BEAT_1_8},{NOTE_G4, BEAT_1_8}, {NOTE_A4, BEAT_1_4}, {NOTE_G4, BEAT_1_4}, {NOTE_C5, BEAT_1_4}, {NOTE_B4, BEAT_1_4},
// 第二句
{NOTE_G4, BEAT_1_8},{NOTE_G4, BEAT_1_8}, {NOTE_A4, BEAT_1_4}, {NOTE_G4, BEAT_1_4}, {NOTE_D4, BEAT_1_4}, {NOTE_C4, BEAT_1_4},
// 第三句
{NOTE_G4, BEAT_1_8},{NOTE_G4, BEAT_1_8}, {NOTE_G5, BEAT_1_4}, {NOTE_E5, BEAT_1_4}, {NOTE_C5, BEAT_1_4}, {NOTE_B4, BEAT_1_4}, {NOTE_A4, BEAT_1_4},
// 第四句
{NOTE_F4, BEAT_1_8}, {NOTE_F4, BEAT_1_8}, {NOTE_E5, BEAT_1_4}, {NOTE_C5, BEAT_1_4}, {NOTE_D4, BEAT_1_4}, {NOTE_C4, BEAT_3_4},
{NOTE_REST, BEAT_1_2}
};
// 生日快乐歌音乐总长度(自动计算数组元素个数)
#define BIRTHDAY_MUSIC_LEN (sizeof(Birthday_Music)/sizeof(Music_Note_t))
// 软件模拟PWM蜂鸣器 - 可调音量、可调音调
// 参数说明:
// high_time : 高电平持续时间(单位:微秒us) 高电平=蜂鸣器响
// low_time : 低电平持续时间(单位:微秒us) 低电平=蜂鸣器停
// cycle_num : 发声次数(0=无限循环发声,直到手动停止)
void BEEP_Soft_PWM(uint16_t high_time, uint16_t low_time, uint16_t cycle_num)
{
uint16_t i = 0;
if(cycle_num == 0) // 无限循环发声
{
while(1)
{
HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_SET); // PB2高电平,蜂鸣器通电
delay_us(high_time); // 保持高电平时间
HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);// PB2低电平,蜂鸣器断电
delay_us(low_time); // 保持低电平时间
}
}
else // 指定次数发声
{
for(i=0; i<cycle_num; i++)
{
HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_SET);
delay_us(high_time);
HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);
delay_us(low_time);
}
}
// 发声结束,PB2置低,蜂鸣器彻底停止
HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);
}
// 输入:音符频率(如NOTE_E5),输出:对应的high_time/low_time
void Note_To_PWM(uint16_t note, uint16_t *high_time, uint16_t *low_time)
{
if(note == NOTE_REST) // 休止符:高低电平均为0(静音)
{
*high_time = 0;
*low_time = 0;
return;
}
uint32_t period = 1000000 / note; // 计算单个周期的总时长(us)
*high_time = period / 2; // 50%占空比
*low_time = period - *high_time; // 补全周期(避免整数截断误差)
}
void BEEP_Play_Birthday_Music(void)
{
uint16_t high_time, low_time;
uint32_t single_cycle_us;
uint32_t cycle_num;
for(uint16_t i=0; i<BIRTHDAY_MUSIC_LEN; i++)
{
Note_To_PWM(Birthday_Music[i].note, &high_time, &low_time);
if(Birthday_Music[i].note == NOTE_REST)
{
delay_ms(Birthday_Music[i].beat);
continue;
}
single_cycle_us = high_time + low_time;
// 关键修改:实际播放时长 = 理论节拍时长 - 间隙时长
// 确保 (Birthday_Music[i].beat) 必须大于 BEAT_PAUSE
uint32_t actual_play_ms = Birthday_Music[i].beat - BEAT_PAUSE;
cycle_num = (actual_play_ms * 1000) / single_cycle_us;
BEEP_Soft_PWM(high_time, low_time, cycle_num);
// 补回间隙,使得总时长等于节拍定义
delay_ms(BEAT_PAUSE);
}
}
运行效果如下:
虚拟PWM通过蜂鸣器实现音乐播放