Merge branch 'revert/i2s_apll_pm_type_v5.4' into 'release/v5.4'

fix(i2s): lock APB when using apll with DFS feature (v5.4)

See merge request espressif/esp-idf!37144
This commit is contained in:
morris 2025-02-20 21:33:39 +08:00
commit a043a96abb
11 changed files with 107 additions and 14 deletions

View File

@ -1479,6 +1479,11 @@ static esp_err_t i2s_init_legacy(i2s_port_t i2s_num, int intr_alloc_flag)
/* Create power management lock */ /* Create power management lock */
#ifdef CONFIG_PM_ENABLE #ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_lock = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_lock = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL
if (p_i2s[i2s_num]->use_apll) {
pm_lock = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_lock, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock), TAG, "I2S pm lock error"); ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_lock, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock), TAG, "I2S pm lock error");
#endif //CONFIG_PM_ENABLE #endif //CONFIG_PM_ENABLE

View File

@ -228,7 +228,7 @@ esp_err_t dac_continuous_new_channels(const dac_continuous_config_t *cont_cfg, d
/* Create PM lock */ /* Create PM lock */
#if CONFIG_PM_ENABLE #if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_lock_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_lock_type = cont_cfg->clk_src == DAC_DIGI_CLK_SRC_APLL ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_APB_FREQ_MAX;
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, "dac_driver", &handle->pm_lock), err3, TAG, "Failed to create DAC pm lock"); ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, "dac_driver", &handle->pm_lock), err3, TAG, "Failed to create DAC pm lock");
#endif #endif
handle->chan_cnt = __builtin_popcount(cont_cfg->chan_mask); handle->chan_cnt = __builtin_popcount(cont_cfg->chan_mask);

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -202,6 +202,13 @@ esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_t
#ifdef CONFIG_PM_ENABLE #ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (pdm_tx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed"); ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
#endif #endif
@ -249,8 +256,10 @@ esp_err_t i2s_channel_reconfig_pdm_tx_clock(i2s_chan_handle_t handle, const i2s_
if (pdm_tx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) { if (pdm_tx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed"); ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL #if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) { if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP; pm_type = ESP_PM_NO_LIGHT_SLEEP;
} }
#endif // SOC_I2S_SUPPORTS_APLL #endif // SOC_I2S_SUPPORTS_APLL
@ -488,8 +497,10 @@ esp_err_t i2s_channel_init_pdm_rx_mode(i2s_chan_handle_t handle, const i2s_pdm_r
#ifdef CONFIG_PM_ENABLE #ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL #if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) { if (pdm_rx_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP; pm_type = ESP_PM_NO_LIGHT_SLEEP;
} }
#endif // SOC_I2S_SUPPORTS_APLL #endif // SOC_I2S_SUPPORTS_APLL
@ -540,8 +551,10 @@ esp_err_t i2s_channel_reconfig_pdm_rx_clock(i2s_chan_handle_t handle, const i2s_
if (pdm_rx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) { if (pdm_rx_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed"); ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL #if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) { if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP; pm_type = ESP_PM_NO_LIGHT_SLEEP;
} }
#endif // SOC_I2S_SUPPORTS_APLL #endif // SOC_I2S_SUPPORTS_APLL

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -245,6 +245,13 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
#ifdef CONFIG_PM_ENABLE #ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed"); ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
#endif #endif
@ -293,8 +300,10 @@ esp_err_t i2s_channel_reconfig_std_clock(i2s_chan_handle_t handle, const i2s_std
if (std_cfg->clk_cfg.clk_src != clk_cfg->clk_src) { if (std_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed"); ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL #if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) { if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP; pm_type = ESP_PM_NO_LIGHT_SLEEP;
} }
#endif // SOC_I2S_SUPPORTS_APLL #endif // SOC_I2S_SUPPORTS_APLL

View File

@ -254,6 +254,13 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf
#endif #endif
#ifdef CONFIG_PM_ENABLE #ifdef CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (tdm_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP;
}
#endif // SOC_I2S_SUPPORTS_APLL
ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed"); ESP_RETURN_ON_ERROR(esp_pm_lock_create(pm_type, 0, "i2s_driver", &handle->pm_lock), TAG, "I2S pm lock create failed");
#endif #endif
@ -302,8 +309,10 @@ esp_err_t i2s_channel_reconfig_tdm_clock(i2s_chan_handle_t handle, const i2s_tdm
if (tdm_cfg->clk_cfg.clk_src != clk_cfg->clk_src) { if (tdm_cfg->clk_cfg.clk_src != clk_cfg->clk_src) {
ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed"); ESP_GOTO_ON_ERROR(esp_pm_lock_delete(handle->pm_lock), err, TAG, "I2S delete old pm lock failed");
esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX; esp_pm_lock_type_t pm_type = ESP_PM_APB_FREQ_MAX;
#if SOC_I2S_SUPPORTS_APLL #if SOC_I2S_SUPPORTS_APLL && SOC_I2S_HW_VERSION_2
if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) { if (clk_cfg->clk_src == I2S_CLK_SRC_APLL) {
/* Only I2S HW 2 supports to adjust APB frequency while using APLL clock source
* HW 1 will have timing issue because the DMA and I2S are under different clock domains */
pm_type = ESP_PM_NO_LIGHT_SLEEP; pm_type = ESP_PM_NO_LIGHT_SLEEP;
} }
#endif // SOC_I2S_SUPPORTS_APLL #endif // SOC_I2S_SUPPORTS_APLL

View File

@ -667,7 +667,7 @@ static esp_err_t i2s_lcd_select_periph_clock(esp_lcd_i80_bus_handle_t bus, lcd_c
// create pm lock based on different clock source // create pm lock based on different clock source
// clock sources like PLL and XTAL will be turned off in light sleep // clock sources like PLL and XTAL will be turned off in light sleep
#if CONFIG_PM_ENABLE #if CONFIG_PM_ENABLE
ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i80_bus_lcd", &bus->pm_lock), TAG, "create pm lock failed"); ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i80_bus_lcd", &bus->pm_lock), TAG, "create pm lock failed");
#endif #endif
return ESP_OK; return ESP_OK;
} }

View File

@ -96,7 +96,7 @@ Power Management
When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the clock source of DAC before entering Light-sleep mode, thus potential influence to the DAC signals may lead to false data conversion. When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the clock source of DAC before entering Light-sleep mode, thus potential influence to the DAC signals may lead to false data conversion.
When using DAC driver in continuous mode, it can prevent the system from changing or stopping the clock source in DMA or cosine mode by acquiring a power management lock. The power lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`. Whenever the DAC is converting (i.e., DMA or cosine wave generator is working), the driver guarantees that the power management lock is acquired after calling :cpp:func:`dac_continuous_enable`. Likewise, the driver will release the lock when :cpp:func:`dac_continuous_disable` is called. When using DAC driver in continuous mode, it can prevent the system from changing or stopping the clock source in DMA or cosine mode by acquiring a power management lock. When the clock source is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`. When the clock source is APLL (only in DMA mode), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the DAC is converting (i.e., DMA or cosine wave generator is working), the driver guarantees that the power management lock is acquired after calling :cpp:func:`dac_continuous_enable`. Likewise, the driver will release the lock when :cpp:func:`dac_continuous_disable` is called.
IRAM Safe IRAM Safe
^^^^^^^^^ ^^^^^^^^^

View File

@ -243,7 +243,7 @@ Power Management
When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the source clock of I2S before entering Light-sleep, thus potentially changing the I2S signals and leading to transmitting or receiving invalid data. When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the source clock of I2S before entering Light-sleep, thus potentially changing the I2S signals and leading to transmitting or receiving invalid data.
The I2S driver can prevent the system from changing or stopping the source clock by acquiring a power management lock. The power lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`. Whenever the user is reading or writing via I2S (i.e., calling :cpp:func:`i2s_channel_read` or :cpp:func:`i2s_channel_write`), the driver guarantees that the power management lock is acquired. Likewise, the driver releases the lock after the reading or writing finishes. The I2S driver can prevent the system from changing or stopping the source clock by acquiring a power management lock. When the source clock is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX` and when the source clock is APLL (if supported), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the user is reading or writing via I2S (i.e., calling :cpp:func:`i2s_channel_read` or :cpp:func:`i2s_channel_write`), the driver guarantees that the power management lock is acquired. Likewise, the driver releases the lock after the reading or writing finishes.
.. only:: SOC_I2S_SUPPORT_SLEEP_RETENTION .. only:: SOC_I2S_SUPPORT_SLEEP_RETENTION

View File

@ -96,7 +96,7 @@ DAC 外设中包含一个余弦波发生器,可以在通道上产生余弦波
启用电源管理时(即开启 :ref:`CONFIG_PM_ENABLE`),系统会在进入 Light-sleep 模式前调整或停止 DAC 时钟源,这可能会影响 DAC 信号,从而导致数据无法正确转换。 启用电源管理时(即开启 :ref:`CONFIG_PM_ENABLE`),系统会在进入 Light-sleep 模式前调整或停止 DAC 时钟源,这可能会影响 DAC 信号,从而导致数据无法正确转换。
在连续模式下使用 DAC 驱动时,可以通过获取电源管理锁来防止系统在 DMA 或余弦波模式下改变或停止时钟源。电源锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`。在进行 DAC 转换时(即 DMA 或余弦波发生器运行时),驱动程序会保证在调用 :cpp:func:`dac_continuous_enable` 后获取电源管理锁。同样地,在调用 :cpp:func:`dac_continuous_disable` 时,驱动程序会释放锁。 在连续模式下使用 DAC 驱动时,可以通过获取电源管理锁来防止系统在 DMA 或余弦波模式下改变或停止时钟源。时钟源为 APB 时,锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`。时钟源为 APLL 时(仅在 DMA 模式下),锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`。在进行 DAC 转换时(即 DMA 或余弦波发生器运行时),驱动程序会保证在调用 :cpp:func:`dac_continuous_enable` 后获取电源管理锁。同样地,在调用 :cpp:func:`dac_continuous_disable` 时,驱动程序会释放锁。
IRAM 安全 IRAM 安全
^^^^^^^^^ ^^^^^^^^^

View File

@ -237,7 +237,7 @@ I2S 驱动中的资源可分为三个级别:
电源管理启用(即开启 :ref:`CONFIG_PM_ENABLE`)时,系统将在进入 Light-sleep 前调整或停止 I2S 时钟源,这可能会影响 I2S 信号,从而导致传输或接收的数据无效。 电源管理启用(即开启 :ref:`CONFIG_PM_ENABLE`)时,系统将在进入 Light-sleep 前调整或停止 I2S 时钟源,这可能会影响 I2S 信号,从而导致传输或接收的数据无效。
I2S 驱动可以获取电源管理锁,从而防止系统设置更改或时钟源被禁用。电源锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`。用户通过 I2S 读写时(即调用 :cpp:func:`i2s_channel_read`:cpp:func:`i2s_channel_write`),驱动程序将获取电源管理锁,并在读写完成后释放锁。 I2S 驱动可以获取电源管理锁,从而防止系统设置更改或时钟源被禁用。时钟源为 APB 时,锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`。时钟源为 APLL若支持锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`。用户通过 I2S 读写时(即调用 :cpp:func:`i2s_channel_read`:cpp:func:`i2s_channel_write`),驱动程序将获取电源管理锁,并在读写完成后释放锁。
.. only:: SOC_I2S_SUPPORT_SLEEP_RETENTION .. only:: SOC_I2S_SUPPORT_SLEEP_RETENTION

View File

@ -5,7 +5,7 @@
(See the README.md file in the upper level 'examples' directory for more information about examples.) (See the README.md file in the upper level 'examples' directory for more information about examples.)
I2S on `ESP32S3` and `ESP32C3` supports [TDM mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#tdm-mode), in which multiple slots can be transmitted by standard I2S connection. The targets that listed on the top support [TDM mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#tdm-mode), in which multiple slots can be transmitted by standard I2S connection.
This example demonstrates how to use I2S TDM mode to record 4 MICs connected to [ES7210](http://www.everest-semi.com/pdf/ES7210%20PB.pdf) codec. ES7210 has 4 TDM modes, which are `ES7210_I2S_FMT_I2S` `ES7210_I2S_FMT_LJ` `ES7210_I2S_FMT_DSP_A` and `ES7210_I2S_FMT_DSP_B`, and they are all supported by I2S TDM driver. Relation between ES7210 TDM modes and I2S Driver TDM modes is shown in the following table. This example demonstrates how to use I2S TDM mode to record 4 MICs connected to [ES7210](http://www.everest-semi.com/pdf/ES7210%20PB.pdf) codec. ES7210 has 4 TDM modes, which are `ES7210_I2S_FMT_I2S` `ES7210_I2S_FMT_LJ` `ES7210_I2S_FMT_DSP_A` and `ES7210_I2S_FMT_DSP_B`, and they are all supported by I2S TDM driver. Relation between ES7210 TDM modes and I2S Driver TDM modes is shown in the following table.
@ -91,6 +91,63 @@ I (10431) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulld
I (10431) example: You can now safely remove the card, recorded file is /RECORD.WAV I (10431) example: You can now safely remove the card, recorded file is /RECORD.WAV
``` ```
## Application Notes
TDM mode is widely used in multi-channel scenario. Some ADC/DAC (e.g., ES7210 and ES7243) that supports TDM can be cascaded to sample more channels synchronously.
Take ES7210 for example, it is a 4-channel audio ADC that supports TDM. And we can chain upto four ES7210 to form a 16-channel recorder, meanwhile it only needs one I2S port.
You can refer to the connection as follow:
```
┌───────────────┐
│ ESP │
│ │
│ MCLK ├───────────┬──────────────────────────────┬──────────────────────────────┬──────────────────────────────┐
│ │ │ │ │ │
│ BCLK ├────────┬──┼───────────────────────────┬──┼───────────────────────────┬──┼───────────────────────────┐ │
│ │ │ │ │ │ │ │ │ │
│ WS ├─────┬──┼──┼────────────────────────┬──┼──┼────────────────────────┬──┼──┼────────────────────────┐ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ DOUT │ │ │ │ ┌───────────────┐ │ │ │ ┌───────────────┐ │ │ │ ┌───────────────┐ │ │ │ ┌───────────────┐
│ │ │ │ │ │ ES7210_0 │ │ │ │ │ ES7210_1 │ │ │ │ │ ES7210_2 │ │ │ │ │ ES7210_3 │
│ DIN │◄─┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
└───────────────┘ │ │ │ └───►│ MCLK │ │ │ └───►│ MCLK │ │ │ └───►│ MCLK │ │ │ └───►│ MCLK │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ └──────►│ SCLK │ │ └──────►│ SCLK │ │ └──────►│ SCLK │ │ └──────►│ SCLK │
│ │ │ │ │ │ │ │ │ │ │ │ │
│ └─────────►│ LRCK │ └─────────►│ LRCK │ └─────────►│ LRCK │ └─────────►│ LRCK │
│ │ │ │ │ │ │ │ │
└─────────────┤ TDMOUT │ ┌──────────┤ TDMOUT │ ┌──────────┤ TDMOUT │ ┌──────────┤ TDMOUT │
│ │ │ │ │ │ │ │ │ │ │
┌───►│ TDMIN │ │ ┌───►│ TDMIN │ │ ┌───►│ TDMIN │ │ │ TDMIN │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ └───────────────┘ │ │ └───────────────┘ │ │ └───────────────┘ │ └───────────────┘
│ │ │ │ │ │
└────────────────────────┘ └────────────────────────┘ └────────────────────────┘
```
### How A Frame Formed in This Cascaded Case
Generally, all the ES7210 will send the data that sampled by itself at the beginning of a frame, and then catenate the backward ES7210 data behind. In another word, ES7210_0 will send the data in the order ES7210_0 -> ES7210_1 -> ES7210_2 -> ES7210_3.
See the detailed steps as follows:
1. First 4 channels of the 16 channels:
- ES7210_3 send data that sampled by itself to ES7210_2;
- ES7210_2 send data that sampled by itself to ES7210_1, store the data sent from ES7210_3;
- ES7210_1 send data that sampled by itself to ES7210_0, store the data sent from ES7210_2;
- ES7210_0 send data that sampled by itself to ESP, store the data sent from ES7210_1;
2. Second 4 channels of the 16 channels:
- ES7210_2 send the stored data (i.e., sampled by ES7210_3) to ES7210_1;
- ES7210_1 send the stored data (i.e., sampled by ES7210_2) to ES7210_0, and store the data from ES7210_2 (i.e., sampled by ES7210_3);
- ES7210_0 send the stored data (i.e., sampled by ES7210_1) to ESP, and store the data sent from ES7210_1 (i.e., sampled by ES7210_2);
3. Third 4 channels of the 16 channels:
- ES7210_1 send the stored data (i.e., sampled by ES7210_3) to ES7210_0;
- ES7210_0 send the stored data (i.e., sampled by ES7210_2) to ESP, and store the data from ES7210_1 (i.e., sampled by ES7210_3);
4. Last 4 channels of the 16 channels:
- ES7210_0 send the stored data (i.e., sampled by ES7210_3) to ESP;
## Troubleshooting ## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.