1. 从一个真实的翻车现场说起
去年帮朋友处理一个智能语音交互的小项目,硬件方案用的是 ESP32-S3 开发板,固件基于小智那套开源语音框架。当时手头有两块板子,一块是官方推荐的 ESP32-S3-DevKitC-1,另一块是某宝上淘的第三方 S3 核心板,价格便宜一半,引脚看着也差不多。我心想,都是 ESP32-S3,Flash 和 PSRAM 容量一样,烧同一份固件应该直接跑起来吧?结果烧进去之后串口日志一路报错,音频初始化直接失败,喇叭里只有电流底噪,麦克风采集的数据全是乱码。
这个坑让我意识到一个很多人容易忽略的问题:同一套小智源码,换一块 ESP32 开发板,往往不是“烧进去就能用”,而是需要重新做板级适配。这里的适配不是改几行 Wi-Fi 密码那么简单,它涉及到音频编解码器选型、I2S 引脚映射、电源域控制、PSRAM 分配策略、甚至芯片型号差异带来的外设寄存器区别。
这篇文章就是把这个适配过程完整拆开讲清楚。如果你手里有小智的源码,正准备换板子、打样自己的 PCB,或者单纯好奇“为什么换个开发板就这么麻烦”,那接下来的内容应该能帮你少走不少弯路。我会从板级抽象层的设计逻辑讲起,一直讲到GetAudioCodec这类接口的具体实现,再配合实际的引脚配置、参数计算和排查经验,给出一套可以直接抄作业的适配流程。
2. 为什么“同一套源码”不等于“同一套硬件”
2.1 小智源码的板级抽象层到底抽象了什么
小智这套语音框架在设计上其实已经做了不错的解耦。它把跟硬件强相关的部分收敛到了board这一层,上层应用逻辑通过统一的接口去调用音频、显示、按键这些外设。理论上,你只要实现一套新的 board 定义,上层代码一行都不用动。
但问题就出在“实现一套新的 board 定义”这件事上。很多人以为 board 定义就是填几个引脚号,实际上它至少包含四类信息:
- 引脚映射:I2S 的 BCK、WS、DATA 引脚,I2C 的 SCL、SDA,以及功放使能、麦克风使能等控制脚。
- 器件选型:用的是 ES8311 还是 ES7210,是单麦克风还是麦克风阵列,功放是 NS4150 还是直接走 codec 内部 PA。
- 总线参数:I2S 的采样率、位宽、主从模式,I2C 的地址和速率。
- 资源分配:PSRAM 是否启用、音频缓冲区放在内部 RAM 还是外部 PSRAM、任务栈大小。
这四类信息里,引脚映射和器件选型是最容易出问题的。因为不同开发板厂商在这两块的设计差异非常大,有的板子为了省成本把 codec 换成便宜型号,有的板子把功放使能和某个 GPIO 复用,还有的板子 I2S 引脚跟 SPI Flash 引脚冲突——这些在原理图上看不出来,只有实际跑起来才会暴露。
2.2 开发板之间的差异远比想象中大
我整理了一下手头几块常见 ESP32-S3 开发板的音频相关配置,差异一目了然:
| 开发板型号 | 音频 Codec | I2S 引脚 (BCK/WS/DATA) | 功放使能 | PSRAM |
|---|---|---|---|---|
| ESP32-S3-DevKitC-1 + 官方音频扩展板 | ES8311 | GPIO9/GPIO45/GPIO8 | GPIO46 | 8MB Octal |
| 某第三方 S3 核心板 | ES7210 + ES8311 | GPIO5/GPIO6/GPIO7 | 无独立使能 | 8MB Octal |
| ESP32-S3-BOX | ES8311 | GPIO9/GPIO45/GPIO8 | GPIO46 | 8MB Octal |
| ESP32-S3-Korvo-2 | ES8311 + ES7210 | GPIO9/GPIO45/GPIO8 | GPIO46 | 8MB Octal |
| 自制板 (参考设计) | ES8311 | GPIO15/GPIO16/GPIO17 | GPIO18 | 8MB Quad |
你看,光是 I2S 引脚就有三套不同的组合。更麻烦的是,有些板子的 codec 是 ES7210 做 ADC、ES8311 做 DAC,这是两套独立的 I2C 地址和配置寄存器,而小智源码里默认可能只初始化了一个 codec 实例。如果你不把GetAudioCodec的实现改对,音频通路根本建不起来。
还有一个隐蔽的坑:PSRAM 的 Octal 和 Quad 模式。ESP32-S3 支持两种 PSRAM 接口模式,Octal 模式占用更多引脚但带宽更高。如果你的板子是 Quad PSRAM,而源码里默认按 Octal 配置,启动阶段就会直接挂掉,连串口日志都出不来。这个问题的排查难度很高,因为现象看起来像是“板子坏了”。
2.3 芯片型号相同,外设寄存器也可能不同
有人会问:都是 ESP32-S3,芯片型号一样,寄存器不应该一样吗?大部分情况下是的,但有几个地方需要注意。
第一,芯片版本差异。ESP32-S3 有多个 revision,早期版本和后期版本在某些外设的默认行为上有细微差别,比如 I2S 的时钟分频寄存器默认值。如果你用的板子是早期批次的芯片,而源码是基于后期版本调试的,可能会遇到采样率偏差的问题。
第二,Flash 和 PSRAM 的供电电压。有些板子用 3.3V Flash,有些用 1.8V,这会影响 GPIO 的默认电平状态。如果某个控制引脚在启动时被拉到了错误电平,可能导致 codec 进入异常模式。
第三,晶振频率。大部分 ESP32-S3 板子用 40MHz 晶振,但也有用 26MHz 的。晶振频率直接影响 I2S 的时钟分频计算,如果源码里写死了 40MHz 的分频参数,换到 26MHz 的板子上音频采样率就会偏。
这些问题在原理图上都不显眼,但每一个都足以让音频功能完全失效。所以“换板子要重新适配”不是小题大做,而是硬件差异的客观现实。
3. GetAudioCodec 这个接口到底在做什么
3.1 从函数签名看设计意图
小智源码里GetAudioCodec通常是一个返回AudioCodec*指针的函数,定义在 board 相关的头文件里。它的作用很简单:告诉上层“这块板子用的是什么音频编解码器,怎么初始化它”。
但就是这个简单的接口,实现起来要考虑的事情不少。一个典型的实现大概长这样:
AudioCodec* GetAudioCodec() { static ES8311AudioCodec codec( /* i2c_port */ I2C_NUM_0, /* i2c_scl */ GPIO_NUM_10, /* i2c_sda */ GPIO_NUM_11, /* i2s_port */ I2S_NUM_0, /* i2s_bck */ GPIO_NUM_9, /* i2s_ws */ GPIO_NUM_45, /* i2s_data_out */ GPIO_NUM_8, /* i2s_data_in */ GPIO_NUM_8, /* pa_enable */ GPIO_NUM_46, /* sample_rate */ 16000, /* mclk */ GPIO_NUM_16 ); return &codec; }这里每一个参数都对应硬件上的一个具体连接。你换一块板子,至少要改其中三到五个参数。如果 codec 型号也变了,那整个类都要换。
3.2 为什么不能自动检测
有人可能会想:I2C 不是可以扫描地址吗?为什么不让代码自动识别 codec 型号?
理论上可以,但实际工程中很少这么做。原因有三个:
- I2C 扫描只能拿到地址,拿不到型号。ES8311 的 I2C 地址是 0x18,ES7210 是 0x40,但同一个地址可能对应多个兼容型号,寄存器定义未必完全一致。
- 初始化时序有要求。codec 上电后需要先配置时钟,再配置通路,最后使能输出。如果先扫描再初始化,中间的时间窗口可能导致 codec 进入不确定状态。
- 自动检测会增加启动时间。对于语音交互设备,启动速度直接影响用户体验,多花 100ms 做扫描是不划算的。
所以工程上的做法就是:在编译期确定板子型号,通过宏定义或 board 选择来切换GetAudioCodec的实现。小智源码里通常用CONFIG_BOARD_TYPE_XXX这样的 Kconfig 选项来控制,你新增一块板子,就新增一个 board 目录,在里面实现自己的GetAudioCodec。
3.3 音频通路的完整链路
理解GetAudioCodec只是第一步,你还需要知道整个音频通路是怎么走的。以典型的 ES8311 方案为例:
麦克风 → ES8311 ADC → I2S DATA_IN → ESP32-S3 I2S 外设 → 音频处理任务 音频处理任务 → ESP32-S3 I2S 外设 → I2S DATA_OUT → ES8311 DAC → 功放 → 喇叭这条链路上任何一个环节配置错误,都会导致音频异常。比如:
- I2S 的 BCK 和 WS 接反了,数据会错位,听起来像噪音。
- MCLK 没接或频率不对,codec 内部 PLL 锁不住,采样率会漂。
- 功放使能引脚没拉高,DAC 有输出但喇叭不响。
- I2S 主从模式设错,ESP32-S3 和 codec 都等着对方出时钟,结果谁都不工作。
我在调试第三方板子的时候,就遇到过 MCLK 没接的情况。那块板子为了省引脚,把 MCLK 省掉了,codec 靠 BCK 来恢复时钟。这种设计在特定条件下能用,但对 BCK 的抖动要求很高,稍微有点干扰音频就断断续续。后来我在GetAudioCodec里把 MCLK 参数改成I2S_GPIO_UNUSED,同时调整了 codec 的时钟源配置,才勉强稳定下来。
4. 换板子适配的完整实操流程
4.1 第一步:拿到原理图,确认三组关键信息
适配新板子的第一件事不是改代码,而是看原理图。你需要从原理图上确认三组信息:
第一组:音频 codec 型号和 I2C 地址。找到 codec 芯片的型号,比如 ES8311、ES7210、ES8388、WM8978 等。然后确认它的 I2C 地址,通常由 ADDR 引脚决定,接地是一个地址,接 VCC 是另一个地址。这个地址要跟代码里的配置一致。
第二组:I2S 引脚映射。找到 codec 的 BCK、WS、DATA_OUT、DATA_IN、MCLK 分别接到 ESP32-S3 的哪些 GPIO。注意有些板子 DATA_OUT 和 DATA_IN 是分开的引脚,有些是复用同一个引脚(比如 ES8311 支持单线模式)。
第三组:控制引脚。包括功放使能(PA_EN)、codec 复位(RESET)、麦克风使能等。这些引脚的电平逻辑也要确认,是高电平有效还是低电平有效。
如果拿不到原理图,可以用万用表蜂鸣档自己测。把 codec 芯片的引脚和 ESP32-S3 的 GPIO 一一对应测出来,虽然麻烦但最可靠。我一般会画一张对照表,像这样:
| Codec 引脚 | ESP32-S3 GPIO | 功能 |
|---|---|---|
| Pin 1 (BCK) | GPIO9 | I2S 位时钟 |
| Pin 2 (WS) | GPIO45 | I2S 帧时钟 |
| Pin 3 (DATA_OUT) | GPIO8 | 音频输出 |
| Pin 4 (DATA_IN) | GPIO8 | 音频输入 |
| Pin 5 (MCLK) | GPIO16 | 主时钟 |
| Pin 6 (PA_EN) | GPIO46 | 功放使能 |
| Pin 7 (SCL) | GPIO10 | I2C 时钟 |
| Pin 8 (SDA) | GPIO11 | I2C 数据 |
这张表填完,代码里要改哪些参数就一目了然了。
4.2 第二步:新建 board 目录,实现 GetAudioCodec
小智源码的 board 目录结构通常是这样的:
main/boards/ ├── esp32-s3-devkitc-1/ │ ├── config.h │ ├── board.cc │ └── Kconfig.projbuild ├── esp32-s3-box/ │ ├── config.h │ ├── board.cc │ └── Kconfig.projbuild └── your-custom-board/ ├── config.h ├── board.cc └── Kconfig.projbuild你需要在boards下新建一个目录,然后在board.cc里实现GetAudioCodec。同时修改Kconfig.projbuild,增加你的板子选项,这样在menuconfig里就能选到。
config.h里通常放一些板级宏定义,比如:
#define BOARD_TYPE "your-custom-board" #define AUDIO_SAMPLE_RATE 16000 #define AUDIO_I2S_BCK_GPIO GPIO_NUM_9 #define AUDIO_I2S_WS_GPIO GPIO_NUM_45 #define AUDIO_I2S_DATA_GPIO GPIO_NUM_8 #define AUDIO_MCLK_GPIO GPIO_NUM_16 #define AUDIO_PA_EN_GPIO GPIO_NUM_46 #define AUDIO_I2C_SCL_GPIO GPIO_NUM_10 #define AUDIO_I2C_SDA_GPIO GPIO_NUM_11然后在board.cc里引用这些宏,传给 codec 构造函数。这样做的好处是,如果以后引脚有调整,只改config.h就行,不用动board.cc。
4.3 第三步:处理 codec 型号差异
如果你的板子用的 codec 跟源码默认的不一样,就需要换一个 codec 类。小智源码里通常已经支持了几种常见 codec,比如ES8311AudioCodec、ES7210AudioCodec、ES8388AudioCodec。如果源码里没有你需要的型号,就得自己实现一个。
实现一个新的 codec 类,核心是三个函数:
Initialize():配置 codec 的时钟、通路、增益。SetOutputVolume():设置 DAC 输出音量。SetInputGain():设置 ADC 输入增益。
以 ES8388 为例,初始化流程大概是:
bool ES8388AudioCodec::Initialize() { // 1. 复位 codec WriteReg(0x00, 0x80); vTaskDelay(pdMS_TO_TICKS(10)); WriteReg(0x00, 0x00); // 2. 配置时钟 WriteReg(0x08, 0x00); // 从模式,MCLK 来自 ESP32 WriteReg(0x02, 0x00); // 采样率 16kHz // 3. 配置 DAC 通路 WriteReg(0x2E, 0x1E); // DAC 使能 WriteReg(0x30, 0x00); // 左声道输出 WriteReg(0x31, 0x00); // 右声道输出 // 4. 配置 ADC 通路 WriteReg(0x0A, 0x00); // ADC 使能 WriteReg(0x0B, 0x00); // 输入选择 // 5. 设置默认音量 SetOutputVolume(70); SetInputGain(30); return true; }每个寄存器的具体值需要查 codec 的数据手册。这里最容易出错的是时钟配置,因为不同 codec 的 PLL 参数计算方式不一样。ES8311 的 MCLK 可以是 256 倍采样率,也可以是 384 倍,配置错了采样率就会偏。
4.4 第四步:验证音频通路
代码改完之后,不要急着跑完整应用,先做一个最小验证:只初始化 codec,然后播放一段固定频率的正弦波,用示波器或者耳机听一下。
我通常会在app_main里加一段测试代码:
void app_main() { // 初始化 I2C i2c_config_t i2c_cfg = { .mode = I2C_MODE_MASTER, .sda_io_num = AUDIO_I2C_SDA_GPIO, .scl_io_num = AUDIO_I2C_SCL_GPIO, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = 100000, }; i2c_param_config(I2C_NUM_0, &i2c_cfg); i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0); // 初始化 codec auto codec = GetAudioCodec(); if (!codec->Initialize()) { ESP_LOGE(TAG, "Codec init failed"); return; } // 播放 1kHz 正弦波 int16_t buffer[160]; for (int i = 0; i < 160; i++) { buffer[i] = (int16_t)(sin(2 * M_PI * 1000 * i / 16000) * 10000); } while (1) { codec->Write(buffer, sizeof(buffer)); vTaskDelay(pdMS_TO_TICKS(10)); } }如果耳机里能听到清晰的 1kHz 声音,说明 I2S 输出通路没问题。然后再测麦克风输入,用codec->Read()读数据,看波形是否正常。这一步过了,再跑完整的语音交互应用。
5. 常见问题与排查技巧实录
5.1 音频完全没声音
这是最常见的问题,排查顺序建议从后往前:
- 先确认功放使能引脚。用万用表测 PA_EN 引脚,看初始化后是否拉到了有效电平。有些板子的 PA_EN 是低电平有效,代码里如果按高电平配置,功放永远不工作。
- 再确认 I2S 时钟。用示波器测 BCK 和 WS 引脚,看有没有波形。如果没有,说明 I2S 外设没启动,检查
i2s_driver_install是否成功。 - 然后确认 codec 初始化。读 codec 的某个寄存器,看返回值是否跟写入值一致。如果不一致,说明 I2C 通信有问题,检查 SCL/SDA 引脚和上拉电阻。
- 最后确认数据通路。用逻辑分析仪抓 I2S DATA 引脚,看有没有数据输出。如果有数据但没声音,可能是 codec 的 DAC 通路没配好。
5.2 声音断断续续或有噪音
这个问题通常跟时钟或缓冲区有关:
- MCLK 频率不对。如果 codec 需要 MCLK 但板子上没接,或者 MCLK 频率跟 codec 期望的不一致,PLL 会失锁,音频就会断。解决办法是在
GetAudioCodec里正确配置 MCLK 引脚,或者把 codec 配置成不需要 MCLK 的模式。 - I2S 缓冲区太小。如果音频任务优先级不够高,或者缓冲区设置太小,数据供应不上就会断音。可以尝试增大
dma_desc_num和dma_frame_num。 - 电源干扰。有些板子的功放和 ESP32 共用一路电源,功放工作时电流波动会干扰 ESP32,导致 I2S 时钟抖动。这种情况可以在功放电源脚并一个大电容,或者给 ESP32 单独供电。
5.3 麦克风采集不到数据
麦克风通路的问题排查:
- 确认麦克风偏置电压。驻极体麦克风需要偏置电压,如果板子上没有提供,麦克风就不工作。可以测麦克风正极对地电压,正常应该在 1V 到 2V 之间。
- 确认 ADC 通路配置。ES8311 的 ADC 需要单独使能,有些代码只配了 DAC 没配 ADC,导致只能放音不能录音。
- 确认 I2S 输入模式。ESP32-S3 的 I2S 可以配置成只输出、只输入或双向。如果配置成只输出,
codec->Read()永远读不到数据。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决思路 |
|---|---|---|---|
| 完全没声音 | PA_EN 电平错误 | 万用表测 PA_EN | 改代码中的有效电平 |
| 完全没声音 | I2S 时钟未输出 | 示波器测 BCK/WS | 检查 I2S 驱动安装 |
| 完全没声音 | codec I2C 不通 | 读寄存器对比 | 检查 SCL/SDA 和上拉 |
| 声音断续 | MCLK 缺失或频率错 | 示波器测 MCLK | 配置 MCLK 或改 codec 时钟源 |
| 声音断续 | 缓冲区太小 | 查看日志有无 underrun | 增大 DMA 缓冲区 |
| 有噪音 | BCK/WS 接反 | 对照原理图 | 交换引脚定义 |
| 录音无声 | ADC 未使能 | 读 ADC 相关寄存器 | 补充 ADC 初始化 |
| 录音无声 | I2S 模式错误 | 检查 I2S 配置 | 改为双向或输入模式 |
| 启动失败 | PSRAM 模式不匹配 | 看启动日志 | 改 Kconfig 中 PSRAM 模式 |
| 采样率偏 | 晶振频率不匹配 | 测实际采样率 | 调整 I2S 分频参数 |
5.5 几个容易忽略的细节
第一个细节:GPIO 复用。ESP32-S3 的 GPIO 很多都有复用功能,有些引脚在启动时会被内部电路拉高或拉低。如果你选的 I2S 引脚恰好是启动时必须为低电平的 strapping 引脚,可能会导致启动失败。选引脚的时候尽量避开 GPIO0、GPIO45、GPIO46 这些 strapping 引脚。
第二个细节:I2C 上拉电阻。很多开发板已经在 SCL/SDA 上焊了 4.7k 上拉电阻,但有些板子没有。如果 I2C 通信不稳定,先检查上拉电阻。没有的话,在代码里启用内部上拉也能凑合用,但外部上拉更可靠。
第三个细节:codec 的电源时序。有些 codec 要求先上电再给 MCLK,有些要求先给 MCLK 再上电。如果时序不对,codec 可能进入异常状态。这种情况可以在初始化代码里加延时,或者用 GPIO 控制 codec 的电源。
第四个细节:采样率匹配。小智源码默认用 16kHz 采样率,因为语音识别通常用 16kHz。如果你的 codec 配置成了 48kHz,但上层按 16kHz 处理,音频会变调。确认GetAudioCodec里的采样率参数和上层一致。
6. 从适配到量产:一些工程经验
6.1 把板级配置做成可切换的
如果你手头有多块不同的板子,每次换板子都改代码很麻烦。更好的做法是用 Kconfig 做板级选择,在menuconfig里切换。小智源码已经支持这种方式,你只需要在Kconfig.projbuild里增加选项:
config BOARD_TYPE_CUSTOM bool "Custom Board" help Select this for your custom board.然后在board.cc里用#ifdef CONFIG_BOARD_TYPE_CUSTOM来包含对应的实现。这样编译不同板子的固件时,只需要改一个配置项,不用动代码。
6.2 保留一份“最小可用配置”
调试新板子的时候,建议先做一个最小可用配置:只初始化 codec 和 I2S,不跑语音识别,不连 Wi-Fi。这样可以把问题范围缩小到硬件层面。等音频通路验证通过了,再逐步加上网络和语音功能。
我一般会在 board 目录下放一个test子目录,里面放几个独立的测试程序:test_i2s、test_codec、test_mic。每个测试程序只验证一个功能,编译的时候通过宏来选择。这样排查问题的时候可以快速定位到具体环节。
6.3 记录每块板子的适配笔记
适配过的板子多了之后,很容易忘记哪块板子改了什么。建议每适配一块板子,就在 board 目录下写一个README.md,记录:
- 板子型号和购买渠道
- codec 型号和 I2C 地址
- I2S 引脚映射表
- 特殊注意事项(比如某个引脚需要外部上拉)
- 已知问题和解决方法
这份笔记在后期维护的时候价值很大。尤其是当你需要给客户或者同事交接的时候,有笔记和没笔记完全是两个效率。
6.4 关于自制板的一些建议
如果你准备自己打样 PCB,在音频部分有几个设计要点:
- MCLK 尽量接上。虽然有些 codec 可以不用 MCLK,但接了之后时钟更稳定,音频质量更好。
- I2S 引脚尽量选连续的 GPIO。这样布线方便,也减少信号交叉。
- 功放电源和数字电源分开。用磁珠或电感隔离,减少数字噪声对音频的干扰。
- codec 的模拟地和数字地分开。最后在一点连接,避免地环路噪声。
- 预留测试点。在 BCK、WS、DATA、MCLK 上留测试点,调试的时候方便接示波器。
这些经验都是踩坑踩出来的。我第一块自制板就是因为功放和 ESP32 共用电源,音频里一直有“滋滋”声,后来加了 LC 滤波才解决。
6.5 一个真实的适配案例
最后分享一个我最近适配的板子。那是一块基于 ESP32-S3 的语音交互板,codec 用的是 ES8311,但 I2S 引脚跟官方 DevKitC 完全不同:BCK 在 GPIO15,WS 在 GPIO16,DATA 在 GPIO17,MCLK 在 GPIO18,PA_EN 在 GPIO19。
我按照前面的流程,先看原理图确认引脚,然后新建 board 目录,改config.h和board.cc。第一次烧录后,音频输出正常但麦克风没数据。排查发现是 ES8311 的 ADC 通路没使能,源码里的ES8311AudioCodec默认只配了 DAC。我在Initialize()里补了一段 ADC 配置:
// 使能 ADC WriteReg(0x0A, 0x00); // ADC 使能 WriteReg(0x0B, 0x00); // 输入选择 WriteReg(0x0C, 0x00); // ADC 增益重新烧录后麦克风正常。整个过程花了大概两个小时,其中大部分时间在查寄存器和验证。如果一开始就知道要配 ADC,可能半小时就搞定了。所以适配新板子的时候,先确认 codec 的完整初始化流程,不要假设源码里的实现是完整的。
7. 写在最后的一些个人体会
换板子适配这件事,说难不难,说简单也不简单。核心就是三件事:确认硬件连接、实现 board 层接口、验证音频通路。但每一件事都有很多细节,任何一个细节错了都会导致功能异常。
我的经验是,不要怕麻烦,一步一步来。先看原理图,再改代码,然后做最小验证,最后跑完整应用。每一步都确认通过了再进入下一步,这样出问题的时候容易定位。如果一上来就烧完整固件,出了问题面对一堆日志,反而不知道从哪里下手。
另外,多准备几块不同型号的板子练手。适配的板子多了,你会发现很多问题是共通的,比如 I2C 上拉、PA_EN 电平、PSRAM 模式。这些经验积累下来,以后拿到新板子,看一眼原理图就能猜到可能哪里会出问题。
音频调试还需要一些工具:示波器、逻辑分析仪、万用表。示波器用来看时钟和模拟波形,逻辑分析仪用来看 I2C 和 I2S 的数字信号,万用表用来看电平和通断。这些工具不一定要很贵,但有一台会大大加快排查速度。
最后,如果你也在做类似的项目,欢迎交流。音频硬件适配这条路,坑多但也不难走,关键是耐心和细心。