玩嵌入式的谁没被驱动折磨过:数据手册几十页,例程跑不通,一行行抠寄存器。这期我把 MAX98357(功放喇叭)和 INMP441(数字麦克风)两颗芯片的驱动整个交给 AI agent——资料先喂给它,出一份图文学习报告;需求一句话提进去,它追问、拆任务、写代码;我负责接线、编译、烧录。成品就是本期视频开头那样:长按屏幕播《起风了》高潮段,单击录音再回放。

视频看操作过程,这篇放干货:接线表、I2S 关键配置、踩坑清单,照着能复现。

硬件就两颗芯片:MAX98357 出声,INMP441 拾音

声音链路一进一出,各用一颗专用芯片,都挂在同一种总线上:

  • MAX98357:内置 16bit DAC + D 类功放,把 ESP32 发来的数字采样变成推得动喇叭的电流。它自动识别 I2S 时钟,没有寄存器可配——供电、给时钟、给数据,就出声。
  • INMP441:MEMS 拾音 + 片上 24bit ADC + I2S 从接口,声音进芯片、数字出芯片,ESP32 拿到的直接是采样值。

两颗芯片与 ESP32-S3 之间都走 I2S 三根线:BCLK(位时钟)、WS(帧同步)、SD(串行数据)。全链路统一 16kHz / 16bit——人声能量集中在 4kHz 以下,采样率取最高频率两倍就够还原。

音频接线总图

接线表如下,引脚是我按避让原则挑的:避开屏幕触摸(17/10/18/21)、USB(19/20)、strapping(0/45/46)和 PSRAM 专用(2637)。

接线表按模块分开列,「接」一列就是对面要接的引脚:

MAX98357(喇叭,I2S1 发送)

模块引脚 接 说明
VIN 3.3V 就近并联 220µF 电解,缓冲功放峰值电流
GND GND 与数字地单点汇接,防交流嗡嗡声
DIN GPIO14 串行数据
BCLK GPIO12 位时钟
LRC GPIO13 帧时钟
SD GPIO15 使能脚:拉高发声,拉低静音
GAIN 悬空 不接线,3.3V 供电下即 9dB 档
OUT± 8Ω 2W 喇叭 功率输出,无极性随便接

INMP441(麦克风,I2S0 接收)

模块引脚 接 说明
VDD 3.3V 就近 100nF,严禁 5V
GND GND 共地
SCK GPIO9 位时钟,ESP32 发出
WS GPIO8 帧时钟,ESP32 发出
SD GPIO11 串行数据,唯一回流的线
L/R GND 左声道选择,不许悬空

器材清单:ESP32-S3 开发板、MAX98357 模块、INMP441 模块、8Ω 2W 喇叭、公对公杜邦线若干。

第一步不是写代码,是让 AI 先学

驱动写错多半是原理没吃透,所以流程第一步是把资料交给 agent 学习。我用的编排技能是自己的 EM skill(安装见文末),调它的 learn 命令,提示词大概这样:

学习 MAX98357 和 INMP441 的驱动原理与接线。资料在 docs 目录:ESP32-S3 数据手册、核心板原理图、两颗芯片的数据手册。产出图文学习报告,作为后续开发参考。

它会检索资料、读数据手册,最后产出一份带图的报告。这份报告后来就是全程的技术底座,两段核心结论贴在这里。

播放方向:音频文件本来就是一个采样数列,ESP32 把数列写进 DMA 环形缓冲,I2S 硬件按节拍把采样送上三根线,MAX98357 接到后依次做数字转电压、电压放大功率,膜片推动空气。CPU 只负责填缓冲,搬运是 DMA 的活。

播放数据流

录音方向正好相反:三根线里 SCK、WS 两根时钟是 ESP32 发出去的,只有 SD 一根回来。INMP441 内部完成声音到数字的转换,ESP32 按同样的节拍收数。

拾音链路

有一个细节值得单独记:INMP441 的 24bit 采样装在 32bit 帧槽的高位。ESP32 按 32bit 槽收到 int32 后移位取值即可;要是按 16bit 槽配置,MSB 直接被截掉,录出来全是错位噪声——这是社区第一大配置错误。

24bit 装进 32bit 帧

报告里同时定稿了两个容易踩雷的时序:上电时先起 I2S 时钟、延时约 250ms 再拉高 SD(防爆音);停止时先拉低 SD 再停时钟(BCLK 运行中撤掉 WS 会输出大直流,有烧喇叭风险)。

看完报告接好线,有不懂的地方直接追问 agent,直到自己想明白再开工。

需求一句话给够,agent 追问四轮再动手

开工用 EM skill 的 new 命令,需求原文就这么一句:

实现麦克风和喇叭驱动。长按屏幕播放《起风了》高潮段,播放中再长按停止;单击屏幕开始录音,再次单击停止并立即回放。

它没有直接开写,先追问了四个问题,都是影响边界的:

  1. 和上一版音频代码什么关系?——从零重写极简版,旧版留作参考。
  2. 重写边界?——只写这两颗芯片的驱动和应用逻辑,屏幕触摸沿用。
  3. 歌曲素材哪来?——从 150 秒完整版里自动剪出高潮三句。
  4. 什么算完成?——编译通过 + 真机四个交互全过;录音 16kHz/16bit/单声道,上限 60 秒。

追问完它给出理解摘要,我确认后进入拆分:五个子系统(喇叭驱动、麦克风驱动、演示应用、歌曲素材、界面复用),每个都有明确参数。接着是硬件对齐环节,agent 把接线表列出来让我核对——这一步拦住过我的错接,值得留着。

全部确认后,需求和拆分落成文档存在工程的 features 目录里。这一设计的实际好处在第二天兑现:我中断了一次开发,新会话里敲一条 rec 命令,agent 把上下文完整恢复到中断点,接着干。

代码只有两段值得看

整个驱动一千多行,真正决定成败的是两段。

第一段,I2S 初始化。标准 Philips 时序、16kHz、16bit、单声道(硬件自动复制左右声道),MAX98357 不需要 MCLK:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* bsp_audio_out.c(节选) */
i2s_chan_config_t chan = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
chan.auto_clear = true; /* disable 时 DMA 自动清零,收尾干净 */
i2s_new_channel(&chan, &s_tx, NULL);

i2s_std_config_t std = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(AUDIO_SAMPLE_RATE),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED, /* MAX98357 不需要 MCLK */
.bclk = AUDIO_BCLK_PIN, /* GPIO12 */
.ws = AUDIO_LRCK_PIN, /* GPIO13 */
.dout = AUDIO_DIN_PIN, /* GPIO14 */
},
};
i2s_channel_init_std_mode(s_tx, &std);
i2s_channel_enable(s_tx);
vTaskDelay(pdMS_TO_TICKS(250)); /* 时钟稳定后再放行功放,防爆音 */
bsp_audio_out_mute(false);

第二段,麦克风收数。按 32bit 槽读进 int32,取高 16 位,算术移位自动保符号:

1
2
3
4
5
6
/* bsp_audio_in.c(节选) */
static int32_t raw[MIC_DMA_FRAMES];
i2s_channel_read(s_rx, raw, sizeof(raw), &bytes, 200);
for (uint32_t i = 0; i < n; i++) {
chunk[i] = (int16_t)(raw[i] >> 16); /* 高 16 位,算术移位保符号 */
}

写代码的活 agent 用多 agent 分工完成:主 agent 拆任务,三个子 agent 分别做各子系统,完成后再互查一遍。我全程没改一行,只负责看懂。

编译烧录,四条交互全过才算完

代码写完回到 VS Code,用 ESP-IDF 插件编译烧录。两个注意点:目标芯片要选对(选错构建直接失败);首次全量编译要等几分钟,之后增量很快。

验收清单就四条,对照着按一遍:

  1. 长按屏幕 → 播放《起风了》高潮段
  2. 播放中再长按 → 停止,回待机
  3. 单击 → 开始录音,界面计时
  4. 录音中再单击 → 停止并立即回放

实测一次通过:喇叭出声、录音回放,串口没有 brownout 和爆音。最后一条 result 命令发给 agent,它更新项目文档、归档这个步骤——工作流闭环。要是哪条不过,把失败现象描述发过去,它按现象排查原因,改完再验。

踩坑速查

下面这些坑学习报告里都有,贴出来省得再踩:

症状 原因 处理
喇叭完全无声 SD 引脚被拉低 SD 拉高才发声,先查使能脚
上电”噗”一声 先开功放后起时钟 先 init I2S,延时 250ms 再放 SD
麦克风读出全 0 L/R 悬空(静默模式) L/R 必须接 GND 或 VDD
录音全是错位噪声 I2S 槽配成 16bit,MSB 被截 按 32bit 槽收,再移位取值
麦克风无响应 误接 5V 烧毁 INMP441 只吃 3.3V,别照抄喇叭接法
反复 brownout 复位 3.3V 顶不住功放峰值电流 退 5V 供电方案 + GAIN 接 GND

资料包

本期资料包在粉丝群公告,里面是:驱动源码(bsp/audio 目录)、学习报告全文、需求与拆分文档(features/S10)、接线表、本文用到的全部提示词,以及一张可直接打印的接线图。

EM skill 的安装方法见站内这篇:EM skill 安装指引。任意一个 AI 编程工具(Claude Code / Codex / ZCode)都能跑这套流程,编排技能是通用的。

桌面搭子系列继续往下走:耳朵嘴巴装上了,下期让它把听到的听懂。