ESP32 C3 浅谈I2S DAC 与 TF卡数字音频MP3 FLAC 播放器-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 1589|回复: 9

ESP32 C3 浅谈I2S DAC 与 TF卡数字音频MP3 FLAC 播放器

[复制链接]
发表于 2022-5-25 11:46 | 显示全部楼层 |阅读模式
新来论坛, 各位多指教。


通过ARDUINO+IDF开发的esp32c3的T卡 数字音频播放器测试基本完成了,为了获取论坛成长,把开发过程记录下来,请坛友们多指正:

实际效果在B站https://www.bilibili.com/video/BV1uB4y1R76d?spm_id_from=333.337.search-card.all.click




一  为什么关注esp的音频播放功能:
    不少坛友的esp帖子中,谈到了ESP的音频播放功能,一个重要的因素就是esp的系列iot mcu,提供了i2s接口(spdif输出接口我们先不谈)。这样,如果ESP对数字音源文件做了解码,如果解码过程没有损失,那么通过数字接口i2s输出的就是数字信号,后接一个i2s的音频dac,将数字信号转为模拟信号,输出直接驱动扬声器,或者传递给后级功放。整个过程中我们看到, 只要保证无损的解码过程,esp就能输出无损的数字信号,只要后续的i2s前级和后级放大器足够好,不到10元的esp就可以承担起无损音乐的解码任务! 而且可以充分利用现有的成熟i2s dac模块。这个在DIY HIFI领域一根线动则成百上千的领域,真是一个价廉物美的数字信号源。或者抛开HIFI不谈,单从DIY小物品出发,用I2S音频系统也能做出一些和声音相关的小玩意,让你的小工具能够发出声音,的确比较有意思。
    特别值得一提的,嵌入式领域的软解码方案中,MP3多用的libmad库,或者helix的解码库,因为嵌入式MCU多数没有浮点处理器FPU,故大多采用了整数解码算法。尽管MP3本来就是有损压缩,整数解码还是显得有些遗憾。不过幸运的是,对于无损压缩格式FLAC而言,无需浮点数解码,因此, 大量的高清FLAC音源,ESP就可以直接拿来使用,提供真正的无损信号输出。

二 常见的esp音频解码方案
市面上涉及esp音频的开源方案中,多采用下述三种形态:
     1)  esp + 外挂音频处理模块方案: 这个方案中,  esp只是作为mcu来使用, 音频通过外挂的模块,如dfplayer mini等处理。esp与音频模块间通过串口或者spi等机制通讯,根据用户的输入,触发音频模块执行播放、暂停、定位等动作。这种方案好处是实现相对简单,对于公交报站、定点有限内容语音提醒、固定格式音频播放等特定应用来说很方便。外挂的音频模块往往自带TF存储,可以提供大容量的音乐存储空间。  不过这个方案在略复杂的应用方面存在扩展性不佳的缺点, 例如要显示音乐频谱,或者是控制频率变换实现变音,或者是多种不同协议混杂(例如flac/ape/aac/amr),已经定死功能的外部模块无法与mcu进行大数据量音频sample级别的数据交互,实现上述功能有些困难。
   2) esp 直接音频处理方案: 在这个方案中,esp不是作为简单的mcu来控制外部模块,而是直接参与音频数据的解码,可以得到音频sample级别的数据并加以处理,根据输出的变化,又有两个方案:
         a)esp + 内置DAC超采样输出方案: 这个方案无需外部dac,直接超采样,使用软件实现的delta-sigma dac完成音频输出功能。参见https://github.com/earlephilhower/ESP8266Audio github库中的说明。此种情况下,外部接线(输出)相对简单,8266audio库的作者建议的一种接线方法如下:
                            2N3904 (NPN)
                            +---------+
                            |         |     +-|
                            | E  B  C |    / S|
                            +-|--|--|-+    | P|
                              |  |  +------+ E|
                              |  |         | A|
ESP8266-GND ------------------+  |  +------+ K|
                                 |  |      | E|
ESP8266-I2SOUT (Rx) -----/\/\/\--+  |      \ R|
                                    |       +-|
USB 5V -----------------------------+

         不过需要提醒,上述方法可能存在直流成分对喇叭的伤害,可考虑加入一定的直流阻隔。
         b)显然,上述方案的音质存在明显局限性,HIFI就不用考虑了。因此,对于音质有一定要求的场景下,一般采用esp + 外置I2S DAC的方案了,就是esp解码,i2s方式数字输出到外部声卡。这种方案的灵活性非常好,成本可根据声卡选型控制,也不是特别高,在功能、性能、成本等方面可以获得很好的平衡。建议采用这样的方案。


三  ESP系MCU的选型考虑
     乐鑫家的esp系列, 主流的区分8266和32, 32里头常用的,包括32, 32c3, 32s2, 和32s3,以及后期要推出的32c2.   如果按照市场定位的角度, 老大哥esp8266和 esp32c3的市场定位是接近的,单核, 一般4M的flash,不带PSRAM。其中,c3的sram为400k,比8266的大。至于其他32,提供了大flash和psram,以及双核处理能力,更多的PIN脚接入,市场定位是偏高端一些的。  反应在价格上, 就是8266和c3的开发板都是10元级别的, 而其他32一般是15-35元级别的(好像实际差别不大啊,只对产品量产有影响)     本人有8266, c3, esp32 和 32s3. 其中32是自己买的16M + 8M的模块焊接的底板, S3买的aithinker家的s3-12k, c3买的合宙家的9.9元网红c3开发板。最终选择的是c3来做试验。因为这块板子做的比较精致,虽然是单核,但s3之类另外一个核是ulp的,对于MP3/FLAC这个级别的解码,C3 160MHZ CPU加带cache的指令、数据处理,C3应对起来绰绰有余。加之arduino写代码多核处理稍显麻烦,为了充分利用9.9元,就拿他上手了。 8266和c3比略有缺陷,根据8266audio库作者提示:播放flac时,flac音源的压缩level应当控制在2以下,否则8266可能播放时内存不足。

四  DIY常用I2S声卡选型
    作为轻度diy玩家和手残党,本人没有焊接i2s声卡的能力,就在淘宝上购买常见成品i2s声卡吧,那到底选那款呢? 根据datasheet,找下如下信息:
   
DAC
采样位数
输入电压
输出
采样频率
动态范围/SNR
后级
淘宝价格
  
MAX9357A
  
Maxim
16/24/32 bit
3.5-5.5
单声道
8-96Khz
105/ PSRR77@1KHZ
输出3W,直推4欧3w小喇叭
12.5
  
CJMCU UDA1334A
  
NXP 2020已停产
<= 24bits
3.5-5.5
立体声
16-100Khz
/100db@44.1Khz
后级功放阻抗3 ;或耳机32欧
14
  
PCM5102A
  
TI
16/24/32
立体声
8-384Khz
112/112DB
后级功放阻抗>1  KΩ;或推耳机
20-50


    我们一般使用的flac音源,存储的是44.1khz, 16bit的无损压缩过的wav数据。就是说,三款声卡都可以比较完美的支持。稍微较真一点,高品质24bit 母带级flac音源,也在上述处理范围之内。测试时带耳机麻烦,挂个小喇叭比较方便,虽然是单声道。 最后选择的是9357A和1334A,一个测试,一个用来真的听一下.  其实很馋5102a的参数,但是这伙计淘宝价格差异太大,不知真假(单dac购买的价格是7元左右),所以没敢下手。最后发现,1334推一般的耳机,不做增益调试,实在是声音太大了(用了45欧的原道耳塞和SONY的MDR 7506都试过了)。而且1334不加静音控制的话,启动时会有一点电流杂音,启动后效果还可以。 建议各位diyer买9357A玩玩就可以。上述声卡都自带锁相环,无需独立的MCLK信号,一般接线只需LRC/MSEL,din,BCLK三线,加电源输入即可。
    能力强的小伙伴们就可随意发挥,自己买个1,2元的I2S DAC芯片焊个板子就好了。


后面继续。。。


发表于 2022-5-25 13:51 | 显示全部楼层
先赞了,谢谢分享与解读
 楼主| 发表于 2022-5-26 09:22 | 显示全部楼层
五  硬件环境简单一览

      附图是测试用的硬件环境,可以看到各个元件的大小。楼主因为是手残党不会画板子和焊接SOP封装原件,只能采用最原始的面包板+杜邦线方式连接(多亏I2S是数字信号输出啊,杜邦线不会对信号有任何劣化)。图中98357是测试时使用的,直接推小喇叭。试听时换上1334加mdr 7506耳机。 240x320的TFT IL9341显示是之前把网红项目小电视移植到C3上用的。 T卡模块拍这个记录的时候还没买,后面买的。第一阶段实际测试的器件其实只涉及esp32c3, max98357,和小喇叭。 第二阶段涉及到完整的T卡播放,和播放器显示,就需要TF卡和TFT屏了。
       论坛实物照片.jpg

      成本简单罗列:
      

  
硬件
  
ESP32C3
98357
喇叭
QVGA TFT
TF卡槽
其他
价格
9.9
12.5
4.5
30
3.8
T卡耳机杜邦线面包板
说明
要焊排线
前期不需要
前期不需要
这个是以前就买的


   注意一点是ESP32C3这个开发板送了排针,但没焊接。  焊接排针这个正好在楼主极限之类,于是拿了黄花烙铁、锡丝、助焊膏焊好了,比想象中容易。98357买不焊排针的省2元,楼主买了这个。 万事具备,下面就要开始敲代码了。


 楼主| 发表于 2022-5-27 09:54 | 显示全部楼层
六  开发环境搭建

    先预告下后面的源代码例子:
    示例一:   C3使用最简单的PROGMEM(即数组)中MP3音乐片段的播放例子与代码。(FLAC也一样,C3 4M FLASH和400K内存支持多种音频协议,性价比完美)
    示例二:   C3通过WIFI实现HTTP流式MP3播放 ,家有OPENWRT或者NAS的音乐爱好者福音(直接联网能力是IOT MCU和传统MCU如STM相比巨大的突破 )
    示例三:   C3通过TFT显示播放歌曲的名称(也可扩展到显示LRC歌词)
    示例四:   C3通过TF卡直接播放MP3和FLAC(没有NAS也没关系,自己做一个播放器吧)

    软件开发阶段,首先要搭建esp32c3的开发环境。帖子给出的代码理论上arduino IDE开发环境也能使用,但楼主选择了vscode + platformio插件形式做前期开发。platformio从今年初的版本好像就直接支持expressif平台对arduino框架的支持了,比较好用。 安装这个环境唯一的缺陷就是: 需要搭建不可言说的事务。这里特别提醒,搭建的事物必须是全局事务,因为platformio在初次创建工程或者直接下载expressif平台时,使用了http, git和python。这三种不同的工具/协议并不尊从同一个代理设置。即使在vscode环境中设置好了http代理, GIT和python也不会使用,这将导致国内网络下创建(下载工具、库源码等)失败。  其中,python建议自行更新pip源设置到国内ali或者清华源,下载会更快,通过事物获取反而慢。
       选择vscode而非arduino本身的ide,除了楼主没用过arduino的ide外,还有一个重要的原因就是jtag调试支持。 熟悉ios或者android嵌入式开发的坛友应当都很清楚,在这两个平台上开发,就像在pc上开发一样,可以设置断点、单步调试!这个对于应用层开发来说帮助太大了。在单片机领域, 例如STM,调试往往需要通过外接的jlink等硬件来完成,而神奇的esp32c3,和esp32s3一样,内置了jtag功能! 合宙的这块9,9网红版,和它推出的AIR 105之类不一样,没有阉割或者隐藏jtag功能! 这样只需引出2根数据线+1跟GND线,外接一个USB小接口,就可以以近乎0的成本,在platformio里头执行jtag调试啦! 断点、单步、查看变量值、查看内存、功能非常丰富。这比stm动辄几十,贵则上百的jlink之类硬件实在是要实惠的多。
     安装好vscode和platform后,
     1)进入platformio主页,选择New Project(新建工程)
       1.jpg

    2)弹出的对话框中, 输入你想做的程序工程名称,这里填的是c3mp3test,在board(开发板)中选择espressif ESP32-C3-DevKitM-1,FrameWork(开发框架)选择arduino framework,下面的工程存放位置选择缺省即可,也可自行指定位置;

    3)  会出现一个wait的等待界面,如果是初次使用,platformio会下载c3 esp arudino需要的所有开发资源,例如工具链、esp arduino代码等等。没有不可言说事物的话,这一步可能失败。如果等的不耐烦,可以打开资源监视器,看看后台有没有下载任务, 没有的话, 就得自己想办法了。。。

    4)  一切顺利,或者非初次使用,会出现新建的工程,这样就可以开始正式编码了:下次正式进入实例和代码一。


2.jpg
3.jpg
发表于 2022-5-27 21:23 | 显示全部楼层
牛B卡拉斯,期待更新
 楼主| 发表于 2022-6-6 10:25 | 显示全部楼层
本帖最后由 cqcqwind 于 2022-6-6 10:56 编辑

七:Demo1   C3(8266也适用)使用最简单的PROGMEM(即数组)中MP3音乐片段的播放例子与代码

       这一个例子,我们使用开源的8266audio库,这个库提供了MP3, FLAC, AAC等多种音频解码,支持FLASH文件系统, FLASH数组,内存, HTTP等各种数据源,使用起来极其简单方便。 例子通过最简单的数组文件播放音乐, 就是把MP3片段做成一个数组,直接编译到项目里头去。(整个项目已经打包好放在附件里,不想一步步来的坛友可以直接用platformio下载编译)。      
      按照前述步骤,新建工程,命名c3MusicDemo1后, 在左边的资源管理器中会看到最下面有一个叫做platformio.ini的文件,


      打开这个文件,我们要做如下修改:
  1. [env:esp32-c3-devkitm-1]
  2. platform = espressif32
  3. board = esp32-c3-devkitm-1
  4. framework = arduino
  5. ; 设置串口监控速率
  6. monitor_speed = 115200
  7. ; 合宙C3采用的2线SPI FLASH,所以要设置成dio模式,否则烧写后无法正常运行
  8. board_build.flash_mode = dio
  9. ; 使用8266库,platformio会自动下载更新这个库
  10. lib_deps = earlephilhower/ESP8266Audio@^1.9.6
复制代码


    main.cpp的核心代码在这里:
  1. #include <Arduino.h>
  2. #ifdef ESP32
  3. #include <WiFi.h>
  4. #include "SPIFFS.h"
  5. #else
  6. #include <ESP8266WiFi.h>
  7. #endif

  8. #include "AudioFileSourceSPIFFS.h"
  9. #include "AudioFileSourcePROGMEM.h"
  10. #include "AudioFileSourceID3.h"
  11. #include "AudioGeneratorMP3.h"
  12. #include "AudioOutputI2S.h"

  13. //音乐数组文件在这里
  14. #include "show.h"

  15. AudioGeneratorMP3       *mp3;
  16. AudioFileSourcePROGMEM  *file;
  17. AudioOutputI2S          *out;
  18. AudioFileSourceID3      *id3;

  19. void setup()
  20. {
  21.   //关闭wifi,可以减少干扰和电源扰动
  22.   WiFi.mode(WIFI_OFF);

  23.   Serial.begin(115200);
  24.   Serial.printf("Sample MP3 playback begins...\n");

  25.   //音频库内部日志输出
  26.   audioLogger = &Serial;
  27.   // PLEASE_NAME_ME是音乐数组的名称,放在show.h里头
  28.   file = new AudioFileSourcePROGMEM(PLEASE_NAME_ME, sizeof(PLEASE_NAME_ME));
  29.   // MP3 id3头过滤器
  30.   id3 = new AudioFileSourceID3(file);
  31.   //生成I2S驱动
  32.   out = new AudioOutputI2S();
  33.   //设置I2S PIN脚
  34. #if defined(ESP32)
  35.   //如果按照本C3示例接线图接线,需要设置i2s的pin脚,因原8266audio库是按照esp32的PIN脚接线的
  36.   out->SetPinout(1, 0, 12);
  37. #else
  38.   //如果是8266,而且按照标准建议引脚接了i2s声卡,就不用额外设置pin脚
  39. #endif
  40.   //设置MP3解码器,它的输入是ID3过滤器,输出是I2S
  41.   mp3 = new AudioGeneratorMP3();
  42.   mp3->begin(id3, out);
  43. }

  44. void loop()
  45. {
  46.   if (mp3)
  47.   {
  48.     if (mp3->isRunning())
  49.     {
  50.       if (!mp3->loop())
  51.         mp3->stop();
  52.     }
  53.     else
  54.     {
  55.       delete mp3;
  56.       mp3 = NULL;
  57.       Serial.printf("MP3 done\n");
  58.       delay(1000);
  59.     }
  60.   }
  61. }
复制代码
音乐数组文件show.h也请拷贝到工程源代码的src目录下,和main.cpp同级。 点击编译,成功后烧写到C3开发板,如果硬件连接没有问题,上述寥寥数十行简单代码,就可以听到美妙的音乐啦。
          因为8266audio这个库支持8266, esp32等比较全面的esp系,所以有其他板子也完全能够支持。以8266为例,只要按照8266audio文档里头的pin脚接好i2s声卡,在platformio.ini里头加入
  1. [env:nodemcuv2]
  2. platform = espressif8266
  3. board = nodemcuv2
  4. framework = arduino
  5. lib_deps = earlephilhower/ESP8266Audio@^1.9.6
  6. upload_speed = 3000000
  7. monitor_speed = 115200
复制代码
      然后注意点击底部的开发板选择,设置成nodemcuv2, 即可为8266编译了,引脚对应的情况下,代码无需修改。     
         附件是代码,因为8266audio库太大超过附件大小1M要求,所以代码里头没有包含,编译时platformio应当会自动去下载。

c3MusicDemo1.rar (313.99 KB, 下载次数: 10)

发表于 2022-7-24 17:33 | 显示全部楼层
很有意思的研究,谢谢分享,希望能继续发布后续
发表于 2022-7-24 17:36 | 显示全部楼层
LZ,我怎么找不到MAX9357A的资料或者模块呢?
发表于 2022-7-24 17:46 | 显示全部楼层
问题找到了:应该是 MAX98357A
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 10:43 , Processed in 0.218809 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表