拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

苹果震动马达-MP3 单曲循环播放器

苹果震动马达-MP3 单曲循环播放器

MP3 单曲循环播放器 — 最简版说明

STM32F103C8T6 + MP3-TF-16P,固定重复播放一首歌。
对应代码:User/main.c(最简版,无 ESP / OLED / 按键)。


一、硬件清单

物品说明
STM32F103C8T6 蓝板主控,USB 或 ST-Link 供电
MP3-TF-16P 模块带底板版,底部 5 针:VCC / GND / RXD / TXD / BUSY
TF 卡≤32GB,FAT32 格式(64G 默认 exFAT 模块不认)
小喇叭8Ω / 1~3W,接模块"喇叭±"焊盘,不分正负
杜邦线3~4 根(公对母)

二、接线表(只要 3 根线)

MP3-TF-16P 引脚接到 STM32说明
VCC5V(板上 5V 排针)⚠️ 别接 3.3V,供电不足会卡顿/重启
GNDGND必须共地,否则指令全丢
RXDPA9(USART1_TX)交叉:单片机 TX → 模块 RX
TXD(可不接)模块返回数据,本版代码不读
BUSY默认悬空;备用接PB0见下文"USE_BUSY 开关"

电源提醒:蓝板用 USB 线供电时,板上5V针即 USB 直通 5V,直接给模块;若只用 ST-Link(只有 3.3V),需另找一路 5V 给模块,且两路电源GND 连在一起。

三、TF 卡准备

TF卡根目录 └─ 01 ← 文件夹名:两位数字 01~99 └─ 001.mp3 ← 文件名:三位数字 .mp3,不能有其他前缀字符
  • 格式化选FAT32(不是 exFAT / NTFS)。
  • 只放这一首歌:文件夹里只有001.mp3,"全部循环"就是单曲循环。

四、代码说明(main.c)

初始化流程

上电 → 延时 1s(模块稳定) → 0x09, 0x02 指定设备 = TF 卡 → 延时 500ms(等卡挂载,太短会吞命令) → 0x06, 20 音量 20(范围 0~30,改这个数调音量) → 0x08, 0x00 全部循环(主流固件认) → 0x11, 0x01 循环播放(另一批固件认,双保险) → 0x0F, 0x0101 播放 01 文件夹的 001.mp3 → 主循环空转,重复播放由模块自己完成

DFPlayer 命令帧格式(10 字节)

7E FF 06 [CMD] 00 [参数高] [参数低] [校验高] [校验低] EF
  • 波特率9600,USART1 只发不收;
  • 校验和 =0 - (0xFF + 0x06 + CMD + 参数高 + 参数低)的 16 位补码(代码已算好)。

USE_BUSY 开关(备用方案)

#defineUSE_BUSY0/* 默认 0:不接 BUSY,靠模块固件循环 */
现象处理
能自动循环(默认情况)什么都不用改
播完一遍真停了、不循环说明固件不支持循环命令:接BUSY→PB0,把USE_BUSY改1重新编译

USE_BUSY=1 时原理:BUSY 播放时为低、停止时跳高,STM32 检测到变高(消抖 200ms)就重发播放命令,不依赖固件。

五、烧录步骤

  1. Keil 打开工程(MP3目录,标准库 V3.5.0);
  2. 无需增删文件,直接Rebuild;
  3. ST-Link / 串口烧录Objects下的 HEX;
  4. 复位上电,喇叭应开始播放并无限循环。

六、故障排查(按顺序过)

现象排查
完全没声① 共地?② TF 卡 FAT32?③ 文件名严格001.mp3?④ 模块接的是 5V?⑤ 喇叭焊点牢?
有咔哒声/重启模块供电不足,换 5V 直供
没播完就重播BUSY 悬空被误读,确认USE_BUSY 0(本版已默认关)
播完不循环接BUSY→PB0,USE_BUSY改1
找不到文件文件夹/文件命名不严格,或卡是 exFAT
#include"stm32f10x.h"#include"Delay.h"/* * 最简版:固定重复播放一首歌 * ========================= * 接线(只要 3 根线): * MP3-TF-16P VCC -> 5V * MP3-TF-16P GND -> GND(共地) * MP3-TF-16P RXD -> PA9 (USART1_TX) * 喇叭接模块"喇叭±"焊盘 * * TF 卡(FAT32):01 文件夹里放 001.mp3(一首歌) * * 如果烧录后发现播完一遍就停、不自动循环(说明固件不支持循环命令), * 再把 BUSY 接到 PB0,并把下面的 USE_BUSY 改成 1 重新编译烧录。 */#defineUSE_BUSY0/* 0 = 不用 BUSY 引脚(默认);1 = BUSY 接 PB0 后启用 */voidMP3_Init(void);voidMP3_SendCmd(uint8_tcmd,uint16_tparam);voidBusy_Init(void);intmain(void){MP3_Init();#ifUSE_BUSYBusy_Init();#endifDelay_ms(1000);/* 等模块上电稳定 */MP3_SendCmd(0x09,0x02);/* 指定设备:TF 卡 */Delay_ms(500);/* 等卡挂载 */MP3_SendCmd(0x06,20);/* 音量 20(0~30),按需改 */Delay_ms(100);MP3_SendCmd(0x08,0x00);/* 全部循环(主流固件认) */Delay_ms(100);MP3_SendCmd(0x11,0x01);/* 循环播放(另一批固件认,双保险) */Delay_ms(100);MP3_SendCmd(0x0F,0x0101);/* 播放 01/001.mp3,文件夹只有一首 = 单曲循环 */while(1){#ifUSE_BUSY/* BUSY 兜底:模块停止时 BUSY 变高 -> 重新播放 */if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==1){Delay_ms(200);/* 消抖,避开切歌间隙 */if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==1){MP3_SendCmd(0x0F,0x0101);Delay_ms(500);}}#elseDelay_ms(100);/* 空转,一切交给模块自己循环 */#endif}}/* ---------------- USART1 初始化(9600,发命令给 MP3 模块) ---------------- */voidMP3_Init(void){GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;/* PA9 = TX */GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);USART_InitStructure.USART_BaudRate=9600;USART_InitStructure.USART_WordLength=USART_WordLength_8b;USART_InitStructure.USART_StopBits=USART_StopBits_1;USART_InitStructure.USART_Parity=USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode=USART_Mode_Tx;/* 只发不收 */USART_Init(USART1,&USART_InitStructure);USART_Cmd(USART1,ENABLE);}/* ---------------- BUSY 引脚(PB0 输入,播放=低,停止=高) ---------------- */voidBusy_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_Init(GPIOB,&GPIO_InitStructure);}/* ---------------- 发送一条 MP3 命令帧(10 字节) ---------------- */voidMP3_SendCmd(uint8_tcmd,uint16_tparam){uint8_tframe[10];uint16_tsum,checksum;uint8_ti;frame[0]=0x7E;frame[1]=0xFF;frame[2]=0x06;frame[3]=cmd;frame[4]=0x00;frame[5]=(uint8_t)(param>>8);frame[6]=(uint8_t)(param&0xFF);sum=0xFF+0x06+cmd+frame[5]+frame[6];checksum=(uint16_t)(0-sum);/* 16 位补码 */frame[7]=(uint8_t)(checksum>>8);frame[8]=(uint8_t)(checksum&0xFF);frame[9]=0xEF;for(i=0;i<10;i++){USART_SendData(USART1,frame[i]);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);}}
返回列表