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通过蜂鸣器实现音乐播放

相关推荐
钰珠AIOT7 分钟前
在同一块电路板上同时存在 0805 0603 不同的封装有什么利弊?
嵌入式硬件
代码游侠9 分钟前
复习——Linux设备驱动开发笔记
linux·arm开发·驱动开发·笔记·嵌入式硬件·架构
代码游侠11 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
xuxg200513 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT15 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen15 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制
全栈游侠18 小时前
STM32F103XX 02-电源与备份寄存器
stm32·单片机·嵌入式硬件
Lsir10110_18 小时前
【Linux】中断 —— 操作系统的运行基石
linux·运维·嵌入式硬件
深圳市九鼎创展科技20 小时前
瑞芯微 RK3399 开发板 X3399 评测:高性能 ARM 平台的多面手
linux·arm开发·人工智能·单片机·嵌入式硬件·边缘计算
辰哥单片机设计20 小时前
STM32项目分享:车辆防盗报警系统
stm32·单片机·嵌入式硬件