STM32L475蜂鸣器实验

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


电路理解

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

它在这里的作用就是开关并且放大电流. 从芯片的BEEP GPIO口拉高时出来的是20mA左右的小电流, 然而蜂鸣器需要的电流是30~50mA, 所以这里需要三极管将BEEP引脚输出的电流放大.

大电流需要外部独立的电源供电, 所以蜂鸣器的上方接3.3V电源, 用来给蜂鸣器供电.

三极管的特性

  1. 基极(2)小电流流过时, 三极管导通, 大电流从集电极(3)--流向-->发射极(1), 这时蜂鸣器响
  2. 基极(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通过蜂鸣器实现音乐播放

相关推荐
qq_401700412 小时前
经典78M05 LDO经典应用电路
单片机·嵌入式硬件
欢乐熊嵌入式编程3 小时前
BLE 蓝牙开发入门:用手机控制开发板 LED
单片机·ble蓝牙开发·嵌入式入门
小美单片机4 小时前
External model DLL ”ADC083XDLL“ not found_proteus仿真报错解决方法
c语言·单片机·51单片机·proteus·课程设计·课设
Tyrion.Mon4 小时前
沁恒PD协议诱骗芯片CH224A/CH224Q--IIC研究(0x60~0x8F)
单片机·硬件工程
田甲4 小时前
基于STM32L051和HDC2080的低功耗温湿度计
单片机·嵌入式硬件·温湿度计·hdc2080
List<String> error_P4 小时前
STM32 GPIO HAL库常用函数
stm32·单片机·hal库
乡野码圣5 小时前
【RK3588 Android12】设备树DTS进阶
嵌入式硬件
小痞同学5 小时前
【铁头山羊STM32】HAL库 5.SPI部分
stm32·单片机·嵌入式硬件