Skip to content

Commit a5bd9d2

Browse files
committed
Merge branch 'feature/i2s_support_merge_simplex_to_duplex' into 'master'
feat(i2s): support to lazy constitute full-duplex mode Closes IDF-11696 See merge request espressif/esp-idf!39428
2 parents 2044fba + e3bf25d commit a5bd9d2

File tree

9 files changed

+384
-82
lines changed

9 files changed

+384
-82
lines changed

components/esp_driver_i2s/i2s_common.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,12 +297,6 @@ static inline bool i2s_take_available_channel(i2s_controller_t *i2s_obj, uint8_t
297297
{
298298
bool is_available = false;
299299

300-
#if SOC_I2S_HW_VERSION_1
301-
/* In ESP32 and ESP32-S2, tx channel and rx channel are not totally separated
302-
* Take both two channels in case one channel can affect another
303-
*/
304-
chan_search_mask = I2S_DIR_RX | I2S_DIR_TX;
305-
#endif
306300
portENTER_CRITICAL(&g_i2s.spinlock);
307301
if (!(chan_search_mask & i2s_obj->chan_occupancy)) {
308302
i2s_obj->chan_occupancy |= chan_search_mask;

components/esp_driver_i2s/i2s_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ struct i2s_channel_obj_t {
164164
bool is_etm_stop: 1; /*!< Whether stop by etm tasks */
165165
bool is_raw_pdm: 1; /*!< Flag of whether send/receive PDM in raw data, i.e., no PCM2PDM/PDM2PCM filter enabled */
166166
bool is_external: 1; /*!< Whether use external clock */
167+
bool full_duplex_slave: 1; /*!< whether the channel is forced to switch to slave role for full duplex */
167168
#if SOC_I2S_SUPPORTS_APLL
168169
bool apll_en: 1; /*!< Flag of whether APLL enabled */
169170
#endif

components/esp_driver_i2s/i2s_std.c

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std
3333
uint32_t slot_bits = (slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO) ||
3434
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
3535
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
36+
slot_cfg->slot_bit_width = slot_bits;
3637
/* Calculate multiple
3738
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
38-
if (handle->role == I2S_ROLE_MASTER) {
39+
if (handle->role == I2S_ROLE_MASTER || handle->full_duplex_slave) {
3940
clk_info->bclk = rate * handle->total_slot * slot_bits;
4041
clk_info->mclk = rate * clk_cfg->mclk_multiple;
4142
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
@@ -122,18 +123,13 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c
122123
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, buf_size),
123124
TAG, "allocate memory for dma descriptor failed");
124125
}
125-
bool is_slave = handle->role == I2S_ROLE_SLAVE;
126126
/* Share bck and ws signal in full-duplex mode */
127127
if (handle->controller->full_duplex) {
128128
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
129-
/* Since bck and ws are shared, only tx or rx can be master
130-
Force to set rx as slave to avoid conflict of clock signal */
131-
if (handle->dir == I2S_DIR_RX) {
132-
is_slave = true;
133-
}
134129
} else {
135130
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
136131
}
132+
bool is_slave = handle->role == I2S_ROLE_SLAVE;
137133

138134
portENTER_CRITICAL(&g_i2s.spinlock);
139135
/* Configure the hardware to apply STD format */
@@ -178,43 +174,101 @@ static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_c
178174
/* Set mclk pin */
179175
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(handle, id, gpio_cfg->mclk, std_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
180176

181-
if (handle->role == I2S_ROLE_SLAVE) {
182-
/* For "tx + slave" mode, select TX signal index for ws and bck */
183-
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
184177
#if SOC_I2S_HW_VERSION_2
178+
/* Bind the MCLK signal to the TX or RX clock source */
179+
if (!handle->controller->full_duplex) {
180+
if (handle->dir == I2S_DIR_TX) {
185181
I2S_CLOCK_SRC_ATOMIC() {
186182
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
187183
}
188-
#endif
189-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
190-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
191-
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
192184
} else {
193-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
194-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
185+
I2S_CLOCK_SRC_ATOMIC() {
186+
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
187+
}
195188
}
196-
} else {
197-
/* For "rx + master" mode, select RX signal index for ws and bck */
198-
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
199-
#if SOC_I2S_HW_VERSION_2
189+
} else if (handle->role == I2S_ROLE_MASTER) {
190+
if (handle->dir == I2S_DIR_TX) {
191+
I2S_CLOCK_SRC_ATOMIC() {
192+
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
193+
}
194+
} else {
200195
I2S_CLOCK_SRC_ATOMIC() {
201196
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
202197
}
198+
}
199+
}
203200
#endif
204-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
205-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
206-
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
201+
202+
uint32_t ws_sig = 0;
203+
uint32_t bck_sig = 0;
204+
bool is_input = handle->role == I2S_ROLE_SLAVE;
205+
if (handle->role == I2S_ROLE_SLAVE) {
206+
// Assign slave signals
207+
if (handle->dir == I2S_DIR_TX) {
208+
ws_sig = i2s_periph_signal[id].s_tx_ws_sig;
209+
bck_sig = i2s_periph_signal[id].s_tx_bck_sig;
210+
} else {
211+
ws_sig = i2s_periph_signal[id].s_rx_ws_sig;
212+
bck_sig = i2s_periph_signal[id].s_rx_bck_sig;
213+
}
214+
} else {
215+
// Assign master signals
216+
if (handle->dir == I2S_DIR_TX) {
217+
ws_sig = i2s_periph_signal[id].m_tx_ws_sig;
218+
bck_sig = i2s_periph_signal[id].m_tx_bck_sig;
207219
} else {
208-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
209-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
220+
ws_sig = i2s_periph_signal[id].m_rx_ws_sig;
221+
bck_sig = i2s_periph_signal[id].m_rx_bck_sig;
210222
}
211223
}
224+
i2s_gpio_check_and_set(handle, gpio_cfg->ws, ws_sig, is_input, gpio_cfg->invert_flags.ws_inv);
225+
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, bck_sig, is_input, gpio_cfg->invert_flags.bclk_inv);
226+
212227
/* Update the mode info: gpio configuration */
213228
memcpy(&(std_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_std_gpio_config_t));
214229

215230
return ESP_OK;
216231
}
217232

233+
static esp_err_t s_i2s_channel_try_to_constitude_std_duplex(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
234+
{
235+
/* Get another direction handle */
236+
i2s_chan_handle_t another_handle = handle->dir == I2S_DIR_RX ? handle->controller->tx_chan : handle->controller->rx_chan;
237+
/* Condition: 1. Another direction channel is registered
238+
* 2. Not a full-duplex channel yet
239+
* 3. Another channel is initialized, try to compare the configurations */
240+
if (another_handle && another_handle->state >= I2S_CHAN_STATE_READY) {
241+
/* Judge if the two channels can constitute full-duplex */
242+
if (!handle->controller->full_duplex) {
243+
i2s_std_config_t curr_cfg = *std_cfg;
244+
/* Override the slot bit width to the actual slot bit width */
245+
curr_cfg.slot_cfg.slot_bit_width = (int)curr_cfg.slot_cfg.slot_bit_width < (int)curr_cfg.slot_cfg.data_bit_width ?
246+
curr_cfg.slot_cfg.data_bit_width : curr_cfg.slot_cfg.slot_bit_width;
247+
/* Compare the hardware configurations of the two channels, constitute the full-duplex if they are the same */
248+
if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_std_config_t)) == 0) {
249+
handle->controller->full_duplex = true;
250+
ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id);
251+
}
252+
#if SOC_I2S_HW_VERSION_1
253+
else {
254+
ESP_LOGE(TAG, "Can't set different channel configurations on a same port");
255+
return ESP_ERR_INVALID_ARG;
256+
}
257+
#endif
258+
}
259+
/* Switch to the slave role if needed */
260+
if (handle->controller->full_duplex &&
261+
handle->role == I2S_ROLE_MASTER &&
262+
another_handle->role == I2S_ROLE_MASTER) {
263+
/* The later initialized channel must be slave for full duplex */
264+
handle->role = I2S_ROLE_SLAVE;
265+
handle->full_duplex_slave = true;
266+
}
267+
}
268+
269+
return ESP_OK;
270+
}
271+
218272
esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_config_t *std_cfg)
219273
{
220274
#if CONFIG_I2S_ENABLE_DEBUG_LOG
@@ -232,6 +286,11 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
232286
handle->mode_info = calloc(1, sizeof(i2s_std_config_t));
233287
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
234288
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_REGISTER, ESP_ERR_INVALID_STATE, err, TAG, "the channel has initialized already");
289+
/* Try to constitute full-duplex mode if the STD configuration is totally same as another channel */
290+
ret = s_i2s_channel_try_to_constitude_std_duplex(handle, std_cfg);
291+
#if SOC_I2S_HW_VERSION_1
292+
ESP_GOTO_ON_ERROR(ret, err, TAG, "Failed to constitute full-duplex mode");
293+
#endif
235294
/* i2s_set_std_slot should be called before i2s_set_std_clock while initializing, because clock is relay on the slot */
236295
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
237296
#if SOC_I2S_SUPPORTS_APLL

components/esp_driver_i2s/i2s_tdm.c

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm
3434
uint32_t slot_bits = (slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO) ||
3535
((int)slot_cfg->slot_bit_width < (int)slot_cfg->data_bit_width) ?
3636
slot_cfg->data_bit_width : slot_cfg->slot_bit_width;
37+
slot_cfg->slot_bit_width = slot_bits;
3738
/* Calculate multiple
3839
* Fmclk = bck_div*fbck = fsclk/(mclk_div+b/a) */
39-
if (handle->role == I2S_ROLE_MASTER) {
40+
if (handle->role == I2S_ROLE_MASTER || handle->full_duplex_slave) {
4041
clk_info->bclk = rate * handle->total_slot * slot_bits;
4142
clk_info->mclk = rate * clk_cfg->mclk_multiple;
4243
clk_info->bclk_div = clk_info->mclk / clk_info->bclk;
@@ -126,18 +127,13 @@ static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_c
126127
ESP_RETURN_ON_ERROR(i2s_alloc_dma_desc(handle, buf_size),
127128
TAG, "allocate memory for dma descriptor failed");
128129
}
129-
bool is_slave = handle->role == I2S_ROLE_SLAVE;
130130
/* Share bck and ws signal in full-duplex mode */
131131
if (handle->controller->full_duplex) {
132132
i2s_ll_share_bck_ws(handle->controller->hal.dev, true);
133-
/* Since bck and ws are shared, only tx or rx can be master
134-
Force to set rx as slave to avoid conflict of clock signal */
135-
if (handle->dir == I2S_DIR_RX) {
136-
is_slave = true;
137-
}
138133
} else {
139134
i2s_ll_share_bck_ws(handle->controller->hal.dev, false);
140135
}
136+
bool is_slave = handle->role == I2S_ROLE_SLAVE;
141137

142138
portENTER_CRITICAL(&g_i2s.spinlock);
143139
/* Configure the hardware to apply TDM format */
@@ -183,43 +179,92 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c
183179
/* Set mclk pin */
184180
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(handle, id, gpio_cfg->mclk, tdm_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
185181

186-
if (handle->role == I2S_ROLE_SLAVE) {
187-
/* For "tx + slave" mode, select TX signal index for ws and bck */
188-
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
189182
#if SOC_I2S_HW_VERSION_2
183+
/* Bind the MCLK signal to the TX or RX clock source */
184+
if (!handle->controller->full_duplex) {
185+
if (handle->dir == I2S_DIR_TX) {
190186
I2S_CLOCK_SRC_ATOMIC() {
191187
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
192188
}
193-
#endif
194-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_tx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
195-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_tx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
196-
/* For "tx + rx + slave" or "rx + slave" mode, select RX signal index for ws and bck */
197189
} else {
198-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].s_rx_ws_sig, true, gpio_cfg->invert_flags.ws_inv);
199-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
190+
I2S_CLOCK_SRC_ATOMIC() {
191+
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
192+
}
200193
}
201-
} else {
202-
/* For "rx + master" mode, select RX signal index for ws and bck */
203-
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
204-
#if SOC_I2S_HW_VERSION_2
194+
} else if (handle->role == I2S_ROLE_MASTER) {
195+
if (handle->dir == I2S_DIR_TX) {
196+
I2S_CLOCK_SRC_ATOMIC() {
197+
i2s_ll_mclk_bind_to_tx_clk(handle->controller->hal.dev);
198+
}
199+
} else {
205200
I2S_CLOCK_SRC_ATOMIC() {
206201
i2s_ll_mclk_bind_to_rx_clk(handle->controller->hal.dev);
207202
}
203+
}
204+
}
208205
#endif
209-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_rx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
210-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_rx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
211-
/* For "tx + rx + master" or "tx + master" mode, select TX signal index for ws and bck */
206+
207+
uint32_t ws_sig = 0;
208+
uint32_t bck_sig = 0;
209+
bool is_input = handle->role == I2S_ROLE_SLAVE;
210+
if (handle->role == I2S_ROLE_SLAVE) {
211+
// Assign slave signals
212+
if (handle->dir == I2S_DIR_TX) {
213+
ws_sig = i2s_periph_signal[id].s_tx_ws_sig;
214+
bck_sig = i2s_periph_signal[id].s_tx_bck_sig;
215+
} else {
216+
ws_sig = i2s_periph_signal[id].s_rx_ws_sig;
217+
bck_sig = i2s_periph_signal[id].s_rx_bck_sig;
218+
}
219+
} else {
220+
// Assign master signals
221+
if (handle->dir == I2S_DIR_TX) {
222+
ws_sig = i2s_periph_signal[id].m_tx_ws_sig;
223+
bck_sig = i2s_periph_signal[id].m_tx_bck_sig;
212224
} else {
213-
i2s_gpio_check_and_set(handle, gpio_cfg->ws, i2s_periph_signal[id].m_tx_ws_sig, false, gpio_cfg->invert_flags.ws_inv);
214-
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, i2s_periph_signal[id].m_tx_bck_sig, false, gpio_cfg->invert_flags.bclk_inv);
225+
ws_sig = i2s_periph_signal[id].m_rx_ws_sig;
226+
bck_sig = i2s_periph_signal[id].m_rx_bck_sig;
215227
}
216228
}
229+
i2s_gpio_check_and_set(handle, gpio_cfg->ws, ws_sig, is_input, gpio_cfg->invert_flags.ws_inv);
230+
i2s_gpio_check_and_set(handle, gpio_cfg->bclk, bck_sig, is_input, gpio_cfg->invert_flags.bclk_inv);
231+
217232
/* Update the mode info: gpio configuration */
218233
memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t));
219234

220235
return ESP_OK;
221236
}
222237

238+
static void s_i2s_channel_try_to_constitude_tdm_duplex(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
239+
{
240+
/* Get another direction handle */
241+
i2s_chan_handle_t another_handle = handle->dir == I2S_DIR_RX ? handle->controller->tx_chan : handle->controller->rx_chan;
242+
/* Condition: 1. Another direction channel is registered
243+
* 2. Not a full-duplex channel yet
244+
* 3. Another channel is initialized, try to compare the configurations */
245+
if (another_handle && another_handle->state >= I2S_CHAN_STATE_READY) {
246+
if (!handle->controller->full_duplex) {
247+
i2s_tdm_config_t curr_cfg = *tdm_cfg;
248+
/* Override the slot bit width to the actual slot bit width */
249+
curr_cfg.slot_cfg.slot_bit_width = (int)curr_cfg.slot_cfg.slot_bit_width < (int)curr_cfg.slot_cfg.data_bit_width ?
250+
curr_cfg.slot_cfg.data_bit_width : curr_cfg.slot_cfg.slot_bit_width;
251+
/* Compare the hardware configurations of the two channels, constitute the full-duplex if they are the same */
252+
if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_tdm_config_t)) == 0) {
253+
handle->controller->full_duplex = true;
254+
ESP_LOGD(TAG, "Constitude full-duplex on port %d", handle->controller->id);
255+
}
256+
}
257+
/* Switch to the slave role if needed */
258+
if (handle->controller->full_duplex &&
259+
handle->role == I2S_ROLE_MASTER &&
260+
another_handle->role == I2S_ROLE_MASTER) {
261+
/* The later initialized channel must be slave for full duplex */
262+
handle->role = I2S_ROLE_SLAVE;
263+
handle->full_duplex_slave = true;
264+
}
265+
}
266+
}
267+
223268
esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_config_t *tdm_cfg)
224269
{
225270
#if CONFIG_I2S_ENABLE_DEBUG_LOG
@@ -237,6 +282,8 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf
237282
}
238283
handle->mode_info = calloc(1, sizeof(i2s_tdm_config_t));
239284
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
285+
/* Try to constitute full-duplex mode if the TDM configuration is totally same as another channel */
286+
s_i2s_channel_try_to_constitude_tdm_duplex(handle, tdm_cfg);
240287
/* i2s_set_tdm_slot should be called before i2s_set_tdm_clock while initializing, because clock is relay on the slot */
241288
ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, &tdm_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
242289
#if SOC_I2S_SUPPORTS_APLL

components/esp_driver_i2s/include/driver/i2s_std.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ typedef struct {
299299
* @brief Initialize I2S channel to standard mode
300300
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
301301
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
302+
* @note When initialize the STD mode with a same configuration as another channel on a same port,
303+
* these two channels can constitude as full-duplex mode automatically
302304
*
303305
* @param[in] handle I2S channel handler
304306
* @param[in] std_cfg Configurations for standard mode, including clock, slot and GPIO

components/esp_driver_i2s/include/driver/i2s_tdm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ typedef struct {
196196
* @brief Initialize I2S channel to TDM mode
197197
* @note Only allowed to be called when the channel state is REGISTERED, (i.e., channel has been allocated, but not initialized)
198198
* and the state will be updated to READY if initialization success, otherwise the state will return to REGISTERED.
199+
* @note When initialize the TDM mode with a same configuration as another channel on a same port,
200+
* these two channels can constitude as full-duplex mode automatically
199201
*
200202
* @param[in] handle I2S channel handler
201203
* @param[in] tdm_cfg Configurations for TDM mode, including clock, slot and GPIO

0 commit comments

Comments
 (0)