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

news2024/11/15 21:56:51

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

说一下我是如何踩坑的,原本是打算用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相对麻烦就懒得对比测试了。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2090120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java学习第五天

数组 数组适合做一批同类型数据的存储。 静态初始化数组&#xff1a; 注意&#xff1a;数组变量名中存储的是数组在内存中的地址&#xff0c;数组是引用类型。 数组的访问 动态初始化数组&#xff1a; 数组的遍历&#xff1a; 注意左边和右边的区别&#xff0c;一个是改变数组…

日元升值,日股遇冷:出口商的烦恼

最近&#xff0c;日元汇率的走强让不少日本企业感到头疼。日元升值就像一把双刃剑&#xff0c;既能带来好处&#xff0c;也能带来坏处。 为什么日元升值会让日本企业头疼&#xff1f; 出口受阻&#xff1a; 当日元升值时&#xff0c;日本商品在国际市场上的价格就会变得相对较…

Access OpenAI (json) API from R

题意&#xff1a;“从 R 访问 OpenAI (JSON) API” 问题背景&#xff1a; I want to access the OpenAI API with the following curl command from R: “我想从 R 中使用以下 curl 命令访问 OpenAI API&#xff1a;” curl https://api.openai.com/v1/engines/davinci/comp…

centos换源安装升级gcc

使用devtools升级安装的时候&#xff0c;由于此库已经停止更新 了&#xff0c;因此需要切换阿里源 SCLDevtoolset 安装与使用笔记-腾讯云开发者社区-腾讯云 (tencent.com)https://cloud.tencent.com/developer/article/1889181 1 yum 安装 yum install centos-release-scl c…

一文说清JMeter如何用于用于性能测试(超长请耐心看完)

JMeter是纯Java语言开发。开源、免费是其重要的特点。 起初就是用于性能测试&#xff0c;主要Web端的性能。 后来扩展到接口测试、回归测试等功能测试领域。 拥有界面&#xff0c;支持多语种。界面还比较完善&#xff0c;适合初学者掌握和使用。 JMeter无需安装&#xff0c;…

Django中的第一个自动化测试编写

跟着Django官网中的投票应用学习&#xff0c;其中有官方说明的一个bug:如果 Question 是在一天之内发布的&#xff0c;那么这个Question 应该显示“published_recently”&#xff0c;返回值为True &#xff0c;然而现在如果问题发布时间为30天之后(未来时间)&#xff0c;也会返…

Prometheus+Grafana监控数据可视化

上一篇文章讲了prometheus的简单使用&#xff0c;这一篇就先跳过中间略显枯燥的内容&#xff0c;来到监控数据可视化。 一方面&#xff0c;可视化的界面看着更带劲&#xff0c;另一方面&#xff0c;也更方便我们直观的查看监控数据&#xff0c;方便后面的学习。 Grafana安装与…

如何使用 TortoiseGit(小乌龟)进行项目源代码的检出、添加与提交、代码推送与拉取

&#x1f600;前言 本文详细介绍如何使用 TortoiseGit&#xff08;小乌龟&#xff09;进行项目源代码的检出、文件的添加与提交、代码的推送与拉取&#xff0c; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#…

准备并执行库存盘点

库存盘点 企业需要定期盘点其库存的原因有很多。 许多国家的法律要求公司对其物料库存进行库存盘点。库存盘点会检查公司财务报表中所显示的流动资产的物料库存。 由于内部原因&#xff0c;建立正确可用的库存数量是非常重要的。这也是“物料需求计划”的目标&#xff0c;例…

单位普通职工去世了,该单位工会领导参加她的追悼会是这样致辞的?

单位普通职工去世了&#xff0c;该单位工会领导参加她的追悼会是这样致辞的&#xff1f; 这是一篇单位工会领导参加本单位一位普通职工追悼会的致词 &#xff08;范文点评&#xff09; 各位来宾、各位亲朋好友&#xff1a; 今天&#xff0c;我们怀着十分沉痛的心情悼念襄阳农…

【高校科研前沿】加州理工学院Brendan Byrne等人在Nature 正刊发文:2023年加拿大野火的碳排放

论文名称&#xff1a;Carbon emissions from the 2023 Canadian wildfires&#xff08;2023年加拿大野火的碳排放&#xff09; 第一作者及单位&#xff1a;Brendan Byrne&#xff08;碳循环科学家|加州理工学院&#xff09; 通讯作者及单位&#xff1a;Brendan Byrne&#xf…

【C++ Primer Plus习题】7.4

问题: 解答: #include <iostream> using namespace std;long double probability(double num1, double num2, double picks) {long double result 1.0;for (int i num1;picks>0; i--,picks--){result result * (picks / i);}result * 1 / num2;return result; }in…

Shader笔记:光照与阴影1

引&#xff1a;旋转动画&#xff08;三角函数&#xff09; float3 rotationY(float3 vertex){float c cos(_Time.y*_Speed);float s sin(_Time.y*_Speed);float3x3 m {c,0,s,0,1,0,-s,0,c};return mul(m,vertex); } v2f vert (a2v v) {v2f o;o.pos UnityObjectToClipPos(r…

【GPT】Coze使用开放平台接口-【6】Dify 也来一遍

前面讲了 coze 的相关用法&#xff0c;这边想着用 Dify 也来一遍&#xff0c;刚开始的时候接触的是 Dify&#xff0c;后面才是 coze。Dify 和 coze 的侧重点不同&#xff0c;我个人是更倾向用 Dify 构建工作流就可以了&#xff0c;coze 还是相对全能。 本节用 Dify 也会创建插…

k8s的组件以及安装

目录 概念 k8s的使用场景 k8s的特点 核心组件 master主组件 1.kube-apiserver 2.etcd 3.kube-controller-manager 控制器 4.kube-scheduler node从节点组件 1.kubelet 2.kube-proxy 3.docker 总结 k8s的核心概念 安装k8s 架构 安装步骤 实验&#xff1a;创…

快递盒检测检测系统源码分享 # [一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]

快递盒检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

使用pytorch深度学习框架搭建神经网络

简介 现在主流有两个框架pytorch和TensorFlow,本文主要介绍pytorch PyTorch&#xff1a;由 Facebook 的人工智能研究小组开发和维护。PyTorch 以其动态计算图&#xff08;Dynamic Computational Graph&#xff09;和易用性著称&#xff0c;非常适合研究人员和开发者进行实验和…

智谱发布新一代基座模型

自 2023 年 3 月发布第一代语言基座模型 ChatGLM 以来&#xff0c;我们经过对基座模型的深入探索&#xff0c;在模型性能和模态上取得了显著的提升。 基于此&#xff0c;在KDD 2024大会上&#xff0c;我们发布了新一代基座模型&#xff0c;包括语言模型GLM-4-Plus、文生图模型…

数据仓库系列15:数据集成的常见挑战有哪些,如何应对?

在大数据时代&#xff0c;数据集成已成为数据仓库建设中不可或缺的一部分。无论是来自多个数据库、外部数据源&#xff0c;还是实时数据流的整合&#xff0c;数据集成都面临着诸多挑战。那么&#xff0c;这些挑战具体是什么&#xff0c;我们又该如何应对呢&#xff1f;本文将通…

Elasticsearch(一):单节点安装并开启ssl

单节点安装并开启ssl 1、概述2、安装2.1、前期准备2.2、下载和解压Elasticsearch2.3、创建用户和配置权限2.4、生成证书2.4.1、创建CA&#xff08;证书颁发机构&#xff09;2.4.2、与CA签署证书&#xff08;生成节点证书&#xff09; 2.5、 为Elasticsearch配置 证书2.6、启动 …