From 7c694de81cf8dab4f54f2e348cb4dff04acfcaa8 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Mon, 29 Dec 2025 16:29:27 +0800 Subject: [PATCH] feat(i2s): support to get more channel info --- components/driver/deprecated/i2s_legacy.c | 10 ++-- components/esp_driver_i2s/i2s_common.c | 6 ++ components/esp_driver_i2s/i2s_pdm.c | 33 +++++++--- components/esp_driver_i2s/i2s_private.h | 7 ++- components/esp_driver_i2s/i2s_std.c | 46 +++++++++----- components/esp_driver_i2s/i2s_tdm.c | 60 +++++++++++++------ .../include/driver/i2s_common.h | 11 ++++ .../esp_driver_i2s/include/driver/i2s_types.h | 2 +- components/hal/i2s_hal.c | 30 ++++++---- components/hal/include/hal/i2s_hal.h | 10 ++-- .../i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py | 2 - .../i2s/i2s_basic/i2s_std/pytest_i2s_std.py | 4 -- .../i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py | 4 -- 13 files changed, 151 insertions(+), 74 deletions(-) diff --git a/components/driver/deprecated/i2s_legacy.c b/components/driver/deprecated/i2s_legacy.c index 5b5148fedd..7ab15a4f84 100644 --- a/components/driver/deprecated/i2s_legacy.c +++ b/components/driver/deprecated/i2s_legacy.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -1096,10 +1096,10 @@ static void i2s_set_clock_legacy(i2s_port_t i2s_num) i2s_calculate_clock(i2s_num, &clk_info); I2S_CLOCK_SRC_ATOMIC() { if (p_i2s[i2s_num]->dir & I2S_DIR_TX) { - i2s_hal_set_tx_clock(&(p_i2s[i2s_num]->hal), &clk_info, clk_cfg->clk_src); + i2s_hal_set_tx_clock(&(p_i2s[i2s_num]->hal), &clk_info, clk_cfg->clk_src, NULL); } if (p_i2s[i2s_num]->dir & I2S_DIR_RX) { - i2s_hal_set_rx_clock(&(p_i2s[i2s_num]->hal), &clk_info, clk_cfg->clk_src); + i2s_hal_set_rx_clock(&(p_i2s[i2s_num]->hal), &clk_info, clk_cfg->clk_src, NULL); } } } @@ -1610,10 +1610,10 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) I2S_CLOCK_SRC_ATOMIC() { // switch back to PLL clock source if (obj->dir & I2S_DIR_TX) { - i2s_hal_set_tx_clock(&obj->hal, NULL, I2S_CLK_SRC_DEFAULT); + i2s_hal_set_tx_clock(&obj->hal, NULL, I2S_CLK_SRC_DEFAULT, NULL); } if (obj->dir & I2S_DIR_RX) { - i2s_hal_set_rx_clock(&obj->hal, NULL, I2S_CLK_SRC_DEFAULT); + i2s_hal_set_rx_clock(&obj->hal, NULL, I2S_CLK_SRC_DEFAULT, NULL); } } periph_rtc_apll_release(); diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index a057a7eee3..5ea541fcba 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -1184,6 +1184,12 @@ found: chan_info->dir = handle->dir; chan_info->role = handle->role; chan_info->mode = handle->mode; + chan_info->is_enabled = handle->state == I2S_CHAN_STATE_RUNNING; + chan_info->clk_src = handle->clk_src; + chan_info->sclk_hz = handle->sclk_hz; + chan_info->mclk_hz = handle->curr_mclk_hz; + chan_info->bclk_hz = handle->bclk_hz; + chan_info->mode_cfg = handle->mode_info; chan_info->total_dma_buf_size = handle->state >= I2S_CHAN_STATE_READY ? handle->dma.desc_num * handle->dma.buf_size : 0; if (handle->controller->full_duplex) { if (handle->dir == I2S_DIR_TX) { diff --git a/components/esp_driver_i2s/i2s_pdm.c b/components/esp_driver_i2s/i2s_pdm.c index d47a732569..c9d5fdf319 100644 --- a/components/esp_driver_i2s/i2s_pdm.c +++ b/components/esp_driver_i2s/i2s_pdm.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -66,13 +66,12 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx i2s_hal_clock_info_t clk_info; /* Calculate clock parameters */ ESP_RETURN_ON_ERROR(i2s_pdm_tx_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed"); - ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %d [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", - clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk); + hal_utils_clk_div_t ret_mclk_div = {}; portENTER_CRITICAL(&g_i2s.spinlock); /* Set clock configurations in HAL*/ I2S_CLOCK_SRC_ATOMIC() { - i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src); + i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src, &ret_mclk_div); } #if SOC_I2S_HW_VERSION_2 /* Work around for PDM TX clock, overwrite the raw division directly to reduce the noise @@ -81,8 +80,19 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx #endif portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); + /* Update the mode info: clock configuration */ memcpy(&(pdm_tx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_tx_clk_config_t)); + handle->clk_src = clk_cfg->clk_src; + handle->sclk_hz = clk_info.sclk; + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; + handle->curr_mclk_hz = handle->origin_mclk_hz; + handle->bclk_hz = clk_info.bclk; + + ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", + clk_info.sclk, ret_mclk_div.integer, ret_mclk_div.numerator, ret_mclk_div.denominator, handle->origin_mclk_hz, clk_info.bclk_div, clk_info.bclk); return ret; } @@ -375,18 +385,27 @@ static esp_err_t i2s_pdm_rx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_rx i2s_hal_clock_info_t clk_info; /* Calculate clock parameters */ ESP_RETURN_ON_ERROR(i2s_pdm_rx_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed"); - ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %d [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", - clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk); + hal_utils_clk_div_t ret_mclk_div = {}; portENTER_CRITICAL(&g_i2s.spinlock); /* Set clock configurations in HAL*/ I2S_CLOCK_SRC_ATOMIC() { - i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src); + i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src, &ret_mclk_div); } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); + /* Update the mode info: clock configuration */ memcpy(&(pdm_rx_cfg->clk_cfg), clk_cfg, sizeof(i2s_pdm_rx_clk_config_t)); + handle->clk_src = clk_cfg->clk_src; + handle->sclk_hz = clk_info.sclk; + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; + handle->curr_mclk_hz = handle->origin_mclk_hz; + handle->bclk_hz = clk_info.bclk; + ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", + clk_info.sclk, ret_mclk_div.integer, ret_mclk_div.numerator, ret_mclk_div.denominator, handle->origin_mclk_hz, clk_info.bclk_div, clk_info.bclk); return ret; } diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 678113e5c4..df28ca15bf 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -169,6 +169,11 @@ struct i2s_channel_obj_t { #endif uint32_t active_slot; /*!< Active slot number */ uint32_t total_slot; /*!< Total slot number */ + i2s_clock_src_t clk_src; /*!< Clock source */ + uint32_t sclk_hz; /*!< Source clock frequency */ + uint32_t origin_mclk_hz; /*!< Original mclk frequency */ + uint32_t curr_mclk_hz; /*!< Current mclk frequency */ + uint32_t bclk_hz; /*!< BCLK frequency */ /* Locks and queues */ SemaphoreHandle_t mutex; /*!< Mutex semaphore for the channel operations */ SemaphoreHandle_t binary; /*!< Binary semaphore for writing / reading / enabling / disabling */ diff --git a/components/esp_driver_i2s/i2s_std.c b/components/esp_driver_i2s/i2s_std.c index dc6cf07af5..275ced2268 100644 --- a/components/esp_driver_i2s/i2s_std.c +++ b/components/esp_driver_i2s/i2s_std.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -79,33 +79,53 @@ static esp_err_t i2s_std_set_clock(i2s_chan_handle_t handle, const i2s_std_clk_c i2s_hal_clock_info_t clk_info; /* Calculate clock parameters */ ESP_RETURN_ON_ERROR(i2s_std_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed"); - ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %d [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", - clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk); + hal_utils_clk_div_t ret_mclk_div = {}; portENTER_CRITICAL(&g_i2s.spinlock); /* Set clock configurations in HAL*/ I2S_CLOCK_SRC_ATOMIC() { if (handle->dir == I2S_DIR_TX) { - i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src); + i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src, &ret_mclk_div); } else { - i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src); + i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src, &ret_mclk_div); } } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); /* Update the mode info: clock configuration */ memcpy(&(std_cfg->clk_cfg), clk_cfg, sizeof(i2s_std_clk_config_t)); + handle->clk_src = clk_cfg->clk_src; + handle->sclk_hz = clk_info.sclk; + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; + handle->curr_mclk_hz = handle->origin_mclk_hz; + handle->bclk_hz = clk_info.bclk; + + ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", + clk_info.sclk, ret_mclk_div.integer, ret_mclk_div.numerator, ret_mclk_div.denominator, handle->origin_mclk_hz, clk_info.bclk_div, clk_info.bclk); return ret; } +static i2s_std_slot_config_t s_i2s_std_normalize_slot_config(const i2s_std_slot_config_t *slot_cfg) +{ + i2s_std_slot_config_t normalized_slot_cfg = *slot_cfg; + /* 1. Normalize the slot bit width */ + normalized_slot_cfg.slot_bit_width = (int)normalized_slot_cfg.slot_bit_width < (int)normalized_slot_cfg.data_bit_width ? + normalized_slot_cfg.data_bit_width : normalized_slot_cfg.slot_bit_width; + + return normalized_slot_cfg; +} + static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_config_t *slot_cfg) { + i2s_std_slot_config_t norm_slot_cfg = s_i2s_std_normalize_slot_config(slot_cfg); /* Update the total slot num and active slot num */ handle->total_slot = 2; - handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; + handle->active_slot = norm_slot_cfg.slot_mode == I2S_SLOT_MODE_MONO ? 1 : 2; - uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + uint32_t buf_size = i2s_get_buf_size(handle, norm_slot_cfg.data_bit_width, handle->dma.frame_num); ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { @@ -124,18 +144,15 @@ static esp_err_t i2s_std_set_slot(i2s_chan_handle_t handle, const i2s_std_slot_c portENTER_CRITICAL(&g_i2s.spinlock); /* Configure the hardware to apply STD format */ if (handle->dir == I2S_DIR_TX) { - i2s_hal_std_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg); + i2s_hal_std_set_tx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)&norm_slot_cfg); } else { - i2s_hal_std_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)slot_cfg); + i2s_hal_std_set_rx_slot(&(handle->controller->hal), is_slave, (i2s_hal_slot_config_t *)&norm_slot_cfg); } portEXIT_CRITICAL(&g_i2s.spinlock); /* Update the mode info: slot configuration */ i2s_std_config_t *std_cfg = (i2s_std_config_t *)(handle->mode_info); memcpy(&(std_cfg->slot_cfg), slot_cfg, sizeof(i2s_std_slot_config_t)); - /* Update the slot bit width to the actual slot bit width */ - std_cfg->slot_cfg.slot_bit_width = (int)std_cfg->slot_cfg.slot_bit_width < (int)std_cfg->slot_cfg.data_bit_width ? - std_cfg->slot_cfg.data_bit_width : std_cfg->slot_cfg.slot_bit_width; return ESP_OK; } @@ -231,9 +248,8 @@ static esp_err_t s_i2s_channel_try_to_constitude_std_duplex(i2s_chan_handle_t ha /* Judge if the two channels can constitute full-duplex */ if (!handle->controller->full_duplex) { i2s_std_config_t curr_cfg = *std_cfg; - /* Override the slot bit width to the actual slot bit width */ - curr_cfg.slot_cfg.slot_bit_width = (int)curr_cfg.slot_cfg.slot_bit_width < (int)curr_cfg.slot_cfg.data_bit_width ? - curr_cfg.slot_cfg.data_bit_width : curr_cfg.slot_cfg.slot_bit_width; + i2s_std_slot_config_t norm_slot_cfg = s_i2s_std_normalize_slot_config(&(std_cfg->slot_cfg)); + memcpy(&curr_cfg.slot_cfg, &norm_slot_cfg, sizeof(i2s_std_slot_config_t)); /* Compare the hardware configurations of the two channels, constitute the full-duplex if they are the same */ if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_std_config_t)) == 0) { handle->controller->full_duplex = true; diff --git a/components/esp_driver_i2s/i2s_tdm.c b/components/esp_driver_i2s/i2s_tdm.c index 5bf73a3633..a6d081444e 100644 --- a/components/esp_driver_i2s/i2s_tdm.c +++ b/components/esp_driver_i2s/i2s_tdm.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -80,40 +80,68 @@ static esp_err_t i2s_tdm_set_clock(i2s_chan_handle_t handle, const i2s_tdm_clk_c i2s_hal_clock_info_t clk_info; /* Calculate clock parameters */ ESP_RETURN_ON_ERROR(i2s_tdm_calculate_clock(handle, clk_cfg, &clk_info), TAG, "clock calculate failed"); - ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %d [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", - clk_info.sclk, clk_info.mclk_div, clk_info.mclk, clk_info.bclk_div, clk_info.bclk); + hal_utils_clk_div_t ret_mclk_div = {}; portENTER_CRITICAL(&g_i2s.spinlock); /* Set clock configurations in HAL*/ I2S_CLOCK_SRC_ATOMIC() { if (handle->dir == I2S_DIR_TX) { - i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src); + i2s_hal_set_tx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src, &ret_mclk_div); } else { - i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src); + i2s_hal_set_rx_clock(&handle->controller->hal, &clk_info, clk_cfg->clk_src, &ret_mclk_div); } } portEXIT_CRITICAL(&g_i2s.spinlock); + uint64_t tmp_div = (uint64_t)ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator; + ESP_RETURN_ON_FALSE(tmp_div != 0 && ret_mclk_div.denominator != 0, ESP_ERR_INVALID_ARG, TAG, "invalid mclk division result"); /* Update the mode info: clock configuration */ memcpy(&(tdm_cfg->clk_cfg), clk_cfg, sizeof(i2s_tdm_clk_config_t)); + handle->clk_src = clk_cfg->clk_src; + handle->sclk_hz = clk_info.sclk; + handle->origin_mclk_hz = ((uint64_t)clk_info.sclk * ret_mclk_div.denominator) / tmp_div; + handle->curr_mclk_hz = handle->origin_mclk_hz; + handle->bclk_hz = clk_info.bclk; + + ESP_LOGD(TAG, "Clock division info: [sclk] %"PRIu32" Hz [mdiv] %"PRIu32" %"PRIu32"/%"PRIu32" [mclk] %"PRIu32" Hz [bdiv] %d [bclk] %"PRIu32" Hz", + clk_info.sclk, ret_mclk_div.integer, ret_mclk_div.numerator, ret_mclk_div.denominator, handle->origin_mclk_hz, clk_info.bclk_div, clk_info.bclk); return ret; } +static i2s_tdm_slot_config_t s_i2s_tdm_normalize_slot_config(const i2s_tdm_slot_config_t *slot_cfg) +{ + i2s_tdm_slot_config_t normalized_slot_cfg = *slot_cfg; + uint32_t max_slot_num = 32 - __builtin_clz(normalized_slot_cfg.slot_mask); + /* 1. Normalize the ws width */ + normalized_slot_cfg.ws_width = normalized_slot_cfg.ws_width == I2S_TDM_AUTO_WS_WIDTH ? + normalized_slot_cfg.total_slot * normalized_slot_cfg.slot_bit_width / 2 : normalized_slot_cfg.ws_width; + + /* 2. Normalize the total slot number */ + normalized_slot_cfg.total_slot = normalized_slot_cfg.total_slot < max_slot_num ? max_slot_num : normalized_slot_cfg.total_slot; + // At least two slots in a frame if not using PCM short format + normalized_slot_cfg.total_slot = ((normalized_slot_cfg.total_slot < 2) && (normalized_slot_cfg.ws_width != 1)) ? 2 : normalized_slot_cfg.total_slot; + + /* 3. Normalize the slot bit width */ + normalized_slot_cfg.slot_bit_width = normalized_slot_cfg.slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO ? + normalized_slot_cfg.data_bit_width : normalized_slot_cfg.slot_bit_width; + return normalized_slot_cfg; +} + static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_config_t *slot_cfg) { ESP_RETURN_ON_FALSE(slot_cfg->slot_mask, ESP_ERR_INVALID_ARG, TAG, "At least one channel should be enabled"); + + i2s_tdm_slot_config_t norm_slot_cfg = s_i2s_tdm_normalize_slot_config(slot_cfg); /* Update the total slot num and active slot num */ - handle->active_slot = slot_cfg->slot_mode == I2S_SLOT_MODE_MONO ? 1 : __builtin_popcount(slot_cfg->slot_mask); - uint32_t max_slot_num = 32 - __builtin_clz(slot_cfg->slot_mask); - handle->total_slot = slot_cfg->total_slot < max_slot_num ? max_slot_num : slot_cfg->total_slot; + handle->active_slot = norm_slot_cfg.slot_mode == I2S_SLOT_MODE_MONO ? 1 : __builtin_popcount(norm_slot_cfg.slot_mask); + handle->total_slot = norm_slot_cfg.total_slot; // At least two slots in a frame if not using PCM short format - handle->total_slot = ((handle->total_slot < 2) && (slot_cfg->ws_width != 1)) ? 2 : handle->total_slot; - uint32_t slot_bits = slot_cfg->slot_bit_width == I2S_SLOT_BIT_WIDTH_AUTO ? slot_cfg->data_bit_width : slot_cfg->slot_bit_width; + uint32_t slot_bits = norm_slot_cfg.slot_bit_width; ESP_RETURN_ON_FALSE(handle->total_slot * slot_bits <= I2S_LL_SLOT_FRAME_BIT_MAX, ESP_ERR_INVALID_ARG, TAG, "total slots(%"PRIu32") * slot_bit_width(%"PRIu32") exceeds the maximum %d", handle->total_slot, slot_bits, (int)I2S_LL_SLOT_FRAME_BIT_MAX); - uint32_t buf_size = i2s_get_buf_size(handle, slot_cfg->data_bit_width, handle->dma.frame_num); + uint32_t buf_size = i2s_get_buf_size(handle, norm_slot_cfg.data_bit_width, handle->dma.frame_num); ESP_RETURN_ON_FALSE(buf_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid data_bit_width"); /* The DMA buffer need to re-allocate if the buffer size changed */ if (handle->dma.buf_size != buf_size) { @@ -140,10 +168,7 @@ static esp_err_t i2s_tdm_set_slot(i2s_chan_handle_t handle, const i2s_tdm_slot_c /* Update the mode info: slot configuration */ i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info); - memcpy(&(tdm_cfg->slot_cfg), slot_cfg, sizeof(i2s_tdm_slot_config_t)); - /* Update the slot bit width to the actual slot bit width */ - tdm_cfg->slot_cfg.slot_bit_width = (int)tdm_cfg->slot_cfg.slot_bit_width < (int)tdm_cfg->slot_cfg.data_bit_width ? - tdm_cfg->slot_cfg.data_bit_width : tdm_cfg->slot_cfg.slot_bit_width; + memcpy(&(tdm_cfg->slot_cfg), &norm_slot_cfg, sizeof(i2s_tdm_slot_config_t)); return ESP_OK; } @@ -239,9 +264,8 @@ static void s_i2s_channel_try_to_constitude_tdm_duplex(i2s_chan_handle_t handle, if (another_handle && another_handle->state >= I2S_CHAN_STATE_READY) { if (!handle->controller->full_duplex) { i2s_tdm_config_t curr_cfg = *tdm_cfg; - /* Override the slot bit width to the actual slot bit width */ - curr_cfg.slot_cfg.slot_bit_width = (int)curr_cfg.slot_cfg.slot_bit_width < (int)curr_cfg.slot_cfg.data_bit_width ? - curr_cfg.slot_cfg.data_bit_width : curr_cfg.slot_cfg.slot_bit_width; + i2s_tdm_slot_config_t norm_slot_cfg = s_i2s_tdm_normalize_slot_config(&(tdm_cfg->slot_cfg)); + memcpy(&curr_cfg.slot_cfg, &norm_slot_cfg, sizeof(i2s_tdm_slot_config_t)); /* Compare the hardware configurations of the two channels, constitute the full-duplex if they are the same */ if (memcmp(another_handle->mode_info, &curr_cfg, sizeof(i2s_tdm_config_t)) == 0) { handle->controller->full_duplex = true; diff --git a/components/esp_driver_i2s/include/driver/i2s_common.h b/components/esp_driver_i2s/include/driver/i2s_common.h index 96a30ed561..13cf7a8352 100644 --- a/components/esp_driver_i2s/include/driver/i2s_common.h +++ b/components/esp_driver_i2s/include/driver/i2s_common.h @@ -89,11 +89,22 @@ typedef struct { i2s_role_t role; /*!< I2S role, I2S_ROLE_MASTER or I2S_ROLE_SLAVE */ i2s_dir_t dir; /*!< I2S channel direction */ i2s_comm_mode_t mode; /*!< I2S channel communication mode */ + bool is_enabled; /*!< I2S channel is enabled or not */ i2s_chan_handle_t pair_chan; /*!< I2S pair channel handle in duplex mode, always NULL in simplex mode */ uint32_t total_dma_buf_size; /*!< Total size of all the allocated DMA buffers * - 0 if the channel has not been initialized * - non-zero if the channel has been initialized */ + i2s_clock_src_t clk_src; /*!< Clock source of I2S */ + uint32_t sclk_hz; /*!< Source clock frequency */ + uint32_t mclk_hz; /*!< MCLK frequency */ + uint32_t bclk_hz; /*!< BCLK frequency */ + const void *mode_cfg; /*!< Mode configuration, it need to be casted to the corresponding type according to the communication mode + * - I2S_COMM_MODE_STD: i2s_std_config_t* + * - I2S_COMM_MODE_TDM: i2s_tdm_config_t* + * - I2S_COMM_MODE_PDM + I2S_DIR_RX: i2s_pdm_rx_config_t* + * - I2S_COMM_MODE_PDM + I2S_DIR_TX: i2s_pdm_tx_config_t* + */ } i2s_chan_info_t; /** diff --git a/components/esp_driver_i2s/include/driver/i2s_types.h b/components/esp_driver_i2s/include/driver/i2s_types.h index f7a602fdde..e2607a376c 100644 --- a/components/esp_driver_i2s/include/driver/i2s_types.h +++ b/components/esp_driver_i2s/include/driver/i2s_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/i2s_hal.c b/components/hal/i2s_hal.c index 40613bfb62..b898e93b5e 100644 --- a/components/hal/i2s_hal.c +++ b/components/hal/i2s_hal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -67,68 +67,72 @@ void i2s_hal_init(i2s_hal_context_t *hal, int port_id) } #if SOC_PERIPH_CLK_CTRL_SHARED -void _i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src) +void _i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div) { if (clk_info) { hal_utils_clk_div_t mclk_div = {}; + hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div; #if SOC_I2S_HW_VERSION_2 _i2s_ll_tx_enable_clock(hal->dev); _i2s_ll_mclk_bind_to_tx_clk(hal->dev); #endif _i2s_ll_tx_clk_set_src(hal->dev, clk_src); - i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, &mclk_div); - _i2s_ll_tx_set_mclk(hal->dev, &mclk_div); + i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr); + _i2s_ll_tx_set_mclk(hal->dev, mclk_div_ptr); i2s_ll_tx_set_bck_div_num(hal->dev, clk_info->bclk_div); } else { _i2s_ll_tx_clk_set_src(hal->dev, clk_src); } } -void _i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src) +void _i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div) { if (clk_info) { hal_utils_clk_div_t mclk_div = {}; + hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div; #if SOC_I2S_HW_VERSION_2 _i2s_ll_rx_enable_clock(hal->dev); _i2s_ll_mclk_bind_to_rx_clk(hal->dev); #endif _i2s_ll_rx_clk_set_src(hal->dev, clk_src); - i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, &mclk_div); - _i2s_ll_rx_set_mclk(hal->dev, &mclk_div); + i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr); + _i2s_ll_rx_set_mclk(hal->dev, mclk_div_ptr); i2s_ll_rx_set_bck_div_num(hal->dev, clk_info->bclk_div); } else { _i2s_ll_rx_clk_set_src(hal->dev, clk_src); } } #else -void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src) +void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div) { if (clk_info) { hal_utils_clk_div_t mclk_div = {}; + hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div; #if SOC_I2S_HW_VERSION_2 i2s_ll_tx_enable_clock(hal->dev); i2s_ll_mclk_bind_to_tx_clk(hal->dev); #endif i2s_ll_tx_clk_set_src(hal->dev, clk_src); - i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, &mclk_div); - i2s_ll_tx_set_mclk(hal->dev, &mclk_div); + i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr); + i2s_ll_tx_set_mclk(hal->dev, mclk_div_ptr); i2s_ll_tx_set_bck_div_num(hal->dev, clk_info->bclk_div); } else { i2s_ll_tx_clk_set_src(hal->dev, clk_src); } } -void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src) +void i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div) { if (clk_info) { hal_utils_clk_div_t mclk_div = {}; + hal_utils_clk_div_t *mclk_div_ptr = ret_mclk_div ? ret_mclk_div : &mclk_div; #if SOC_I2S_HW_VERSION_2 i2s_ll_rx_enable_clock(hal->dev); i2s_ll_mclk_bind_to_rx_clk(hal->dev); #endif i2s_ll_rx_clk_set_src(hal->dev, clk_src); - i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, &mclk_div); - i2s_ll_rx_set_mclk(hal->dev, &mclk_div); + i2s_hal_calc_mclk_precise_division(clk_info->sclk, clk_info->mclk, mclk_div_ptr); + i2s_ll_rx_set_mclk(hal->dev, mclk_div_ptr); i2s_ll_rx_set_bck_div_num(hal->dev, clk_info->bclk_div); } else { i2s_ll_rx_clk_set_src(hal->dev, clk_src); diff --git a/components/hal/include/hal/i2s_hal.h b/components/hal/include/hal/i2s_hal.h index 8fa3a38434..5870b12a98 100644 --- a/components/hal/include/hal/i2s_hal.h +++ b/components/hal/include/hal/i2s_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -147,7 +147,7 @@ void i2s_hal_calc_mclk_precise_division(uint32_t sclk, uint32_t mclk, hal_utils_ * @param clk_info clock information, if it is NULL, only set the clock source * @param clk_src clock source */ -void _i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src); +void _i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div); /// use a macro to wrap the function, force the caller to use it in a critical section /// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance #define i2s_hal_set_tx_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; _i2s_hal_set_tx_clock(__VA_ARGS__) @@ -158,8 +158,9 @@ void _i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *c * @param hal Context of the HAL layer * @param clk_info clock information, if it is NULL, only set the clock source * @param clk_src clock source + * @param ret_mclk_div return mclk division coefficients, including integer part and decimal part */ -void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src); +void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div); #endif // SOC_PERIPH_CLK_CTRL_SHARED /** @@ -168,8 +169,9 @@ void i2s_hal_set_tx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *cl * @param hal Context of the HAL layer * @param clk_info clock information, if it is NULL, only set the clock source * @param clk_src clock source + * @param ret_mclk_div return mclk division coefficients, including integer part and decimal part */ -void _i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src); +void _i2s_hal_set_rx_clock(i2s_hal_context_t *hal, const i2s_hal_clock_info_t *clk_info, i2s_clock_src_t clk_src, hal_utils_clk_div_t *ret_mclk_div); #if SOC_PERIPH_CLK_CTRL_SHARED /// use a macro to wrap the function, force the caller to use it in a critical section diff --git a/examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py b/examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py index 1e82c96389..dccedddf1b 100644 --- a/examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py +++ b/examples/peripherals/i2s/i2s_basic/i2s_pdm/pytest_i2s_pdm.py @@ -24,8 +24,6 @@ def test_i2s_pdm_tx_example(dut: Dut) -> None: dut.expect(r'i2s_common: tx channel is registered on I2S0 successfully', timeout=5) dut.expect(r'i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), ' r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5) - dut.expect(r'i2s_pdm: Clock division info: \[sclk\] ([0-9]+) Hz ' - r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5) dut.expect(r'i2s_pdm: The tx channel on I2S0 has been initialized to PDM TX mode successfully', timeout=5) dut.expect(r'i2s_common: i2s tx channel enabled', timeout=5) dut.expect(r'Playing bass `twinkle twinkle little star`', timeout=5) diff --git a/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py b/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py index ed76891e12..d6f5bd34e2 100644 --- a/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py +++ b/examples/peripherals/i2s/i2s_basic/i2s_std/pytest_i2s_std.py @@ -20,13 +20,9 @@ def test_i2s_basic_example(dut: Dut) -> None: dut.expect(r'i2s_common: rx channel is registered on I2S0 successfully', timeout=5) dut.expect(r'i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), ' r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5) - dut.expect(r'i2s_std: Clock division info: \[sclk\] ([0-9]+) Hz ' - r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5) dut.expect(r'i2s_std: The tx channel on I2S0 has been initialized to STD mode successfully', timeout=5) dut.expect(r'i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), ' r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5) - dut.expect(r'i2s_std: Clock division info: \[sclk\] ([0-9]+) Hz ' - r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5) dut.expect(r'i2s_std: The rx channel on I2S0 has been initialized to STD mode successfully', timeout=5) chan_enable_pattern = [ r'i2s_common: i2s tx channel enabled', diff --git a/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py b/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py index 8d6ba95378..a338654fa1 100644 --- a/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py +++ b/examples/peripherals/i2s/i2s_basic/i2s_tdm/pytest_i2s_tdm.py @@ -18,13 +18,9 @@ def test_i2s_tdm_example(dut: Dut) -> None: dut.expect(r'i2s_common: rx channel is registered on I2S0 successfully', timeout=5) dut.expect(r'i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), ' r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5) - dut.expect(r'i2s_tdm: Clock division info: \[sclk\] ([0-9]+) Hz ' - r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5) dut.expect(r'i2s_tdm: The tx channel on I2S0 has been initialized to TDM mode successfully', timeout=5) dut.expect(r'i2s_common: DMA malloc info: dma_desc_num = ([0-9]+), ' r'dma_desc_buf_size = dma_frame_num \* slot_num \* data_bit_width = ([0-9]+)', timeout=5) - dut.expect(r'i2s_tdm: Clock division info: \[sclk\] ([0-9]+) Hz ' - r'\[mdiv\] ([0-9]+) \[mclk\] ([0-9]+) Hz \[bdiv\] ([0-9]+) \[bclk\] ([0-9]+) Hz', timeout=5) dut.expect(r'i2s_tdm: The rx channel on I2S0 has been initialized to TDM mode successfully', timeout=5) chan_enable_pattern = [ r'i2s_common: i2s tx channel enabled',