少走弯路,ESP32 读取Micro SD(TF)播放mp3的坑路历程。

这个坑采的非常冤枉和巨大,非常大的冤枉路,只能一声叹息

说一下我是如何踩坑的,原本是打算用esp32 读取SD卡播放mp3,在esp32 读取自己打的SD卡已经踩了无数坑了,详情见:

少走弯路,ESP32 使用Micro SD(TF)的经验说明-CSDN博客

在好不容易解决了SD卡用1线sdmmc读取的问题,遇到了惊天大雷,esp32 s3 只支持ble 5.0 ,不支持a2dp,那就意味着esp32 s3不能用做蓝牙音频播放的接受端 ,除非哪天升级到ble 5.2的版本才有le audio.

没法,只能回到esp32 ,sdmmc的方式读取走不通,那就用SPI的方式吧。

之前在esp32 通过spi读取数据的代码如下:

    SPIClass spi = SPIClass(HSPI);
  spi.begin(18 /* SCK */, 19 /* MISO */, 23 /* MOSI */, 5 /* SS */);
  if (!SD.begin(5 /* SS */, spi, 120000000,"/cdcard")) {
    Serial.println("Card Mount Failed");
    return;
  }

这段代码是可以正常工作的,访问到TF卡对象,后面也能对文件和目录进行各种访问,结果坑就在这里啊。

 于是,就在这段代码基础上引入了ESP8266Audio 进行音乐播放,这是个相对灵活的库,以前验证使用过。

      if (file->open("/mp3/王杰 - 一场游戏一场梦 (2000版).mp3"))
      {
        // Serial.println(file->getPos());
        // file->seek(100,100);
        // Serial.println(file->getPos());
        Serial.printf_P(PSTR("Playing '%s' from SD card...\n"), mp3file.name());
        id3 = new AudioFileSourceID3(file);
    id3->RegisterMetadataCB(MDCallback, (void*)"ID3TAG");


        mp3->begin(file, out);
      }
      else
      {
        Serial.printf_P(PSTR("Error opening '%s'\n"), mp3file.name());
      }
if (mp3->isRunning()) {
    if (!mp3->loop()) mp3->stop();
  } else {
    Serial.println("MP3 done");
    delay(1000);
  }

以上代码不全,思路是:

1,创建输出到I2S的AudioOutputI2S 对象;

2,AudioFileSourceSD 对象读取SD卡mp3文件;

3,AudioGeneratorMP3 负责解码,并输出到I2S;

4,AudioFileSourceID3 提供辅助信息,可以不用;

5,在loop代码中加上循环;

但是事实上,这代码只要烧录进单片机都会在setup执行完成后,一涉及到loop单片机重启。

各种跟踪后发觉问题出现在:

uint32_t AudioFileSourceID3::getPos()
{
  return src->getPos();
}
uint32_t AudioFileSourceSD::getPos()
{
  if (!f) return 0;
  return f.position();
}

这个

f.position();

只要调用就发生了异常,由于条件限制无法继续定位,怀疑是esp8266audio这个库有问题,换库,于是换成了audioI2S,其实这个库的操作更简单。

官方给出来的示例代码:

#include "Arduino.h"
#include "Audio.h"
#include "SPI.h"

// Digital I/O used
#define SD_CS          5
#define SPI_MOSI      23
#define SPI_MISO      19
#define SPI_SCK       18
#define I2S_DOUT      25
#define I2S_BCLK      27
#define I2S_LRC       26

Audio audio;

void setup() {
    pinMode(SD_CS, OUTPUT);      digitalWrite(SD_CS, HIGH);
    SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
    SPI.setFrequency(1000000);
    Serial.begin(115200);
    SD.begin(SD_CS);

    audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
    audio.setVolume(12); // 0...21

//    audio.connecttoFS(SD, "test.mp3");
    audio.connecttoFS(SD, "良い一日私の友達.mp3");
}

void loop()
{
    audio.loop();
}

// optional
void audio_info(const char *info){
    Serial.print("info        "); Serial.println(info);
}
void audio_id3data(const char *info){  //id3 metadata
    Serial.print("id3data     ");Serial.println(info);
}
void audio_eof_mp3(const char *info){  //end of file
    Serial.print("eof_mp3     ");Serial.println(info);
}

由于SD的操作已经完成,所以只取了部分代码:

    audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
    audio.setVolume(12); // 0...21

//    audio.connecttoFS(SD, "test.mp3");
    audio.connecttoFS(SD, "良い一日私の友達.mp3");

结果还是一样,只有涉及到loop,就会重启

检查官方示例发觉上面有一段注释:

/*

⒈ install SdFat V2 from https://github.com/greiman/SdFat

⒉ activate "SDFATFS_USED" in Audio.h

⒊ activate "#define USE_UTF8_LONG_NAMES 1" in SdFatConfig.h

*/

完成1,2后,重新把代码从原本的

    SPIClass spi = SPIClass(HSPI);
  spi.begin(18 /* SCK */, 19 /* MISO */, 23 /* MOSI */, 5 /* SS */);
  if (!SD.begin(5 /* SS */, spi, 120000000,"/cdcard")) {
    Serial.println("Card Mount Failed");
    return;
  }

换成了:

    pinMode(SD_CS, OUTPUT);      digitalWrite(SD_CS, HIGH);
    SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
    SPI.setFrequency(1000000);
    Serial.begin(115200);
    SD.begin(SD_CS);

结果,居然可以了。

esp32 读取sd卡里面的mp3,然后通过i2s功放板接到喇叭正常播放。

这是sdFat库的功劳?

不服气啊,各种找原因,调整各种参数,后来发觉去掉sdfat库还是可以正常工作,问题出在spi上。

又挨个对比spi的参数,直到最后发觉

    SPIClass spi = SPIClass(HSPI);
  spi.begin(18 /* SCK */, 19 /* MISO */, 23 /* MOSI */, 5 /* SS */);
  if (!SD.begin(5 /* SS */, spi, 120000000,"/cdcard")) {
    Serial.println("Card Mount Failed");
    return;
  }

    pinMode(SD_CS, OUTPUT);      digitalWrite(SD_CS, HIGH);
    SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
    SPI.setFrequency(1000000);

前者是SPIClass,后者也是SPIClass,但是前者指向了esp32中的spi,后者指向了esp8266的spi,就是这个不同。

只要使用了esp32中的spi,则单片机一定会在loop中重启,

于是在用esp8266的spi下,从audioi2s换回esp8266audio,一样能够正常播放了。

因此从头到尾问题都出现在spi上,但是不管那个SPI,对于SD的一般访问读写,都是正常的,这TMD的谁能想到啊!

接着再往后走,最后发现以下通过audioi2s代码可以读取SD卡上的mp3进行播放。

#include <Arduino.h>
#include <SD.h>
#include "Audio.h"

// put function declarations here:
Audio ad;

void setup() {
  // put your setup code here, to run once:
  SD.begin();
  ad.setPinout(32, 33, 21);
  ad.setVolume(21); // 0...21
  ad.connecttoFS(SD,"/马良 - 往后余生.mp3");

}

void loop() {
  ad.loop();
  // put your main code here, to run repeatedly:
}

这是最简洁的代码,基本啥SPI配置在代码中都不需要,只有I2S的配置,这够简单吧?

上面这个能够正常运行的前提是按照esp32的默认访问SD卡进行SPI线路的连接

#define SD_CS          5
#define SPI_MOSI      23
#define SPI_MISO      19
#define SPI_SCK       18
#define I2S_DOUT      21
#define I2S_BCLK      32
#define I2S_LRC       33

你们看,从sd库本身的示例,到audioi2s的示例,到各种网络上关于sdmmc的示例都是在一大抄,如果没有这些错误的引导,也就不会产生错误的各种情况。

也就是说:

1,按照标准进行接线;

2,引入audioi2s库;

3,使用上面最简洁的代码,破开SPI各种配置和不同引用的影响;

4,运行,烧录单片机;成功正常播放mp3音乐。

当然这里面有一些关于VSPI,HSPI的配置在里面,只是应该按照最简洁的方式来啊!

兜兜转转,我的这个坑够大的啊!

写到这里的时候,又去折腾了一下:

在最简代码情况下,进入sd.begin方法,这时方法中的spiclass如果你按照ctrl跳转到esp8266上,而如果按照include 进行跳转又都在32的include上。

搞不懂!

反正是个坑,不知道是乐鑫的框架层面还是platformio层面;由于arduino ide引入audioi2s相对麻烦就懒得对比测试了。

考虑是否给乐鑫汇报一下。

<think> 嗯,用户这次想了解ESP32默认的SD卡引脚信息。从对话历史看,用户之前查询过ESP32-C3的SPI配置,但这次明确需要的是通用ESP32系列的SD卡引脚,可能需要区分不同接口模式。 用户可能是嵌入式开发者或物联网爱好者,正在调试SD卡功能。ta没有指定具体芯片型号(如ESP32-WROOM),也没有说明使用SDMMC还是SPI接口,这意味着需要提供最通用的配置方案。考虑到SD卡操作对硬件连接敏感,引脚信息的准确性至关重要。 准备重点说明三点核心内容:首先强调ESP32没有绝对"默认"引脚,必须分情况讨论;其次详细列出SDMMC和SPI两种模式的引脚映射,其中SDMMC模式需要4根数据线加CLK/CMD,而SPI模式最少只需3根线;最后提醒硬件设计注意事项,特别是上拉电阻的必要性。 注意到用户提供的参考链接指向ESP-IDF仓库,因此会在回复中强调查阅官方技术文档的重要性。虽然引用[^1]的内容不直接相关,但可以借此引导用户查看ESP-IDF示例代码里的sd_card目录,那里有现成的配置参考。 需要特别注意技术细节的严谨性:比如SDMMC模式下GPIO12需要外部上拉才能识别VDD电压,这个很多开发者都踩过。还有SPI模式中CS引脚的灵活性容易被忽视,要在回复里单独说明。 最后考虑补充两个实用建议:一是优先推荐SDMMC模式以获得更高速度,二是提醒GPIO2作为固定TX引脚的特殊性。这些细节能帮用户少走弯路。 </think> 在ESP32开发中,**没有绝对的“默认SD卡引脚”**,具体引脚配置取决于使用的接口模式和硬件设计。以下是两种常用模式的典型引脚映射(基于ESP-IDF框架): ### 📌 一、SDMMC高速模式(推荐) 这种模式使用硬件SDMMC控制器,支持4位数据总线,速度更快: ``` SD_CLK → GPIO14 SD_CMD → GPIO15 SD_DATA0 → GPIO2 SD_DATA1 → GPIO4 SD_DATA2 → GPIO12 SD_DATA3 → GPIO13 ``` > ⚠️ **注意**: > 1. GPIO2通常固定用于串口TX,若冲突需重新设计 > 2. GPIO12需外部上拉电阻识别VDD电压(硬件设计关键)[^1] ### 📟 二、SPI兼容模式 适用于SPI接口SD卡模块(低速但引脚灵活): ``` MOSI → GPIO23 MISO → GPIO19 SCK → GPIO18 CS → 任意空闲GPIO(常用GPIO5/GPIO21) ``` ### 🔧 配置要点 1. **硬件设计**: - SDMMC模式要求走线等长,建议优先使用ESP32-WROVER系列开发板预置接口 - SPI模式下CS引脚需软件控制,参考ESP-IDF示例代码`sdspi`目录[^1] 2. **软件配置**: ```c // SDMMC模式初始化示例 sdmmc_host_t host = SDMMC_HOST_DEFAULT(); sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT(); slot.width = 4; // 使用4位数据总线 ``` ### 💡 实用建议 1. **优先选择SDMMC模式**:速度可达20MHz(SPI模式通常≤10MHz) 2. **检查硬件手册**:不同ESP32模组(如ESP32-S3)引脚可能不同 3. **调试工具**:使用`idf.py monitor`查看SD卡初始化错误码 > ✅ **官方参考**: > ESP-IDF示例项目 [sd_card](https://github.com/espressif/esp-idf/tree/master/examples/storage/sd_card) 提供完整配置模板,包含引脚自定义方法[^1]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值