I2S
简介
I2S(Inter-IC Sound,集成电路内置音频总线)是一种同步串行通信协议,通常用于在两个数字音频设备之间传输音频数据。
ESP32-S2 包含 1 个 I2S 外设。通过配置这些外设,可以借助 I2S 驱动来输入和输出采样数据。
标准或 TDM 通信模式下的 I2S 总线包含以下几条线路:
MCLK:主时钟线。该信号线可选,具体取决于从机,主要用于向 I2S 从机提供参考时钟。
BCLK:位时钟线。用于数据线的位时钟。
WS:字(声道)选择线。通常用于识别声道(除 PDM 模式外)。
DIN/DOUT:串行数据输入/输出线。如果 DIN 和 DOUT 被配置到相同的 GPIO,数据将在内部回环。
每个 I2S 控制器都具备以下功能,可由 I2S 驱动进行配置:
可用作系统主机或从机
可用作发射器或接收器
DMA 控制器支持流数据采样,CPU 无需单独复制每个采样数据
每个控制器都支持 RX 或 TX 单工通信。由于 RX 与 TX 通道共用一个时钟,因此只有在两者拥有相同配置时,才可以实现全双工通信。
I2S 文件结构
需要包含在 I2S 应用中的公共头文件如下所示:
i2s.h
:提供原有 I2S API(用于使用原有驱动的应用)。i2s_std.h
:提供标准通信模式的 API(用于使用标准模式的新驱动程序的应用)。i2s_pdm.h
:提供 PDM 通信模式的 API(用于使用 PDM 模式的新驱动程序的应用)。i2s_tdm.h
:提供 TDM 通信模式的 API(用于使用 TDM 模式的新驱动的应用)。
备注
原有驱动与新驱动无法共存。包含 i2s.h
以使用原有驱动,或包含其他三个头文件以使用新驱动。原有驱动未来可能会被删除。
已包含在上述头文件中的公共头文件如下所示:
i2s_types_legacy.h
:提供只在原有驱动中使用的原有公共类型。i2s_types.h
:提供公共类型。i2s_common.h
:提供所有通信模式通用的 API。
I2S 时钟
时钟源
i2s_clock_src_t::I2S_CLK_SRC_DEFAULT
:默认 PLL 时钟。
i2s_clock_src_t::I2S_CLK_SRC_PLL_160M
:160 MHz PLL 时钟。
i2s_clock_src_t::I2S_CLK_SRC_APLL
:音频 PLL 时钟,在高采样率应用中比I2S_CLK_SRC_PLL_160M
更精确。其频率可根据采样率进行配置,但如果 APLL 已经被 EMAC 或其他通道占用,则无法更改 APLL 频率,驱动程序将尝试在原有 APLL 频率下工作。如果原有 APLL 频率无法满足 I2S 的需求,时钟配置将失败。
时钟术语
采样率:单声道每秒采样数据数量。
SCLK:源时钟频率,即时钟源的频率。
MCLK:主时钟频率,BCLK 由其产生。MCLK 信号通常作为参考时钟,用于同步 I2S 主机和从机之间的 BCLK 和 WS。
BCLK:位时钟频率,一个 BCLK 时钟周期代表数据管脚上的一个数据位。通过
i2s_std_slot_config_t::slot_bit_width
配置的通道位宽即为一个声道中的 BCLK 时钟周期数量,因此一个声道中可以有 8/16/24/32 个 BCLK 时钟周期。LRCK / WS:左/右时钟或字选择时钟。在非 PDM 模式下,其频率等于采样率。
备注
通常,MCLK 应该同时是 采样率
和 BCLK 的倍数。字段 i2s_std_clk_config_t::mclk_multiple
表示 MCLK 相对于 采样率
的倍数。在大多数情况下,将其设置为 I2S_MCLK_MULTIPLE_256
即可。但如果 slot_bit_width
被设置为 I2S_SLOT_BIT_WIDTH_24BIT
,为了保证 MCLK 是 BCLK 的整数倍,应该将 i2s_std_clk_config_t::mclk_multiple
设置为能被 3 整除的倍数,如 I2S_MCLK_MULTIPLE_384
,否则 WS 会不精准。
I2S 通信模式
模式概览
芯片 |
I2S 标准 |
PDM TX |
PDM RX |
TDM |
ADC/DAC |
LCD/摄像头 |
---|---|---|---|---|---|---|
ESP32 |
I2S 0/1 |
I2S 0 |
I2S 0 |
无 |
I2S 0 |
I2S 0 |
ESP32-S2 |
I2S 0 |
无 |
无 |
无 |
无 |
I2S 0 |
ESP32-C3 |
I2S 0 |
I2S 0 |
无 |
I2S 0 |
无 |
无 |
ESP32-S3 |
I2S 0/1 |
I2S 0 |
I2S 0 |
I2S 0/1 |
无 |
无 |
标准模式
标准模式中有且仅有左右两个声道,驱动中将声道称为 slot。这些声道可以支持 8/16/24/32 位宽的采样数据,声道的通信格式主要包括以下几种:
Philips 格式:数据信号与 WS 信号相比有一个位的位移。WS 信号的占空比为 50%。
MSB 格式:与 Philips 格式基本相同,但其数据没有位移。
PCM 帧同步:数据有一个位的位移,同时 WS 信号变成脉冲,持续一个 BCLK 周期。
LCD/摄像头模式
LCD/摄像头模式只支持在 I2S0 上通过并行总线运行。在 LCD 模式下,I2S0 应当设置为主机 TX 模式;在摄像头模式下,I2S0 应当设置为从机 RX 模式。这两种模式不是由 I2S 驱动实现的,关于 LCD 模式的实现,请参阅 LCD。更多信息请参考 ESP32-S2 技术参考手册 > I2S 控制器 (I2S) > LCD 模式 [PDF]。
功能概览
I2S 驱动提供以下服务:
资源管理
I2S 驱动中的资源可分为三个级别:
平台级资源
:当前芯片中所有 I2S 控制器的资源。控制器级资源
:一个 I2S 控制器的资源。通道级资源
:一个 I2S 控制器 TX 或 RX 通道的资源。
公开的 API 都是通道级别的 API,通道句柄 i2s_chan_handle_t
可以帮助用户管理特定通道下的资源,而无需考虑其他两个级别的资源。高级别资源为私有资源,由驱动自动管理。用户可以调用 i2s_new_channel()
来分配通道句柄,或调用 i2s_del_channel()
来删除该句柄。
电源管理
电源管理启用(即开启 CONFIG_PM_ENABLE)时,系统将在进入 Light-sleep 前调整或停止 I2S 时钟源,这可能会影响 I2S 信号,从而导致传输或接收的数据无效。
I2S 驱动可以获取电源管理锁,从而防止系统设置更改或时钟源被禁用。时钟源为 APB 时,锁的类型将被设置为 esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX
。时钟源为 APLL(若支持)时,锁的类型将被设置为 esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP
。用户通过 I2S 读写时(即调用 i2s_channel_read()
或 i2s_channel_write()
),驱动程序将获取电源管理锁,并在读写完成后释放锁。
有限状态机
I2S 通道有三种状态,分别为 registered(已注册)
、 ready(准备就绪)
和 running(运行中)
,它们的关系如下图所示:
图中的 <mode>
可用相应的 I2S 通信模式来代替,如 std
代表标准的双声道模式。更多关于通信模式的信息,请参考 I2S 通信模式 小节。
数据传输
I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输数据之前,请调用 i2s_channel_enable()
来启用特定的通道。发送或接收的数据达到 DMA 缓冲区的大小时,将触发 I2S_OUT_EOF
或 I2S_IN_SUC_EOF
中断。注意,DMA 缓冲区的大小不等于 i2s_chan_config_t::dma_frame_num
,这里的一帧是指一个 WS 周期内的所有采样数据。因此, dma_buffer_size = dma_frame_num * slot_num * slot_bit_width / 8
。传输数据时,可以调用 i2s_channel_write()
来输入数据,并把数据从源缓冲区复制到 DMA TX 缓冲区等待传输完成。此过程将重复进行,直到发送的字节数达到配置的大小。接收数据时,用户可以调用函数 i2s_channel_read()
来等待接收包含 DMA 缓冲区地址的消息队列,从而将数据从 DMA RX 缓冲区复制到目标缓冲区。
i2s_channel_write()
和 i2s_channel_read()
都是阻塞函数,在源缓冲区的数据发送完毕前,或是整个目标缓冲区都被加载数据占用时,它们会一直保持等待状态。在等待时间达到最大阻塞时间时,返回 ESP_ERR_TIMEOUT 错误。要实现异步发送或接收数据,可以通过 i2s_channel_register_event_callback()
注册回调,随即便可在回调函数中直接访问 DMA 缓冲区,无需通过这两个阻塞函数来发送或接收数据。但请注意,该回调是一个中断回调,不要在该回调中添加复杂的逻辑、进行浮点运算或调用不可重入函数。
配置
用户可以通过调用相应函数(即 i2s_channel_init_std_mode()
、 i2s_channel_init_pdm_rx_mode()
、 i2s_channel_init_pdm_tx_mode()
或 i2s_channel_init_tdm_mode()
)将通道初始化为特定模式。如果初始化后需要更新配置,必须先调用 i2s_channel_disable()
以确保通道已经停止运行,然后再调用相应的 ‘reconfig’ 函数,例如 i2s_channel_reconfig_std_slot()
、 i2s_channel_reconfig_std_clock()
和 i2s_channel_reconfig_std_gpio()
。
IRAM 安全
默认情况下,由于写入或擦除 flash 等原因导致 cache 被禁用时,I2S 中断将产生延迟,无法及时执行 EOF 中断。
在实时应用中,可通过启用 Kconfig 选项 CONFIG_I2S_ISR_IRAM_SAFE 来避免此种情况发生,启用后:
即使在 cache 被禁用的情况下,中断仍可继续运行。
驱动程序将存放进 DRAM 中(以防其意外映射到 PSRAM 中)。
启用该选项可以保证 cache 禁用时的中断运行,但会相应增加 IRAM 占用。
线程安全
驱动程序可保证所有公开的 I2S API 的线程安全,使用时,可以直接从不同的 RTOS 任务中调用此类 API,无需额外锁保护。注意,I2S 驱动使用 mutex 锁来保证线程安全,因此不允许在 ISR 中使用这些 API。
Kconfig 选项
CONFIG_I2S_ISR_IRAM_SAFE 控制默认 ISR 处理程序能否在禁用 cache 的情况下工作。更多信息可参考 IRAM 安全。
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN 控制是否在使用原有 I2S 驱动时关闭警告信息。
CONFIG_I2S_ENABLE_DEBUG_LOG 用于启用调试日志输出。启用该选项将增加固件的二进制文件大小。
应用实例
I2S 驱动例程请参考 peripherals/i2s 目录。以下为每种模式的简单用法:
标准 TX/RX 模式的应用
不同声道的通信格式可通过以下标准模式的辅助宏来生成。如上所述,在标准模式下有三种格式,辅助宏分别为:
时钟配置的辅助宏为:
请参考 标准模式 了解 STD API 的相关信息。更多细节请参考 driver/include/driver/i2s_std.h。
STD TX 模式
以 16 位数据位宽为例,如果 uint16_t
写缓冲区中的数据如下所示:
数据 0 |
数据 1 |
数据 2 |
数据 3 |
数据 4 |
数据 5 |
数据 6 |
数据 7 |
… |
---|---|---|---|---|---|---|---|---|
0x0001 |
0x0002 |
0x0003 |
0x0004 |
0x0005 |
0x0006 |
0x0007 |
0x0008 |
… |
下表展示了在不同 i2s_std_slot_config_t::slot_mode
和 i2s_std_slot_config_t::slot_mask
设置下线路上的真实数据。
数据位宽 |
声道模式 |
声道掩码 |
WS 低电平 |
WS 高电平 |
WS 低电平 |
WS 高电平 |
WS 低电平 |
WS 高电平 |
WS 低电平 |
WS 高电平 |
---|---|---|---|---|---|---|---|---|---|---|
16 位 |
单声道 |
左 |
0x0001 |
0x0000 |
0x0002 |
0x0000 |
0x0003 |
0x0000 |
0x0004 |
0x0000 |
右 |
0x0000 |
0x0001 |
0x0000 |
0x0002 |
0x0000 |
0x0003 |
0x0000 |
0x0004 |
||
左右 |
0x0001 |
0x0001 |
0x0002 |
0x0002 |
0x0003 |
0x0003 |
0x0004 |
0x0004 |
||
立体声 |
左 |
0x0001 |
0x0001 |
0x0003 |
0x0003 |
0x0005 |
0x0005 |
0x0007 |
0x0007 |
|
右 |
0x0002 |
0x0002 |
0x0004 |
0x0004 |
0x0006 |
0x0006 |
0x0008 |
0x0008 |
||
左右 |
0x0001 |
0x0002 |
0x0003 |
0x0004 |
0x0005 |
0x0006 |
0x0007 |
0x0008 |
备注
数据位宽为 8 位和 32 位时,缓冲区的类型最好为 uint8_t
和 uint32_t
。但需注意,数据位宽为 24 位时,数据缓冲区应该以 3 字节对齐,即每 3 个字节代表一个 24 位数据,另外,i2s_chan_config_t::dma_frame_num
、 i2s_std_clk_config_t::mclk_multiple
和写缓冲区的大小应该为 3
的倍数,否则线路上的数据或采样率可能会不准确。
#include "driver/i2s_std.h"
#include "driver/gpio.h"
i2s_chan_handle_t tx_handle;
/* 通过辅助宏获取默认的通道配置
* 这个辅助宏在 'i2s_common.h' 中定义,由所有 I2S 通信模式共享
* 它可以帮助指定 I2S 角色和端口 ID */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
/* 分配新的 TX 通道并获取该通道的句柄 */
i2s_new_channel(&chan_cfg, &tx_handle, NULL);
/* 进行配置,可以通过宏生成声道配置和时钟配置
* 这两个辅助宏在 'i2s_std.h' 中定义,只能用于 STD 模式
* 它们可以帮助初始化或更新声道和时钟配置 */
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_4,
.ws = GPIO_NUM_5,
.dout = GPIO_NUM_18,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* 初始化通道 */
i2s_channel_init_std_mode(tx_handle, &std_cfg);
/* 在写入数据之前,先启用 TX 通道 */
i2s_channel_enable(tx_handle);
i2s_channel_write(tx_handle, src_buf, bytes_to_write, bytes_written, ticks_to_wait);
/* 如果需要更新声道或时钟配置
* 需要在更新前先禁用通道 */
// i2s_channel_disable(tx_handle);
// std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO; // 默认为立体声
// i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg);
// std_cfg.clk_cfg.sample_rate_hz = 96000;
// i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg);
/* 删除通道之前必须先禁用通道 */
i2s_channel_disable(tx_handle);
/* 如果不再需要句柄,删除该句柄以释放通道资源 */
i2s_del_channel(tx_handle);
STD RX 模式
例如,当数据位宽为 16 时,如线路上的数据如下所示:
WS 低电平 |
WS 高电平 |
WS 低电平 |
WS 高电平 |
WS 低电平 |
WS 高电平 |
WS 低电平 |
WS 高电平 |
… |
---|---|---|---|---|---|---|---|---|
0x0001 |
0x0002 |
0x0003 |
0x0004 |
0x0005 |
0x0006 |
0x0007 |
0x0008 |
… |
不同 i2s_std_slot_config_t::slot_mode
和 i2s_std_slot_config_t::slot_mask
配置下缓冲区中收到的数据如下所示。
数据位宽 |
声道模式 |
声道掩码 |
数据 0 |
数据 1 |
数据 2 |
数据 3 |
数据 4 |
数据 5 |
数据 6 |
数据 7 |
---|---|---|---|---|---|---|---|---|---|---|
16 位 |
单声道 |
左 |
0x0001 |
0x0003 |
0x0005 |
0x0007 |
0x0009 |
0x000b |
0x000d |
0x000f |
右 |
0x0002 |
0x0004 |
0x0006 |
0x0008 |
0x000a |
0x000c |
0x000e |
0x0010 |
||
立体声 |
任意 |
0x0001 |
0x0002 |
0x0003 |
0x0004 |
0x0005 |
0x0006 |
0x0007 |
0x0008 |
备注
8 位、24 位和 32 位与 16 位的情况类似,接收缓冲区的数据位宽与线路上的数据位宽相等。此外需注意,数据位宽为 24 位时, i2s_chan_config_t::dma_frame_num
、 i2s_std_clk_config_t::mclk_multiple
和接收缓冲区的大小应该为 3
的倍数,否则线路上的数据或采样率可能会不准确。
#include "driver/i2s_std.h"
#include "driver/gpio.h"
i2s_chan_handle_t rx_handle;
/* 通过辅助宏获取默认的通道配置
* 这个辅助宏在 'i2s_common.h' 中定义,由所有 I2S 通信模式共享
* 它可以帮助指定 I2S 角色和端口 ID */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
/* 分配新的 TX 通道并获取该通道的句柄 */
i2s_new_channel(&chan_cfg, NULL, &rx_handle);
/* 进行配置,可以通过宏生成声道配置和时钟配置
* 这两个辅助宏在 'i2s_std.h' 中定义,只能用于 STD 模式
* 它们可以帮助初始化或更新声道和时钟配置 */
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_4,
.ws = GPIO_NUM_5,
.dout = I2S_GPIO_UNUSED,
.din = GPIO_NUM_19,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* 初始化通道 */
i2s_channel_init_std_mode(rx_handle, &std_cfg);
/* 在读取数据之前,先启动 RX 通道 */
i2s_channel_enable(rx_handle);
i2s_channel_read(rx_handle, desc_buf, bytes_to_read, bytes_read, ticks_to_wait);
/* 删除通道之前必须先禁用通道 */
i2s_channel_disable(rx_handle);
/* 如果不再需要句柄,删除该句柄以释放通道资源 */
i2s_del_channel(rx_handle);
全双工
全双工模式可以在 I2S 端口中同时注册 TX 和 RX 通道,同时通道共享 BCLK 和 WS 信号。目前,STD 和 TDM 通信模式支持以下方式的全双工通信,但不支持 PDM 全双工模式,因为 PDM 模式下 TX 和 RX 通道的时钟不同。
请注意,一个句柄只能代表一个通道,因此仍然需要对 TX 和 RX 通道逐个进行声道和时钟配置。
以下示例展示了如何分配两个全双工通道:
#include "driver/i2s_std.h"
#include "driver/gpio.h"
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
/* 分配两个 I2S 通道 */
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
/* 同时分配给 TX 和 RX 通道,使其进入全双工模式。 */
i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle);
/* 配置两个通道,因为在全双工模式下,TX 和 RX 通道必须相同。 */
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(32000),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_4,
.ws = GPIO_NUM_5,
.dout = GPIO_NUM_18,
.din = GPIO_NUM_19,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
i2s_channel_init_std_mode(tx_handle, &std_cfg);
i2s_channel_init_std_mode(rx_handle, &std_cfg);
i2s_channel_enable(tx_handle);
i2s_channel_enable(rx_handle);
...
单工模式
在单工模式下分配通道句柄,应该为每个通道调用 i2s_new_channel()
。在 ESP32-S2 上,TX/RX 通道的时钟和 GPIO 管脚不是相互独立的,因此在单工模式下,TX 和 RX 通道不能共存于同一个 I2S 端口中。
#include "driver/i2s_std.h"
#include "driver/gpio.h"
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
i2s_new_channel(&chan_cfg, &tx_handle, NULL);
i2s_std_config_t std_tx_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(48000),
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = GPIO_NUM_0,
.bclk = GPIO_NUM_4,
.ws = GPIO_NUM_5,
.dout = GPIO_NUM_18,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
/* 初始化通道 */
i2s_channel_init_std_mode(tx_handle, &std_tx_cfg);
i2s_channel_enable(tx_handle);
/* 如果没有找到其他可用的 I2S 设备,RX 通道将被注册在另一个 I2S 上
* 并返回 ESP_ERR_NOT_FOUND */
i2s_new_channel(&chan_cfg, NULL, &rx_handle);
i2s_std_config_t std_rx_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = GPIO_NUM_6,
.ws = GPIO_NUM_7,
.dout = I2S_GPIO_UNUSED,
.din = GPIO_NUM_19,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
i2s_channel_init_std_mode(rx_handle, &std_rx_cfg);
i2s_channel_enable(rx_handle);
应用注意事项
防止数据丢失
对于需要高频采样率的应用,数据的巨大吞吐量可能会导致数据丢失。用户可以通过注册 ISR 回调函数来接收事件队列中的数据丢失事件:
static IRAM_ATTR bool i2s_rx_queue_overflow_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) { // 处理 RX 队列溢出事件 ... return false; } i2s_event_callbacks_t cbs = { .on_recv = NULL, .on_recv_q_ovf = i2s_rx_queue_overflow_callback, .on_sent = NULL, .on_send_q_ovf = NULL, }; TEST_ESP_OK(i2s_channel_register_event_callback(rx_handle, &cbs, NULL));
请按照以下步骤操作,以防止数据丢失:
确定中断间隔。通常来说,当发生数据丢失时,为减少中断次数,中断间隔应该越久越好。因此,在保证 DMA 缓冲区大小不超过最大值 4092 的前提下,应使
dma_frame_num
尽可能大。具体转换关系如下:interrupt_interval(unit: sec) = dma_frame_num / sample_rate dma_buffer_size = dma_frame_num * slot_num * data_bit_width / 8 <= 4092
确定
dma_desc_num
的值。dma_desc_num
由i2s_channel_read
轮询周期的最大时间决定,所有接收到的数据都应该存储在两个i2s_channel_read
之间。这个周期可以通过计时器或输出 GPIO 信号来计算。具体转换关系如下:dma_desc_num > polling_cycle / interrupt_interval
确定接收缓冲区大小。在
i2s_channel_read
中提供的接收缓冲区应当能够容纳所有 DMA 缓冲区中的数据,这意味着它应该大于所有 DMA 缓冲区的总大小:recv_buffer_size > dma_desc_num * dma_buffer_size
例如,如果某个 I2S 应用的已知值包括:
sample_rate = 144000 Hz
data_bit_width = 32 bits
slot_num = 2
polling_cycle = 10 ms
那么可以按照以下公式计算出参数 dma_frame_num
、 dma_desc_num
和 recv_buf_size
:
dma_frame_num * slot_num * data_bit_width / 8 = dma_buffer_size <= 4092
dma_frame_num <= 511
interrupt_interval = dma_frame_num / sample_rate = 511 / 144000 = 0.003549 s = 3.549 ms
dma_desc_num > polling_cycle / interrupt_interval = cell(10 / 3.549) = cell(2.818) = 3
recv_buffer_size > dma_desc_num * dma_buffer_size = 3 * 4092 = 12276 bytes
API 参考
标准模式
Header File
Functions
-
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
Initialize i2s channel to standard mode.
备注
Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized) and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
- 参数
handle – [in] I2S channel handler
std_cfg – [in] Configurations for standard mode, including clock, slot and gpio The clock configuration can be generated by the helper macro
I2S_STD_CLK_DEFAULT_CONFIG
The slot configuration can be generated by the helper macroI2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG
,I2S_STD_PCM_SLOT_DEFAULT_CONFIG
orI2S_STD_MSB_SLOT_DEFAULT_CONFIG
- 返回
ESP_OK Initialize successfully
ESP_ERR_NO_MEM No memory for storing the channel information
ESP_ERR_INVALID_ARG NULL pointer or invalid configuration
ESP_ERR_INVALID_STATE This channel is not registered
-
esp_err_t i2s_channel_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std_clk_config_t *clk_cfg)
Reconfigure the I2S clock for standard mode.
备注
Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started this function won’t change the state. ‘i2s_channel_disable’ should be called before calling this function if i2s has started.
备注
The input channel handle has to be initialized to standard mode, i.e., ‘i2s_channel_init_std_mode’ has been called before reconfiguring
- 参数
handle – [in] I2S channel handler
clk_cfg – [in] Standard mode clock configuration, can be generated by
I2S_STD_CLK_DEFAULT_CONFIG
- 返回
ESP_OK Set clock successfully
ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
-
esp_err_t i2s_channel_reconfig_std_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg)
Reconfigure the I2S slot for standard mode.
备注
Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started this function won’t change the state. ‘i2s_channel_disable’ should be called before calling this function if i2s has started.
备注
The input channel handle has to be initialized to standard mode, i.e., ‘i2s_channel_init_std_mode’ has been called before reconfiguring
- 参数
handle – [in] I2S channel handler
slot_cfg – [in] Standard mode slot configuration, can be generated by
I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG
,I2S_STD_PCM_SLOT_DEFAULT_CONFIG
andI2S_STD_MSB_SLOT_DEFAULT_CONFIG
.
- 返回
ESP_OK Set clock successfully
ESP_ERR_NO_MEM No memory for DMA buffer
ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
-
esp_err_t i2s_channel_reconfig_std_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_config_t *gpio_cfg)
Reconfigure the I2S gpio for standard mode.
备注
Only allowed to be called when the channel state is READY, i.e., channel has been initialized, but not started this function won’t change the state. ‘i2s_channel_disable’ should be called before calling this function if i2s has started.
备注
The input channel handle has to be initialized to standard mode, i.e., ‘i2s_channel_init_std_mode’ has been called before reconfiguring
- 参数
handle – [in] I2S channel handler
gpio_cfg – [in] Standard mode gpio configuration, specified by user
- 返回
ESP_OK Set clock successfully
ESP_ERR_INVALID_ARG NULL pointer, invalid configuration or not standard mode
ESP_ERR_INVALID_STATE This channel is not initialized or not stopped
Structures
-
struct i2s_std_slot_config_t
I2S slot configuration for standard mode.
Public Members
-
i2s_data_bit_width_t data_bit_width
I2S sample data bit width (valid data bits per sample)
-
i2s_slot_bit_width_t slot_bit_width
I2S slot bit width (total bits per slot)
-
i2s_slot_mode_t slot_mode
Set mono or stereo mode with I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO In TX direction, mono means the written buffer contains only one slot data and stereo means the written buffer contains both left and right data
-
i2s_std_slot_mask_t slot_mask
Select the left, right or both slot
-
uint32_t ws_width
WS signal width (i.e. the number of bclk ticks that ws signal is high)
-
bool ws_pol
WS signal polarity, set true to enable high lever first
-
bool bit_shift
Set to enable bit shift in Philips mode
-
bool msb_right
Set to place right channel data at the MSB in the FIFO
-
i2s_data_bit_width_t data_bit_width
-
struct i2s_std_clk_config_t
I2S clock configuration for standard mode.
Public Members
-
uint32_t sample_rate_hz
I2S sample rate
-
i2s_clock_src_t clk_src
Choose clock source
-
i2s_mclk_multiple_t mclk_multiple
The multiple of mclk to the sample rate Default is 256 in the helper macro, it can satisfy most of cases, but please set this field a multiple of ‘3’ (like 384) when using 24-bit data width, otherwise the sample rate might be inaccurate
-
uint32_t sample_rate_hz
-
struct i2s_std_gpio_config_t
I2S standard mode GPIO pins configuration.
Public Members
-
gpio_num_t mclk
MCK pin, output
-
gpio_num_t bclk
BCK pin, input in slave role, output in master role
-
gpio_num_t ws
WS pin, input in slave role, output in master role
-
gpio_num_t dout
DATA pin, output
-
gpio_num_t din
DATA pin, input
-
uint32_t mclk_inv
Set 1 to invert the mclk output
-
uint32_t bclk_inv
Set 1 to invert the bclk input/output
-
uint32_t ws_inv
Set 1 to invert the ws input/output
-
struct i2s_std_gpio_config_t::[anonymous] invert_flags
GPIO pin invert flags
-
gpio_num_t mclk
-
struct i2s_std_config_t
I2S standard mode major configuration that including clock/slot/gpio configuration.
Public Members
-
i2s_std_clk_config_t clk_cfg
Standard mode clock configuration, can be generated by macro I2S_STD_CLK_DEFAULT_CONFIG
-
i2s_std_slot_config_t slot_cfg
Standard mode slot configuration, can be generated by macros I2S_STD_[mode]_SLOT_DEFAULT_CONFIG, [mode] can be replaced with PHILIPS/MSB/PCM
-
i2s_std_gpio_config_t gpio_cfg
Standard mode gpio configuration, specified by user
-
i2s_std_clk_config_t clk_cfg
Macros
-
I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo)
Philips format in 2 slots.
This file is specified for I2S standard communication mode Features:
Philips/MSB/PCM are supported in standard mode
Fixed to 2 slots
- 参数
bits_per_sample – i2s data bit width
mono_or_stereo – I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
-
I2S_STD_PCM_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo)
PCM(short) format in 2 slots.
备注
PCM(long) is same as philips in 2 slots
- 参数
bits_per_sample – i2s data bit width
mono_or_stereo – I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
-
I2S_STD_MSB_SLOT_DEFAULT_CONFIG(bits_per_sample, mono_or_stereo)
MSB format in 2 slots.
- 参数
bits_per_sample – i2s data bit width
mono_or_stereo – I2S_SLOT_MODE_MONO or I2S_SLOT_MODE_STEREO
-
I2S_STD_CLK_DEFAULT_CONFIG(rate)
i2s default standard clock configuration
备注
Please set the mclk_multiple to I2S_MCLK_MULTIPLE_384 while using 24 bits data width Otherwise the sample rate might be imprecise since the bclk division is not a integer
- 参数
rate – sample rate
I2S 驱动
Header File
Functions
-
esp_err_t i2s_new_channel(const i2s_chan_config_t *chan_cfg, i2s_chan_handle_t *ret_tx_handle, i2s_chan_handle_t *ret_rx_handle)
Allocate new I2S channel(s)
备注
The new created I2S channel handle will be REGISTERED state after it is allocated successfully.
备注
When the port id in channel configuration is I2S_NUM_AUTO, driver will allocate I2S port automatically on one of the i2s controller, otherwise driver will try to allocate the new channel on the selected port.
备注
If both tx_handle and rx_handle are not NULL, it means this I2S controller will work at full-duplex mode, the rx and tx channels will be allocated on a same I2S port in this case. Note that some configurations of tx/rx channel are shared on ESP32 and ESP32S2, so please make sure they are working at same condition and under same status(start/stop). Currently, full-duplex mode can’t guarantee tx/rx channels write/read synchronously, they can only share the clock signals for now.
备注
If tx_handle OR rx_handle is NULL, it means this I2S controller will work at simplex mode. For ESP32 and ESP32S2, the whole I2S controller (i.e. both rx and tx channel) will be occupied, even if only one of rx or tx channel is registered. For the other targets, another channel on this controller will still available.
- 参数
chan_cfg – [in] I2S controller channel configurations
ret_tx_handle – [out] I2S channel handler used for managing the sending channel(optional)
ret_rx_handle – [out] I2S channel handler used for managing the receiving channel(optional)
- 返回
ESP_OK Allocate new channel(s) success
ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip
ESP_ERR_INVALID_ARG NULL pointer or illegal parameter in i2s_chan_config_t
ESP_ERR_NOT_FOUND No available I2S channel found
-
esp_err_t i2s_del_channel(i2s_chan_handle_t handle)
Delete the i2s channel.
备注
Only allowed to be called when the i2s channel is at REGISTERED or READY state (i.e., it should stop before deleting it).
备注
Resource will be free automatically if all channels in one port are deleted
- 参数
handle – [in] I2S channel handler
ESP_OK Delete successfully
ESP_ERR_INVALID_ARG NULL pointer
-
esp_err_t i2s_channel_get_info(i2s_chan_handle_t handle, i2s_chan_info_t *chan_info)
Get I2S channel information.
- 参数
handle – [in] I2S channel handler
chan_info – [out] I2S channel basic information
- 返回
ESP_OK Get i2s channel information success
ESP_ERR_NOT_FOUND The input handle doesn’t match any registered I2S channels, it may not an i2s channel handle or not available any more
ESP_ERR_INVALID_ARG The input handle or chan_info pointer is NULL
-
esp_err_t i2s_channel_enable(i2s_chan_handle_t handle)
Enable the i2s channel.
备注
Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started) the channel will enter RUNNING state once it is enabled successfully.
备注
Enable the channel can start the I2S communication on hardware. It will start outputting bclk and ws signal. For mclk signal, it will start to output when initialization is finished
- 参数
handle – [in] I2S channel handler
ESP_OK Start successfully
ESP_ERR_INVALID_ARG NULL pointer
ESP_ERR_INVALID_STATE This channel has not initialized or already started
-
esp_err_t i2s_channel_disable(i2s_chan_handle_t handle)
Disable the i2s channel.
备注
Only allowed to be called when the channel state is READY / RUNNING, (i.e., channel has been initialized) the channel will enter READY state once it is disabled successfully.
备注
Disable the channel can stop the I2S communication on hardware. It will stop bclk and ws signal but not mclk signal
- 参数
handle – [in] I2S channel handler
- 返回
ESP_OK Stop successfully
ESP_ERR_INVALID_ARG NULL pointer
ESP_ERR_INVALID_STATE This channel has not stated
-
esp_err_t i2s_channel_write(i2s_chan_handle_t handle, const void *src, size_t size, size_t *bytes_written, uint32_t timeout_ms)
I2S write data.
备注
Only allowed to be called when the channel state is RUNNING, (i.e., tx channel has been started and is not writing now) but the RUNNING only stands for the software state, it doesn’t mean there is no the signal transporting on line.
- 参数
handle – [in] I2S channel handler
src – [in] The pointer of sent data buffer
size – [in] Max data buffer length
bytes_written – [out] Byte number that actually be sent
timeout_ms – [in] Max block time
- 返回
ESP_OK Write successfully
ESP_ERR_INVALID_ARG NULL pointer or this handle is not tx handle
ESP_ERR_TIMEOUT Writing timeout, no writing event received from ISR within ticks_to_wait
ESP_ERR_INVALID_STATE I2S is not ready to write
-
esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, size_t *bytes_read, uint32_t timeout_ms)
I2S read data.
备注
Only allowed to be called when the channel state is RUNNING but the RUNNING only stands for the software state, it doesn’t mean there is no the signal transporting on line.
- 参数
handle – [in] I2S channel handler
dest – [in] The pointer of receiving data buffer
size – [in] Max data buffer length
bytes_read – [out] Byte number that actually be read
timeout_ms – [in] Max block time
- 返回
ESP_OK Read successfully
ESP_ERR_INVALID_ARG NULL pointer or this handle is not rx handle
ESP_ERR_TIMEOUT Reading timeout, no reading event received from ISR within ticks_to_wait
ESP_ERR_INVALID_STATE I2S is not ready to read
-
esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2s_event_callbacks_t *callbacks, void *user_data)
Set event callbacks for I2S channel.
备注
Only allowed to be called when the channel state is REGISTARED / READY, (i.e., before channel starts)
备注
User can deregister a previously registered callback by calling this function and setting the callback member in the
callbacks
structure to NULL.备注
When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. The variables used in the function should be in the SRAM as well. The
user_data
should also reside in SRAM or internal RAM as well.- 参数
handle – [in] I2S channel handler
callbacks – [in] Group of callback functions
user_data – [in] User data, which will be passed to callback functions directly
- 返回
ESP_OK Set event callbacks successfully
ESP_ERR_INVALID_ARG Set event callbacks failed because of invalid argument
ESP_ERR_INVALID_STATE Set event callbacks failed because the current channel state is not REGISTARED or READY
Structures
-
struct i2s_event_callbacks_t
Group of I2S callbacks.
备注
The callbacks are all running under ISR environment
备注
When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. The variables used in the function should be in the SRAM as well.
Public Members
-
i2s_isr_callback_t on_recv
Callback of data received event, only for rx channel The event data includes DMA buffer address and size that just finished receiving data
-
i2s_isr_callback_t on_recv_q_ovf
Callback of receiving queue overflowed event, only for rx channel The event data includes buffer size that has been overwritten
-
i2s_isr_callback_t on_sent
Callback of data sent event, only for tx channel The event data includes DMA buffer address and size that just finished sending data
-
i2s_isr_callback_t on_send_q_ovf
Callback of sending queue overflowed event, only for tx channel The event data includes buffer size that has been overwritten
-
i2s_isr_callback_t on_recv
-
struct i2s_chan_config_t
I2S controller channel configuration.
Public Members
-
i2s_port_t id
I2S port id
-
i2s_role_t role
I2S role, I2S_ROLE_MASTER or I2S_ROLE_SLAVE
-
uint32_t dma_desc_num
I2S DMA buffer number, it is also the number of DMA descriptor
-
uint32_t dma_frame_num
I2S frame number in one DMA buffer. One frame means one-time sample data in all slots, it should be the multiple of ‘3’ when the data bit width is 24.
-
bool auto_clear
Set to auto clear DMA TX buffer, i2s will always send zero automatically if no data to send
-
i2s_port_t id
-
struct i2s_chan_info_t
I2S channel information.
Public Members
-
i2s_port_t id
I2S port id
-
i2s_role_t role
I2S role, I2S_ROLE_MASTER or I2S_ROLE_SLAVE
-
i2s_comm_mode_t mode
I2S channel communication mode
-
i2s_chan_handle_t pair_chan
I2S pair channel handle in duplex mode, always NULL in simplex mode
-
i2s_port_t id
Macros
-
I2S_CHANNEL_DEFAULT_CONFIG(i2s_num, i2s_role)
get default I2S property
-
I2S_GPIO_UNUSED
Used in i2s_gpio_config_t for signals which are not used
I2S 类型
Header File
Structures
-
struct i2s_event_data_t
Event structure used in I2S event queue.
Public Members
-
void *data
The pointer of DMA buffer that just finished sending or receiving for
on_recv
andon_sent
callback NULL foron_recv_q_ovf
andon_send_q_ovf
callback
-
size_t size
The buffer size of DMA buffer when success to send or receive, also the buffer size that dropped when queue overflow. It is related to the dma_frame_num and data_bit_width, typically it is fixed when data_bit_width is not changed.
-
void *data
Type Definitions
-
typedef struct i2s_channel_obj_t *i2s_chan_handle_t
i2s channel object handle, the control unit of the i2s driver
-
typedef bool (*i2s_isr_callback_t)(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
I2S event callback.
- Param handle
[in] I2S channel handle, created from
i2s_new_channel()
- Param event
[in] I2S event data
- Param user_ctx
[in] User registered context, passed from
i2s_channel_register_event_callback()
- Return
Whether a high priority task has been waken up by this callback function
Enumerations
-
enum i2s_port_t
I2S controller port number, the max port number is (SOC_I2S_NUM -1).
Values:
-
enumerator I2S_NUM_0
I2S controller port 0
-
enumerator I2S_NUM_AUTO
Select whichever port is available
-
enumerator I2S_NUM_0
-
enum i2s_comm_mode_t
I2S controller communication mode.
Values:
-
enumerator I2S_COMM_MODE_STD
I2S controller using standard communication mode, support philips/MSB/PCM format
-
enumerator I2S_COMM_MODE_NONE
Unspecified I2S controller mode
-
enumerator I2S_COMM_MODE_STD
-
enum i2s_mclk_multiple_t
The multiple of mclk to sample rate.
Values:
-
enumerator I2S_MCLK_MULTIPLE_128
mclk = sample_rate * 128
-
enumerator I2S_MCLK_MULTIPLE_256
mclk = sample_rate * 256
-
enumerator I2S_MCLK_MULTIPLE_384
mclk = sample_rate * 384
-
enumerator I2S_MCLK_MULTIPLE_512
mclk = sample_rate * 512
-
enumerator I2S_MCLK_MULTIPLE_128
Header File
Type Definitions
-
typedef soc_periph_i2s_clk_src_t i2s_clock_src_t
I2S clock source
Enumerations
-
enum i2s_slot_mode_t
I2S channel slot mode.
Values:
-
enumerator I2S_SLOT_MODE_MONO
I2S channel slot format mono, transmit same data in all slots for tx mode, only receive the data in the first slots for rx mode.
-
enumerator I2S_SLOT_MODE_STEREO
I2S channel slot format stereo, transmit different data in different slots for tx mode, receive the data in all slots for rx mode.
-
enumerator I2S_SLOT_MODE_MONO
-
enum i2s_dir_t
I2S channel direction.
Values:
-
enumerator I2S_DIR_RX
I2S channel direction RX
-
enumerator I2S_DIR_TX
I2S channel direction TX
-
enumerator I2S_DIR_RX
-
enum i2s_role_t
I2S controller role.
Values:
-
enumerator I2S_ROLE_MASTER
I2S controller master role, bclk and ws signal will be set to output
-
enumerator I2S_ROLE_SLAVE
I2S controller slave role, bclk and ws signal will be set to input
-
enumerator I2S_ROLE_MASTER
-
enum i2s_data_bit_width_t
Available data bit width in one slot.
Values:
-
enumerator I2S_DATA_BIT_WIDTH_8BIT
I2S channel data bit-width: 8
-
enumerator I2S_DATA_BIT_WIDTH_16BIT
I2S channel data bit-width: 16
-
enumerator I2S_DATA_BIT_WIDTH_24BIT
I2S channel data bit-width: 24
-
enumerator I2S_DATA_BIT_WIDTH_32BIT
I2S channel data bit-width: 32
-
enumerator I2S_DATA_BIT_WIDTH_8BIT
-
enum i2s_slot_bit_width_t
Total slot bit width in one slot.
Values:
-
enumerator I2S_SLOT_BIT_WIDTH_AUTO
I2S channel slot bit-width equals to data bit-width
-
enumerator I2S_SLOT_BIT_WIDTH_8BIT
I2S channel slot bit-width: 8
-
enumerator I2S_SLOT_BIT_WIDTH_16BIT
I2S channel slot bit-width: 16
-
enumerator I2S_SLOT_BIT_WIDTH_24BIT
I2S channel slot bit-width: 24
-
enumerator I2S_SLOT_BIT_WIDTH_32BIT
I2S channel slot bit-width: 32
-
enumerator I2S_SLOT_BIT_WIDTH_AUTO
-
enum i2s_std_slot_mask_t
I2S slot select in standard mode.
备注
It has different meanings in tx/rx/mono/stereo mode, and it may have differen behaviors on different targets For the details, please refer to the I2S API reference
Values:
-
enumerator I2S_STD_SLOT_LEFT
I2S transmits or receives left slot
-
enumerator I2S_STD_SLOT_RIGHT
I2S transmits or receives right slot
-
enumerator I2S_STD_SLOT_BOTH
I2S transmits or receives both left and right slot
-
enumerator I2S_STD_SLOT_LEFT
-
enum i2s_pdm_slot_mask_t
I2S slot select in PDM mode.
Values:
-
enumerator I2S_PDM_SLOT_RIGHT
I2S PDM only transmits or receives the PDM device whose ‘select’ pin is pulled up
-
enumerator I2S_PDM_SLOT_LEFT
I2S PDM only transmits or receives the PDM device whose ‘select’ pin is pulled down
-
enumerator I2S_PDM_SLOT_BOTH
I2S PDM transmits or receives both two slots
-
enumerator I2S_PDM_SLOT_RIGHT