ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Arduino ESP32 I2S 音频外设完整指南:总线原理、ESP_I2S 库 API 与实战配置

2026/9/13 23:22:21 拓冰建站 浏览量
Arduino ESP32 I2S 音频外设完整指南:总线原理、ESP_I2S 库 API 与实战配置 Arduino ESP32 I2S 音频外设完整指南总线原理、ESP_I2S 库 API 与实战配置【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32导读本文以 arduino-esp32 官方文档 docs/en/api/i2s.rst 为核心系统讲解 ESP32 系列 SoC 上 I2SInter-IC Sound数字音频总线的原理与用法。你将掌握 I²S 总线四类信号线的含义、I2SClassESP_I2S库从初始化到读写音频流的完整 API以及 STD / TDM / PDM 模式、主从模式、单双工、位宽、槽位等关键配置项的底层行为文末附带可直接运行的 Master/Slave 示例代码与源码级实现佐证。I2S 总线基础I²SInter-IC Sound正确写法为 I²S读音 eye-squared-ess另一种记法为 IIS是一种用于连接数字音频设备的串行电气总线接口标准。它用于在电子设备内部各集成电路之间传输PCMPulse-Code Modulation脉冲编码调制音频数据。I²S 总线将时钟与串行数据信号分离接收端无需像异步通信系统那样从数据流中恢复时钟因此接收器电路更简单。注意尽管名称相似I²S 与双向的 I²CIIC总线无关且不兼容。三条基本信号线I²S 总线至少包含三条信号线。在 arduino-esp32 的I2SClass中每条线都有对应的函数参数名信号线官方名称常见叫法库函数参数说明位时钟线Continuous Serial ClockSCKBit ClockBCLKsck为每个数据位提供时钟字时钟线Word SelectWSLeft-Right ClockLRCLK/ Frame SyncFSws0 左声道1 右声道数据线Serial DataSDSDATA、SDIN、SDOUT、DACDAT、ADCDAT 等dout/din承载音频采样数据一个关键特性与 Arduino I2S 使用单一数据引脚在输入/输出间切换不同ESP core 驱动为输入和输出使用独立的数据线。输出数据线对应函数参数dout输入数据线对应函数参数din。注意所有信号线几乎可以连接到任意 GPIO 引脚并且这种改动即使在运行期间也可以进行。可选的主时钟线总线还可以包含一条Master ClockMCLK线它并不属于 I²S 总线本身的组成部分而是用于同步多个 I2S 设备。在库中对应函数参数mclk。关于每个 ESP32 芯片 I2S 外设的更多细节可查阅 ESP-IDF 官方 I2S 文档ESP-IDF documentation 中api-reference/peripherals/i2s一节。I2S 配置项全解Master / Slave 模式Master 模式默认设备自行产生sck位时钟信号和ws字选择信号。Slave 模式设备监听引脚上由外部主机驱动的时钟与字选择信号。角色通过begin()的role参数选择默认值为I2S_ROLE_MASTER。I2S Port控制器选择默认情况下I2S 驱动会自动选择一个可用的 I2S 控制器。对于需要特定控制器的应用例如双 I2S 音频设计可以在初始化之前使用setPort()或I2SClass构造函数指定。从源码 libraries/ESP_I2S/src/ESP_I2S.cpp 可以看到驱动通过isValidI2SPort()校验端口有效性I2S_NUM_AUTO自动选择或从I2S_NUM_0到芯片物理控制器数量的范围内均为合法值不同目标芯片的控制器数量不同例如 ESP32/ESP32-S3 为 2 个ESP32-P4 为 3 个。操作模式Operation Modes操作模式通过begin()的mode参数设置可选值如下模式说明I2S_MODE_STD标准模式始终有两个声道左/右即所谓的 slots槽位。槽位支持 8/16/24/32 位宽的采样数据通信格式遵循 Philips 标准I2S_MODE_TDM时分复用模式声道数量可变每个声道的位宽固定I2S_MODE_PDM_TXTX 通道的 PDM脉冲密度调制模式可将 PCM 数据转换为 PDM 格式始终具有左右槽位。仅 I2S0 支持且只支持 16 位宽采样数据。至少需要 1 个 CLK 引脚提供时钟、1 个 DOUT 引脚提供数据I2S_MODE_PDM_RXRX 通道的 PDM 模式可接收 PDM 格式数据并转换为 PCM 格式。仅 I2S0 支持且只支持 16 位宽采样数据。至少需要 1 个 CLK 引脚和 1 个 DIN 引脚从源码 libraries/ESP_I2S/src/ESP_I2S.h 可以看出i2s_mode_t枚举的可用值由 SoC 能力宏SOC_I2S_SUPPORTS_TDM、SOC_I2S_SUPPORTS_PDM_TX、SOC_I2S_SUPPORTS_PDM_RX决定——并非所有芯片都支持所有模式编译时即会裁剪。Simplex / Duplex 模式由于时钟源不同PDM 模式始终是 Simplex单工只使用一根数据引脚。而STD 和 TDM 模式运行在 Duplex双工模式使用两根独立的数据引脚输出引脚dout、输入引脚din。在此模式下驱动可以同时在每条线上读写适合对讲机walkie-talkie或电话等应用。数据位宽Data Bit Width数据位宽即单个声道采样的比特数通过bits_cfg参数设置当前支持I2S_DATA_BIT_WIDTH_8BITI2S_DATA_BIT_WIDTH_16BITI2S_DATA_BIT_WIDTH_24BIT—— 需要手动将 MCLK 倍频设置为 384I2S_DATA_BIT_WIDTH_32BITESP32HW v1上的 8 位数据位宽原始 ESP32 的 I2S 外设对 8 位数据使用uint16_t对齐的 DMA 字仅高 8 位有效。arduino-esp32 库透明地处理了这一点write()将每个int8_t采样打包进uint16_t的高字节readBytes()再将其解包还原。对于单声道 8 位模式同样应用了现有的立体声变通方案硬件以立体声运行软件负责转换。这一切都是全自动的应用代码无需特殊处理。采样率Sample Rate采样率通过rate参数设置单位为 Hz表示每秒的样本数。例如16000即 16 kHz。槽位模式Slot Mode槽位模式通过ch参数设置值说明I2S_SLOT_MODE_MONO单声道槽位格式。TX 模式下在所有槽位发送相同数据RX 模式下只接收第一个槽位的数据I2S_SLOT_MODE_STEREO立体声槽位格式。TX 模式下在不同槽位发送不同数据RX 模式下接收所有槽位的数据ESP32I2S 硬件版本 1的已知硬件限制8 位和 16 位单声道模式下FIFO 打包逻辑会在收发过程中打乱数据。库通过自动将硬件配置为立体声模式、并在软件中进行单声道/立体声转换来绕过该限制write()将每个单声道采样复制到左右两个槽位readBytes()提取所选声道产生单声道输出。对于 8 位库还处理硬件要求的uint16_t高字节打包。这一切对应用代码完全透明。槽位掩码Slot Maskslot_mask参数用于选择使用哪个/哪些声道槽位可在begin()、configureTX()和configureRX()中使用。默认值为-1此时库根据槽位模式自动选择合理的默认值。支持的值值数值含义I2S_STD_SLOT_LEFT1仅使用左槽位I2S_STD_SLOT_RIGHT2仅使用右槽位I2S_STD_SLOT_BOTH3同时使用左右槽位TX 行为单声道模式LEFT在左线槽位发送单声道数据右槽位静音。RIGHT在右线槽位发送单声道数据左槽位静音。BOTH在两个线槽位都发送单声道数据适合用单声道音源驱动立体声 DAC。TX 行为立体声模式LEFT仅将缓冲区中的左声道数据发送到左线槽位右槽位静音。RIGHT仅将缓冲区中的右声道数据发送到右线槽位左槽位静音。BOTH正常发送交错interleaved的立体声数据。RX 行为单声道模式LEFT仅接收左声道。RIGHT仅接收右声道。BOTH在原始 ESP32HW v1上对左右声道取平均在其他目标芯片上会导致 DMA 缓冲区中的样本重复不推荐。RX 行为立体声模式无论用户传入什么值库在立体声 RX 时始终强制slot_mask为BOTH。原因是 ESP-IDF 并非在所有目标芯片上都能可靠地在立体声 RX 模式下忽略slot_mask——某些硬件会重复所选声道而不是输出正常的交错数据。库覆盖该掩码以保证正确的交错[L, R, L, R, ...]输出。注意在原始 ESP32HW v1的单声道 8 位或 16 位模式下slot_mask控制软件变通方案提取哪个声道LEFT提取左声道RIGHT提取右声道BOTH对两声道取平均。这可以通过configureRX()在运行时更改。Arduino-ESP32 I2S API 参考初始化与反初始化I2SClass 构造函数创建 I2S 对象。可选的port参数选择 I2S 控制器未指定时驱动使用I2S_NUM_AUTO。I2SClass I2S(i2s_port_t portI2S_NUM_AUTO)setPort设置要使用的 I2S 控制器。必须在begin()之前调用。bool setPort(i2s_port_t port)参数port为 I2S 控制器例如I2S_NUM_0、其他 SoC 专属端口如I2S_NUM_1或I2S_NUM_AUTO。成功返回true失败返回false。从源码 libraries/ESP_I2S/src/ESP_I2S.cpp 可见setPort()在begin()之后调用会直接报错返回false端口必须提前固定。getPort获取已配置的 I2S 控制器。begin()之后返回驱动实际分配的控制器begin()之前返回已配置的控制器值如I2S_NUM_AUTO。i2s_port_t getPort()源码实现中若通道已创建会通过i2s_channel_get_info()返回驱动实际分配的控制器 ID否则返回构造函数或setPort()配置的值见 libraries/ESP_I2S/src/ESP_I2S.cpp。begin初始化 I2S 驱动。调用begin()之前先用setPins()或 PDM 引脚设置函数选择要使用的引脚。bool begin(i2s_mode_t mode, uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch, int8_t slot_mask-1, i2s_role_t roleI2S_ROLE_MASTER)参数说明参数说明mode上文提到的操作模式之一例如I2S_MODE_STDrate采样率Hz例如16000bits_cfg单个声道采样的位数例如I2S_DATA_BIT_WIDTH_16BITch槽位模式例如I2S_SLOT_MODE_STEREOslot_mask选择要使用的槽位见上文「槽位掩码」。可选默认-1库默认单声道 TX/RX 为 LEFT立体声为 BOTH。立体声 RX 时库始终强制 BOTH无论传入何值roleI2S 角色。用I2S_ROLE_MASTER默认生成时钟和字选择信号或I2S_ROLE_SLAVE从外部主机接收它们成功返回true失败返回false。失败时若设置了正确的日志级别会打印错误信息。end执行安全的反初始化——释放缓冲区、销毁任务、结束驱动运行等。void end()引脚设置引脚设置函数取决于操作模式。setPinsSTD / TDM 模式void setPins(int8_t bclk, int8_t ws, int8_t dout, int8_t din-1, int8_t mclk-1)参数说明bclk位时钟引脚ws字选择引脚dout数据输出引脚不用时可设为-1din数据输入引脚可选默认-1不使用mclk主时钟引脚可选默认-1不使用setPinsPdmTxPDM TX 模式void setPinsPdmTx(int8_t clk, int8_t dout0, int8_t dout1-1)参数说明clk时钟引脚dout0数据输出引脚 0dout1数据输出引脚 1可选默认-1不使用setPinsPdmRxPDM RX 模式void setPinsPdmRx(int8_t clk, int8_t din0, int8_t din1-1, int8_t din2-1, int8_t din3-1)参数说明clk时钟引脚din0数据输入引脚 0din1数据输入引脚 1可选默认-1din2数据输入引脚 2可选默认-1din3数据输入引脚 3可选默认-1setInvertedSTD / TDM 模式设置哪些引脚使用反相逻辑。数据引脚不能反相。void setInverted(bool bclk, bool ws, bool mclkfalse)参数说明bclk位时钟引脚反相则为true否则falsews字选择引脚反相则为true否则falsemclk主时钟引脚反相则为true否则false。可选默认falsesetInvertedPdmPDM 模式void setInvertedPdm(bool clk)参数clk为时钟引脚反相则为true否则false。数据引脚同样不能反相。从源码 libraries/ESP_I2S/src/ESP_I2S.cpp 可见setPins()内部通过digitalPinToGPIONumber()将 Arduino 引脚号映射为 GPIO 号且 STD 模式下的 MCLK/BCLK/WS/DOUT/DIN 五个引脚均纳入外设管理器periman的引脚占用管理见 libraries/ESP_I2S/src/ESP_I2S.cpp初始化时会先释放引脚上原有外设避免引脚复用冲突。运行时配置I2S 配置可以在运行期间更改。configureTX配置 I2S TX 通道。bool configureTX(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch, int8_t slot_mask-1)参数rate为采样率Hz如16000bits_cfg为声道采样位数如I2S_DATA_BIT_WIDTH_16BITch为槽位模式如I2S_SLOT_MODE_STEREOslot_mask选择 TX 要使用的槽位可选默认-1使用库默认值。成功返回true失败返回false并在日志级别正确时打印错误信息。configureRX配置 I2S RX 通道。该函数无需调用end()/begin()即可在运行时更改采样率、位宽、槽位模式、RX 变换和槽位掩码。如果只有slot_mask变化rate、bits、声道模式不变库会执行轻量级的仅槽位重配置。bool configureRX(uint32_t rate, i2s_data_bit_width_t bits_cfg, i2s_slot_mode_t ch, i2s_rx_transform_t transformI2S_RX_TRANSFORM_NONE, int8_t slot_mask-1)参数参数说明rate采样率Hz例如16000bits_cfg声道采样位数例如I2S_DATA_BIT_WIDTH_16BITch槽位模式例如I2S_SLOT_MODE_STEREOtransform变换模式例如I2S_RX_TRANSFORM_NONE用于对接收数据施加变换/转换。支持值见下表slot_maskRX 声道选择的槽位掩码可选默认-1使用当前设置。单声道模式下选择接收哪个声道LEFT/RIGHT/BOTH立体声模式下该参数被忽略——库始终使用 BOTHtransform支持的取值值含义I2S_RX_TRANSFORM_NONE不进行任何变换I2S_RX_TRANSFORM_32_TO_16将 32 位数据位宽转换为 16 位I2S_RX_TRANSFORM_16_STEREO_TO_MONO在 16 位数据位宽下将立体声转换为单声道。使用该变换时提取的声道取决于配置的slot_maskLEFT提取左声道RIGHT提取右声道BOTH对两声道取平均从源码 libraries/ESP_I2S/src/ESP_I2S.h 可见i2s_rx_transform_t枚举还包含I2S_RX_TRANSFORM_8_UNPACKESP32 HW v1 的 8 位解包专用该变换在非 HW v1 目标上会被忽略见 libraries/ESP_I2S/src/ESP_I2S.cpp。I2S_RX_TRANSFORM_32_TO_16的典型场景是 SPH0645 等 32 位麦克风——驱动读取后右移 16 位压缩为 16 位数据见 libraries/ESP_I2S/src/ESP_I2S.cpp既节省存储又满足大多数应用的精度需求。成功返回true失败返回false失败时若设置了正确的日志级别会打印错误信息。状态查询i2s_chan_handle_t txChan()获取 TX 通道句柄指针。uint32_t txSampleRate()获取 TX 采样率。i2s_data_bit_width_t txDataWidth()获取 TX 数据位宽8、16 或 32 位。i2s_slot_mode_t txSlotMode()获取 TX 槽位模式stereo 或 mono。i2s_chan_handle_t rxChan()获取 RX 通道句柄指针。uint32_t rxSampleRate()获取 RX 采样率。i2s_data_bit_width_t rxDataWidth()获取 RX 数据位宽8、16 或 32 位。i2s_slot_mode_t rxSlotMode()获取 RX 槽位模式stereo 或 mono。I/O 操作readBytes从 I2S 接口读取指定字节数的数据。size_t readBytes(char *buffer, size_t size)参数buffer为存储读取数据的缓冲区长度必须至少为size字节size为要读取的字节数。返回实际读取的字节数。read从 I2S 接口读取下一个可用字节。int read()返回下一个可用字节若无数据可用或发生错误则返回-1。writePCM 数据必须按采样对齐的块写入。每次调用所需的最小字节数取决于begin()或configureTX()中配置的数据位宽位宽每采样最小字节数I2S_DATA_BIT_WIDTH_8BIT1 字节I2S_DATA_BIT_WIDTH_16BIT2 字节I2S_DATA_BIT_WIDTH_32BIT4 字节对于立体声采样必须以交错顺序先左后右到达驱动。每次write()调用必须至少包含一个完整采样16 位为 2 字节但一个完整的立体声帧可以一次调用发送也可以按声道分次发送。小于一个采样的写入会被忽略函数返回0且不设置lastError()。启用调试日志可看到相关提示。按声道写入——适合学习或一次生成一个采样时的清晰写法// 16-bit stereo — left then right int16_t left ...; int16_t right ...; i2s.write(left, sizeof(left)); i2s.write(right, sizeof(right));帧缓冲区写入——流式或批量 PCM 的首选// 16-bit stereo — one interleaved frame (4 bytes) int16_t frame[2] {left, right}; i2s.write(frame, sizeof(frame));write有两个版本。第一个版本将 PCM 字节缓冲区写入 I2S 接口size_t write(const void *buffer, size_t size)参数buffer为包含待写入数据的缓冲区size为要从缓冲区写入的字节数必须至少达到上述单采样最小值。流式传输时size应为完整帧大小采样大小 × 活跃声道数的整数倍。返回实际写入的字节数若缓冲区为空、TX 通道未初始化或size小于一个采样则返回0。第二个版本继承自Print将单字节转发到上面的缓冲区版本。对于大于 8 位的位宽单字节写入低于采样最小值因此会被忽略返回0且不设置错误size_t write(uint8_t d)参数d为要写入的字节。仅在字节被接受时8 位模式返回1对于 16 位及更宽的格式返回0因为单字节不是完整采样。从源码 libraries/ESP_I2S/src/ESP_I2S.cpp 的write(const void*, size_t)实现可以印证函数会校验size是否小于tx_data_bit_width / 8不足则直接返回0同时通过I2S_ERROR_CHECK_RETURN宏统一管理last_error与日志输出宏定义见 libraries/ESP_I2S/src/ESP_I2S.cpp。available查询是否有可读数据。int available()有数据可读时返回I2S_READ_CHUNK_SIZE否则返回-1。从源码可见该常量在 libraries/ESP_I2S/src/ESP_I2S.cpp 定义为1920available()直接返回该值见 libraries/ESP_I2S/src/ESP_I2S.cpp。peek获取 I2S 接口下一个可用字节但不从缓冲区移除。当前未实现。int peek()当前始终返回-1。lastError获取 I2S 接口上一次 I/O 操作的错误码。int lastError()recordWAV使用当前 RX 设置将一段短暂的 PCM WAV 录制到内存。返回的缓冲区必须由用户释放。uint8_t * recordWAV(size_t rec_seconds, size_t * out_size)参数rec_seconds为要录制的秒数out_size输出为返回缓冲区的大小字节。成功返回包含录制 WAV 数据的缓冲区指针出错返回NULL。playWAV使用当前 TX 设置播放内存中的 PCM WAV 数据。void playWAV(uint8_t * data, size_t len)参数data为包含 WAV 数据的缓冲区len为缓冲区大小字节。playMP3使用当前 TX 设置播放内存中的 MP3 数据。bool playMP3(uint8_t *src, size_t src_len)参数src为包含 MP3 数据的缓冲区src_len为缓冲区大小字节。成功返回true失败返回false日志级别正确时打印错误信息。该功能依赖可选的 MP3 解码器从 libraries/ESP_I2S/src/ESP_I2S.h 可见只有当工程中__has_include(mp3dec.h)成立时ARDUINO_HAS_MP3_DECODERplayMP3()才会被编译进 API 中。示例代码Master 模式默认#include ESP_I2S.h const int buff_size 128; int available_bytes, read_bytes; uint8_t buffer[buff_size]; I2SClass I2S; void setup() { I2S.setPins(5, 25, 26, 35, 0); //SCK, WS, SDOUT, SDIN, MCLK I2S.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO); I2S.read(); available_bytes I2S.available(); if(available_bytes buff_size) { read_bytes I2S.readBytes(buffer, available_bytes); } else { read_bytes I2S.readBytes(buffer, buff_size); } I2S.write(buffer, read_bytes); I2S.end(); } void loop() {}这个例子展示了标准的回环流程setPins()配置 SCK5、WS25、SDOUT26、SDIN35、MCLK0 五个引脚begin()以 16 kHz / 16 位 / 立体声启动 STD 模式随后读取一段音频数据并写回。Slave 模式#include ESP_I2S.h const int buff_size 128; uint8_t buffer[buff_size]; I2SClass I2S; void setup() { I2S.setPins(5, 25, -1, 35); //SCK, WS, SDOUT (not used), SDIN I2S.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO, -1, I2S_ROLE_SLAVE); size_t read_bytes I2S.readBytes((char *)buffer, buff_size); // process received data... I2S.end(); } void loop() {}Slave 模式的关键区别dout传-1仅接收begin()最后一个参数传I2S_ROLE_SLAVE设备只监听外部主机提供的时钟与字选择信号。更多可运行的示例仓库 libraries/ESP_I2S/examples 目录下提供了多个可直接编译运行的示例与本文 API 一一对应Simple_tonelibraries/ESP_I2S/examples/Simple_tone/Simple_tone.ino在loop()中逐采样产生 440 Hz 方波按左/右声道分两次write()到 MAX98357 I2S 功放板演示最小化的 TX 播放流程引脚可自由更换代码注释明确说明 GPIO 不固定。Record_to_WAVlibraries/ESP_I2S/examples/Record_to_WAV/Record_to_WAV.ino在 ESP32-S2-EYE 上以I2S_MODE_STDI2S_DATA_BIT_WIDTH_32BITI2S_SLOT_MODE_MONOI2S_STD_SLOT_LEFT初始化调用recordWAV(5, wav_size)录制 5 秒音频后写入 SD 卡 WAV 文件是recordWAV()与configureRX变换32→16 位的完整落地案例。Port_selectionlibraries/ESP_I2S/examples/Port_selection/Port_selection.ino在begin()前显式调用setPort(I2S_NUM_0)选择控制器并通过getPort()打印实际分配的端口演示双 I2S 设计中的端口管理。总结ESP_I2S库源码位于 libraries/ESP_I2S/src/ESP_I2S.h 与 libraries/ESP_I2S/src/ESP_I2S.cpplibrary.properties中声明版本为 3.3.11把 ESP-IDF 的driver/i2s_std.h、driver/i2s_tdm.h、driver/i2s_pdm.h底层驱动封装为类 Arduino Stream 风格的高层 API。理解本文的总线信号、操作模式、槽位掩码与变换transform机制后即可在 ESP32 全系列芯片上快速落地音频播放、录音、语音通话对讲机、PDM 麦克风采集等应用。配置要点可归纳为四步选端口setPort→ 设引脚setPins/setPinsPdmTx/setPinsPdmRx→ 定参数begin的 mode/rate/bits/ch/slot_mask/role→ 运行时微调configureTX/configureRX且原始 ESP32 的 8 位与单声道硬件缺陷均由库自动规避应用层无需感知。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考