通过原理图确定蜂鸣器引脚
电路理解
这里Q4 S8050是一个NPN型三极管, 原理图如下
它在这里的作用就是开关并且放大电流. 从芯片的BEEP GPIO口拉高时出来的是20mA左右的小电流, 然而蜂鸣器需要的电流是30~50mA, 所以这里需要三极管将BEEP引脚输出的电流放大.
大电流需要外部独立的电源供电, 所以蜂鸣器的上方接3.3V电源, 用来给蜂鸣器供电.
三极管的特性
基极(2)小电流流过时, 三极管导通, 大电流从集电极(3)–流向–>发射极(1), 这时蜂鸣器响基极(2)关闭时, 三极管截止, 不导通, 这时蜂鸣器不响.
所以, BEEP引脚输出高电平时, 蜂鸣器响, 低电平时, 蜂鸣器关闭
在这里我要通过软件频繁反转BEEP引脚产生高低电频变化, 实现模拟的PWM方波, 然后利用这个方波, 形成音调, 最终能够通过蜂鸣器播放一段简单的音乐
通过CubeMX配置BEEP引脚为推挽输出
生成的代码如下:
/** Configure pins as * Analog * Input * Output * EVENT_OUT * EXTI */voidMX_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方波模拟函数, 及乐谱, 实现生日快乐歌
#include"beep_music.h"#include"main.h"#include"delay.h"/************************** 8位机音乐 标准音调频率表(含功夫专用升调,完美适配) **************************/#defineNOTE_REST0// 休止符,静音#defineNOTE_C4262// 中音1 (基准音)#defineNOTE_D4294// 中音2#defineNOTE_E4330// 中音3#defineNOTE_F4349// 中音4#defineNOTE_G4392// 中音5#defineNOTE_A4440// 中音6#defineNOTE_B4494// 中音7#defineNOTE_C5523// 高音1#defineNOTE_D5587// 高音2#defineNOTE_E5659// 高音3#defineNOTE_F5698// 高音4#defineNOTE_G5784// 高音5/************************** 节拍定义 **************************/#defineBPM120// 每分钟120拍#defineQUARTER_BEAT(60000/BPM)// 四分音符 = 500ms#defineBEAT_1_4QUARTER_BEAT// 四分音符#defineBEAT_1_8(QUARTER_BEAT/2)// 八分音符#defineBEAT_3_8(BEAT_1_8*3)// 附点八分音符 (生日快乐歌的核心节奏)#defineBEAT_1_16(QUARTER_BEAT/4)// 十六分音符#defineBEAT_1_2(QUARTER_BEAT*2)// 二分音符#defineBEAT_3_4(QUARTER_BEAT*3)// 附点二分音符 (小节末尾的长音)#defineBEAT_PAUSE40// 增加一点间隙感// 音乐单元:音符 + 节拍(节拍对应持续时间)typedefstruct{uint16_tnote;// 音符(如NOTE_E5、NOTE_REST)uint16_tbeat;// 节拍(如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}};// 生日快乐歌音乐总长度(自动计算数组元素个数)#defineBIRTHDAY_MUSIC_LEN(sizeof(Birthday_Music)/sizeof(Music_Note_t))// 软件模拟PWM蜂鸣器 - 可调音量、可调音调// 参数说明:// high_time : 高电平持续时间(单位:微秒us) 高电平=蜂鸣器响// low_time : 低电平持续时间(单位:微秒us) 低电平=蜂鸣器停// cycle_num : 发声次数(0=无限循环发声,直到手动停止)voidBEEP_Soft_PWM(uint16_thigh_time,uint16_tlow_time,uint16_tcycle_num){uint16_ti=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_timevoidNote_To_PWM(uint16_tnote,uint16_t*high_time,uint16_t*low_time){if(note==NOTE_REST)// 休止符:高低电平均为0(静音){*high_time=0;*low_time=0;return;}uint32_tperiod=1000000/note;// 计算单个周期的总时长(us)*high_time=period/2;// 50%占空比*low_time=period-*high_time;// 补全周期(避免整数截断误差)}voidBEEP_Play_Birthday_Music(void){uint16_thigh_time,low_time;uint32_tsingle_cycle_us;uint32_tcycle_num;for(uint16_ti=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_PAUSEuint32_tactual_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通过蜂鸣器实现音乐播放