Mini MP3 Player

STM32F103C8T6 × HAL库 × UART串口通信

3.3~5V
工作电压
UART 9600
通信方式
20×20mm
模块尺寸
32GB
TF卡支持

01传感器原理与功能

DFPlayer Mini MP3 Player 模块的工作原理、核心功能与引脚定义

1.1 工作原理

DFPlayer Mini MP3 Player 是一款紧凑型串口控制音频解码模块,内部集成了 MCU 控制器和 MP3 解码芯片。模块通过 TF 卡(支持 FAT16/FAT32 文件系统,最大 32GB)存储 MP3/WAV 音频文件。

主控 STM32 通过 UART 串口(9600bps, 8N1)向模块发送 8 字节指令帧,模块接收后解析命令并执行播放、暂停、切歌、音量调节等操作。

解码后的音频通过内置 24-bit DAC 输出,可直接驱动 3W 以下扬声器(SPK1/SPK2),也可通过 DAC_L/DAC_R 接外部功放或耳机。模块还提供 BUSY 引脚指示播放状态。

除串口模式外,模块还支持 I/O 按键模式(短按切歌、长按调音量)和 AD 按键模式(通过 AD 端口分压电阻触发不同曲目),实现独立运行。

串口指令帧格式(8字节)

0x7E
起始
0xFF
版本
0x06
长度
CMD
命令
0x00
反馈
0x00
参数H
0x00
参数L
0xEF
结束

本程序使用的命令

命令码功能
0x01下一首
0x02上一首
0x0D播放/恢复
0x0E暂停

1.2 核心功能

🔊

串口控制播放

MCU通过UART发送指令帧,控制播放、暂停、切歌、音量调节等全部功能

🔌

按键独立控制

支持I/O按键模式和AD按键模式,无需MCU即可通过物理按键独立运行

📣

扬声器直驱

SPK1/SPK2差分输出可直接驱动3W以下8Ω喇叭,无需外部功放电路

💾

TF卡存储

支持FAT16/FAT32文件系统,最大32GB TF卡,支持100个文件夹每文件夹255首

1.3 引脚定义

引脚号名称功能STM32连接
1VCC电源正极 (3.3~5V DC,建议5V)建议5V
2RXUART串口接收PA2 (USART2_TX)
3TXUART串口发送PA3 (USART2_RX)
4DAC_L左声道音频输出接功放/耳机
5DAC_R右声道音频输出接功放/耳机
6SPK2扬声器负极接喇叭-
7GND电源地GND
8SPK1扬声器正极接喇叭+
9IO1触发端口1 (短按上一曲/长按音量-) -
10GND地GND
11IO2触发端口2 (短按下一曲/长按音量+) -
12ADKEY1AD按键1 (触发播放第1首) -
13ADKEY2AD按键2 (触发播放第5首) -
14USB+USB正极 -
15USB-USB负极 -
16BUSY播放状态指示 (低=播放中) -

02模块外观展示

DFPlayer Mini MP3 Player 模块多角度实物展示

外观1
模块整体外观
顶视图
顶视图
底视图
底视图
引脚定义图
引脚定义图

03原理图与PCB封装

DFPlayer Mini MP3 Player 模块原理图与PCB封装展示

原理图
模块原理图
PCB封装
PCB封装

04程序分析

STM32F103C8T6 HAL库源代码逐模块分析

4.1 系统时钟配置

配置说明

main.c — SystemClock_Config()
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue  = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL     = RCC_PLL_MUL9;  // 8MHz x 9 = 72MHz

  if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    Error_Handler();

  RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                  | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;   // AHB = 72MHz
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;      // APB1 = 36MHz
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;      // APB2 = 72MHz

  if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    Error_Handler();
}

4.2 GPIO配置(按键)

配置说明

gpio.c — MX_GPIO_Init()
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /* K1(PA4) K2(PA5) K3(PA6) — 上拉输入 */
  GPIO_InitStruct.Pin   = K1_Pin | K2_Pin | K3_Pin;
  GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull  = GPIO_PULLUP;
  HAL_GPIO_Init(K1_GPIO_Port, &GPIO_InitStruct);
}

引脚宏定义 (main.h)

4.3 USART2串口配置

配置说明

usart.c — MX_USART2_UART_Init()
UART_HandleTypeDef huart2;

void MX_USART2_UART_Init(void)
{
  huart2.Instance        = USART2;
  huart2.Init.BaudRate   = 9600;                  // DFPlayer标准波特率
  huart2.Init.WordLength  = UART_WORDLENGTH_8B;     // 8位数据
  huart2.Init.StopBits    = UART_STOPBITS_1;        // 1停止位
  huart2.Init.Parity      = UART_PARITY_NONE;      // 无校验
  huart2.Init.Mode        = UART_MODE_TX_RX;        // 收发模式
  huart2.Init.HwFlowCtl   = UART_HWCONTROL_NONE;    // 无硬件流控
  huart2.Init.OverSampling= UART_OVERSAMPLING_16;

  if(HAL_UART_Init(&huart2) != HAL_OK)
    Error_Handler();
}

/* USART2 GPIO: PA2=TX(复用推挽), PA3=RX(浮空输入) */
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
  if(uartHandle->Instance == USART2)
  {
    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    // PA2 → USART2_TX (复用推挽输出)
    GPIO_InitStruct.Pin   = GPIO_PIN_2;
    GPIO_InitStruct.Mode  = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // PA3 → USART2_RX (输入)
    GPIO_InitStruct.Pin   = GPIO_PIN_3;
    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
  }
}

4.4 MP3串口命令发送函数

函数说明

MP3_Send() 函数封装了 DFPlayer Mini 的串口命令发送逻辑。使用 u2_printf 宏通过 USART2 发送 8 字节指令帧。

main.c — u2_printf & MP3_Send()
uint8_t USART2_TX_BUF[200];
#define u2_printf(...)  HAL_UART_Transmit(&huart2, USART2_TX_BUF, \
                          sprintf((char *)USART2_TX_BUF, __VA_ARGS__), 0xffff)

/*
 * 发送8字节MP3指令帧
 * CMD: 0x01=下一首  0x02=上一首  0x0D=播放  0x0E=暂停
 */
void MP3_Send(uint8_t CMD)
{
  u2_printf("%c%c%c%c%c%c%c%c", 0x7E, 0xFF, 0x06, CMD, 0x00, 0x00, 0x00, 0xEF);
}

4.5 主循环逻辑

逻辑分析

main.c — main() while(1) loop
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  OLED_Init();

  while (1)
  {
    /* —— 按键检测 —— */
    if(HAL_GPIO_ReadPin(K1_GPIO_Port, K1_Pin) &&
       HAL_GPIO_ReadPin(K2_GPIO_Port, K2_Pin) &&
       HAL_GPIO_ReadPin(K3_GPIO_Port, K3_Pin))
      flag_Key = 0;  // 所有按键松开,复位消抖标志

    if(!HAL_GPIO_ReadPin(K1_GPIO_Port, K1_Pin) && !flag_Key)  // 上一首
    {
      MP3_Send(0x02);
      (song == 0) ? (song = 5) : (song--);
      Flag_Play = 1;
      flag_Key = 1;
    }

    if(!HAL_GPIO_ReadPin(K2_GPIO_Port, K2_Pin) && !flag_Key)  // 下一首
    {
      MP3_Send(0x01);
      (song == 5) ? (song = 0) : (song++);
      Flag_Play = 1;
      flag_Key = 1;
    }

    if(!HAL_GPIO_ReadPin(K3_GPIO_Port, K3_Pin) && !flag_Key)  // 播放/暂停
    {
      if(Flag_Play == 0) { Flag_Play = 1; MP3_Send(0x0D); }  // 播放
      else               { Flag_Play = 0; MP3_Send(0x0E); }  // 暂停
      flag_Key = 1;
    }

    /* —— OLED屏幕显示(50ms刷新) —— */
    if(HAL_GetTick() - Time_Now >= 50)
    {
      OLED_Clear();
      switch(song)  // 显示歌名
      {
        case 0: OLED_CenterPrintf(12, "枫");            break;
        case 1: OLED_CenterPrintf(12, "蒲公英的约定");  break;
        case 2: OLED_CenterPrintf(12, "富士山下");      break;
        case 3: OLED_CenterPrintf(12, "瞬");            break;
        case 4: OLED_CenterPrintf(12, "唯一");          break;
        case 5: OLED_CenterPrintf(12, "特别的人");      break;
      }
      if(Flag_Play == 0) OLED_CenterPrintf(36, "暂停");
      else              OLED_CenterPrintf(36, "播放");
      OLED_Update();
      Time_Now = HAL_GetTick();
    }
  }
}

歌曲与状态对照表

song 值歌曲名称按键操作MP3命令
0枫 - -
1蒲公英的约定K2 下一首0x01
2富士山下K2 下一首0x01
3瞬K2 下一首0x01
4唯一K2 下一首0x01
5特别的人K2 下一首0x01
0←5循环切换K1 上一首0x02
-切换状态K3 播放/暂停0x0D / 0x0E

4.6 OLED显示驱动

配置说明

OLED.h — Pin Defines
#define SCL_Port  GPIOB
#define SCL_Pin   GPIO_PIN_15
#define SDA_Port  GPIOB
#define SDA_Pin   GPIO_PIN_14

/* 字体大小参数 */
#define OLED_8X16   8    // 8x16像素字体
#define OLED_6X8    6    // 6x8像素字体

void OLED_Init(void);
void OLED_Clear(void);
void OLED_Update(void);
void OLED_CenterPrintf(int16_t Y, char *format, ...);

05程序流程图

STM32主程序执行流程,包含 while(1) 主循环

开始 HAL_Init / SystemClock_Config MX_GPIO_Init / MX_USART2_UART_Init / OLED_Init while (1) 读取 K1/K2/K3 按键状态 全部松开? 是 否 flag_Key=0 K1按下 && !flag_Key? 是 否 发送0x02(上一首) song-- / flag_Key=1 K2按下 && !flag_Key? 是 否 发送0x01(下一首) song++ / flag_Key=1 K3按下 && !flag_Key? 是 否 切换播放/暂停 发送0x0D/0x0E 50ms已到? 是 否 OLED_Clear() 显示歌名 + 播放/暂停状态 OLED_Update() / Time_Now更新 循环回退

06交互式动画演示

模拟 STM32 + DFPlayer Mini MP3 Player 的完整工作流程

Mini MP3模块模拟

MH3002
BUSY
扬声器输出
UART TX:
7E
FF
06
--
00
00
00
EF
K1 上一首
K2 下一首
K3 播放/暂停

操作说明

点击 K1/K2/K3 按钮模拟物理按键,观察 UART 数据帧传输、OLED 屏幕更新和扬声器音频输出状态。点击"自动演示"可自动循环展示完整流程。

OLED 128×64 显示屏

枫
播放
第 1/6 首

系统状态

当前歌曲
枫
播放状态
播放中
USART2 波特率
9600 bps
OLED刷新间隔
50 ms
BUSY引脚
LOW (播放)
flag_Key
0

各组件说明

DFPlayer模块:模拟实物芯片MH3002,16引脚封装,通过UART接收指令

BUSY LED:播放时常亮红色,暂停时熄灭

扬声器:播放时显示声波扩散动画,暂停时停止

UART数据流:逐字节高亮显示8字节指令帧的传输过程

OLED显示屏:实时显示歌曲名称和播放/暂停状态