mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 09:09:10 -04:00
feat(i2s): support tuning rate dynamically
This commit is contained in:
parent
0902e70e94
commit
ad623893d4
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1634,10 +1634,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();
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "hal/i2s_hal.h"
|
||||
#include "hal/hal_utils.h"
|
||||
#include "hal/dma_types.h"
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
#include "hal/cache_hal.h"
|
||||
@ -1374,6 +1375,95 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2s_channel_tune_rate(i2s_chan_handle_t handle, const i2s_tuning_config_t *tune_cfg, i2s_tuning_info_t *tune_info)
|
||||
{
|
||||
/** We tune the sample rate via the MCLK clock.
|
||||
* Because the sample rate is decided by MCLK eventually,
|
||||
* and MCLK has a higher resolution which can be tuned more precisely.
|
||||
*/
|
||||
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "NULL pointer");
|
||||
ESP_RETURN_ON_FALSE(!handle->is_external, ESP_ERR_NOT_SUPPORTED, TAG, "Not support to tune rate for external mclk");
|
||||
|
||||
/* If no tuning configuration given, just return the current information */
|
||||
if (tune_cfg == NULL) {
|
||||
xSemaphoreTake(handle->mutex, portMAX_DELAY);
|
||||
goto result;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(tune_cfg->max_delta_mclk >= tune_cfg->min_delta_mclk, ESP_ERR_INVALID_ARG, TAG, "invalid range");
|
||||
|
||||
uint32_t new_mclk = 0;
|
||||
|
||||
/* Get the new MCLK according to the tuning operation */
|
||||
switch (tune_cfg->tune_mode) {
|
||||
case I2S_TUNING_MODE_ADDSUB:
|
||||
new_mclk = handle->curr_mclk_hz + tune_cfg->tune_mclk_val;
|
||||
break;
|
||||
case I2S_TUNING_MODE_SET:
|
||||
new_mclk = tune_cfg->tune_mclk_val;
|
||||
break;
|
||||
case I2S_TUNING_MODE_RESET:
|
||||
new_mclk = handle->origin_mclk_hz;
|
||||
break;
|
||||
default:
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
/* Check if the tuned mclk is within the supposed range */
|
||||
if ((int32_t)new_mclk - (int32_t)handle->origin_mclk_hz > tune_cfg->max_delta_mclk) {
|
||||
new_mclk = handle->origin_mclk_hz + tune_cfg->max_delta_mclk;
|
||||
} else if ((int32_t)new_mclk - (int32_t)handle->origin_mclk_hz < tune_cfg->min_delta_mclk) {
|
||||
new_mclk = handle->origin_mclk_hz + tune_cfg->min_delta_mclk;
|
||||
}
|
||||
xSemaphoreTake(handle->mutex, portMAX_DELAY);
|
||||
#if SOC_CLK_APLL_SUPPORTED
|
||||
if (handle->clk_src == I2S_CLK_SRC_APLL) {
|
||||
periph_rtc_apll_release();
|
||||
handle->sclk_hz = i2s_set_get_apll_freq(new_mclk);
|
||||
periph_rtc_apll_acquire();
|
||||
}
|
||||
#endif
|
||||
/* Calculate the new divider */
|
||||
hal_utils_clk_div_t mclk_div = {};
|
||||
i2s_hal_calc_mclk_precise_division(handle->sclk_hz, new_mclk, &mclk_div);
|
||||
/* mclk_div = sclk / mclk >= 2 */
|
||||
if (mclk_div.integer < 2) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
/* Set the new divider for MCLK */
|
||||
I2S_CLOCK_SRC_ATOMIC() {
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
i2s_ll_tx_set_mclk(handle->controller->hal.dev, &mclk_div);
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_tx_update(handle->controller->hal.dev);
|
||||
#endif
|
||||
} else {
|
||||
i2s_ll_rx_set_mclk(handle->controller->hal.dev, &mclk_div);
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
i2s_ll_rx_update(handle->controller->hal.dev);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* Save the current mclk frequency */
|
||||
handle->curr_mclk_hz = (uint32_t)(((uint64_t)handle->sclk_hz * mclk_div.denominator) /
|
||||
(mclk_div.integer * mclk_div.denominator + mclk_div.numerator));
|
||||
result:
|
||||
/* Assign the information if needed */
|
||||
if (tune_info) {
|
||||
tune_info->curr_mclk_hz = handle->curr_mclk_hz;
|
||||
tune_info->delta_mclk_hz = (int32_t)handle->curr_mclk_hz - (int32_t)handle->origin_mclk_hz;
|
||||
uint32_t tot_size = handle->dma.buf_size * handle->dma.desc_num;
|
||||
uint32_t used_size = 0;
|
||||
if (handle->dir == I2S_DIR_TX) {
|
||||
used_size = uxQueueSpacesAvailable(handle->msg_queue) * handle->dma.buf_size + handle->dma.rw_pos;
|
||||
} else {
|
||||
used_size = uxQueueMessagesWaiting(handle->msg_queue) * handle->dma.buf_size + handle->dma.buf_size - handle->dma.rw_pos;
|
||||
}
|
||||
tune_info->water_mark = used_size * 100 / tot_size;
|
||||
}
|
||||
xSemaphoreGive(handle->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if SOC_I2S_SUPPORTS_TX_SYNC_CNT
|
||||
uint32_t i2s_sync_get_bclk_count(i2s_chan_handle_t tx_handle)
|
||||
{
|
||||
|
@ -81,13 +81,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
|
||||
@ -98,6 +97,13 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx
|
||||
|
||||
/* 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) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator);
|
||||
handle->curr_mclk_hz = handle->origin_mclk_hz;
|
||||
|
||||
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;
|
||||
}
|
||||
@ -401,18 +407,23 @@ 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);
|
||||
|
||||
/* 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) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator);
|
||||
handle->curr_mclk_hz = handle->origin_mclk_hz;
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
@ -159,14 +159,21 @@ struct i2s_channel_obj_t {
|
||||
/* Stored configurations */
|
||||
int intr_prio_flags;/*!< i2s interrupt priority flags */
|
||||
void *mode_info; /*!< Slot, clock and gpio information of each mode */
|
||||
bool is_etm_start; /*!< Whether start by etm tasks */
|
||||
bool is_etm_stop; /*!< Whether stop by etm tasks */
|
||||
struct {
|
||||
bool is_etm_start: 1; /*!< Whether start by etm tasks */
|
||||
bool is_etm_stop: 1; /*!< Whether stop by etm tasks */
|
||||
bool is_raw_pdm: 1; /*!< Flag of whether send/receive PDM in raw data, i.e., no PCM2PDM/PDM2PCM filter enabled */
|
||||
bool is_external: 1; /*!< Whether use external clock */
|
||||
#if SOC_I2S_SUPPORTS_APLL
|
||||
bool apll_en; /*!< Flag of whether APLL enabled */
|
||||
bool apll_en: 1; /*!< Flag of whether APLL enabled */
|
||||
#endif
|
||||
bool is_raw_pdm; /*!< Flag of whether send/receive PDM in raw data, i.e., no PCM2PDM/PDM2PCM filter enabled */
|
||||
};
|
||||
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 */
|
||||
/* Locks and queues */
|
||||
SemaphoreHandle_t mutex; /*!< Mutex semaphore for the channel operations */
|
||||
SemaphoreHandle_t binary; /*!< Binary semaphore for writing / reading / enabling / disabling */
|
||||
|
@ -49,9 +49,10 @@ static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std
|
||||
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
|
||||
}
|
||||
#if SOC_I2S_HW_VERSION_2
|
||||
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL ?
|
||||
clk_cfg->ext_clk_freq_hz : i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
|
||||
float min_mclk_div = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL ? 0.99 : 1.99;
|
||||
handle->is_external = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL;
|
||||
clk_info->sclk = handle->is_external ? clk_cfg->ext_clk_freq_hz :
|
||||
i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
|
||||
float min_mclk_div = handle->is_external ? 0.99 : 1.99;
|
||||
#else
|
||||
clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
|
||||
float min_mclk_div = 1.99;
|
||||
@ -77,22 +78,28 @@ 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);
|
||||
|
||||
/* 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) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator);
|
||||
handle->curr_mclk_hz = handle->origin_mclk_hz;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -59,9 +59,10 @@ static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm
|
||||
clk_info->bclk = rate * handle->total_slot * slot_bits;
|
||||
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
|
||||
}
|
||||
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL ?
|
||||
clk_cfg->ext_clk_freq_hz : i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
|
||||
float min_mclk_div = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL ? 0.99 : 1.99;
|
||||
handle->is_external = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL;
|
||||
clk_info->sclk = handle->is_external ? clk_cfg->ext_clk_freq_hz :
|
||||
i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
|
||||
float min_mclk_div = handle->is_external ? 0.99 : 1.99;
|
||||
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;
|
||||
|
||||
/* Check if the configuration is correct. Use float for check in case the mclk division might be carried up in the fine division calculation */
|
||||
@ -78,25 +79,28 @@ 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);
|
||||
|
||||
/* Update the mode info: clock configuration */
|
||||
memcpy(&(tdm_cfg->clk_cfg), clk_cfg, sizeof(i2s_tdm_clk_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;
|
||||
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) / (ret_mclk_div.integer * ret_mclk_div.denominator + ret_mclk_div.numerator);
|
||||
handle->curr_mclk_hz = handle->origin_mclk_hz;
|
||||
|
||||
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;
|
||||
}
|
||||
@ -147,6 +151,9 @@ 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;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -96,6 +96,7 @@ typedef struct {
|
||||
*/
|
||||
} i2s_chan_info_t;
|
||||
|
||||
/************************************************** Basic APIs ********************************************************/
|
||||
/**
|
||||
* @brief Allocate new I2S channel(s)
|
||||
* @note The new created I2S channel handle will be REGISTERED state after it is allocated successfully.
|
||||
@ -174,28 +175,6 @@ esp_err_t i2s_channel_enable(i2s_chan_handle_t handle);
|
||||
*/
|
||||
esp_err_t i2s_channel_disable(i2s_chan_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Preload the data into TX DMA buffer
|
||||
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
|
||||
* @note As the initial DMA buffer has no data inside, it will transmit the empty buffer after enabled the channel,
|
||||
* this function is used to preload the data into the DMA buffer, so that the valid data can be transmitted immediately
|
||||
* after the channel is enabled.
|
||||
* @note This function can be called multiple times before enabling the channel, the buffer that loaded later will be concatenated
|
||||
* behind the former loaded buffer. But when all the DMA buffers have been loaded, no more data can be preload then, please
|
||||
* check the `bytes_loaded` parameter to see how many bytes are loaded successfully, when the `bytes_loaded` is smaller than
|
||||
* the `size`, it means the DMA buffers are full.
|
||||
*
|
||||
* @param[in] tx_handle I2S TX channel handler
|
||||
* @param[in] src The pointer of the source buffer to be loaded
|
||||
* @param[in] size The source buffer size
|
||||
* @param[out] bytes_loaded The bytes that successfully been loaded into the TX DMA buffer
|
||||
* @return
|
||||
* - ESP_OK Load data successful
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or not TX direction
|
||||
* - ESP_ERR_INVALID_STATE This channel has not stated
|
||||
*/
|
||||
esp_err_t i2s_channel_preload_data(i2s_chan_handle_t tx_handle, const void *src, size_t size, size_t *bytes_loaded);
|
||||
|
||||
/**
|
||||
* @brief I2S write data
|
||||
* @note Only allowed to be called when the channel state is RUNNING, (i.e., TX channel has been started and is not writing now)
|
||||
@ -250,6 +229,47 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si
|
||||
*/
|
||||
esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2s_event_callbacks_t *callbacks, void *user_data);
|
||||
|
||||
/************************************************ Advanced APIs *******************************************************/
|
||||
/**
|
||||
* @brief Preload the data into TX DMA buffer
|
||||
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
|
||||
* @note As the initial DMA buffer has no data inside, it will transmit the empty buffer after enabled the channel,
|
||||
* this function is used to preload the data into the DMA buffer, so that the valid data can be transmitted immediately
|
||||
* after the channel is enabled.
|
||||
* @note This function can be called multiple times before enabling the channel, the buffer that loaded later will be concatenated
|
||||
* behind the former loaded buffer. But when all the DMA buffers have been loaded, no more data can be preload then, please
|
||||
* check the `bytes_loaded` parameter to see how many bytes are loaded successfully, when the `bytes_loaded` is smaller than
|
||||
* the `size`, it means the DMA buffers are full.
|
||||
*
|
||||
* @param[in] tx_handle I2S TX channel handler
|
||||
* @param[in] src The pointer of the source buffer to be loaded
|
||||
* @param[in] size The source buffer size
|
||||
* @param[out] bytes_loaded The bytes that successfully been loaded into the TX DMA buffer
|
||||
* @return
|
||||
* - ESP_OK Load data successful
|
||||
* - ESP_ERR_INVALID_ARG NULL pointer or not TX direction
|
||||
* - ESP_ERR_INVALID_STATE This channel has not stated
|
||||
*/
|
||||
esp_err_t i2s_channel_preload_data(i2s_chan_handle_t tx_handle, const void *src, size_t size, size_t *bytes_loaded);
|
||||
|
||||
/**
|
||||
* @brief Tune the I2S clock rate
|
||||
* @note Only allowed to be called when the channel state is READY, (i.e., channel has been initialized, but not started)
|
||||
* @note This function is mainly to fine-tuning the mclk to match the speed of producer and consumer.
|
||||
* So that to avoid exsaust of the memory to store the data from producer.
|
||||
* Please take care the how different the frequency error can be tolerant by your codec,
|
||||
* otherwise the codec might stop working if the frequency changes a lot.
|
||||
*
|
||||
* @param[in] handle I2S channel handler
|
||||
* @param[in] tune_cfg The clock tuning configuration, can be NULL if only need the current clock result
|
||||
* @param[out] tune_info The clock tuning information, can be NULL if not needed
|
||||
* @return
|
||||
* - ESP_OK Tune the clock successfully
|
||||
* - ESP_ERR_INVALID_ARG Tune the clock failed because of the invalid argument like NULL pointer or out of range
|
||||
* - ESP_ERR_NOT_SUPPORTED Tune the clock failed because this function does not support to tune the external clock source
|
||||
*/
|
||||
esp_err_t i2s_channel_tune_rate(i2s_chan_handle_t handle, const i2s_tuning_config_t *tune_cfg, i2s_tuning_info_t *tune_info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -87,6 +87,37 @@ typedef struct {
|
||||
*/
|
||||
} i2s_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief I2S clock tuning operation
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
I2S_TUNING_MODE_ADDSUB, /*!< Add or subtract the tuning value based on the current clock */
|
||||
I2S_TUNING_MODE_SET, /*!< Set the tuning value to overwrite the current clock */
|
||||
I2S_TUNING_MODE_RESET, /*!< Set the clock to the initial value */
|
||||
} i2s_tuning_mode_t;
|
||||
|
||||
/**
|
||||
* @brief I2S clock tuning configurations
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
i2s_tuning_mode_t tune_mode; /*!< Tuning mode, which decides how to tune the MCLK with the tuning value */
|
||||
int32_t tune_mclk_val; /*!< Tuning value */
|
||||
int32_t max_delta_mclk; /*!< The maximum frequency that can be increased comparing to the initial MCLK freuqnecy */
|
||||
int32_t min_delta_mclk; /*!< The minimum frequency that can be decreased comparing to the initial MCLK freuqnecy */
|
||||
} i2s_tuning_config_t;
|
||||
|
||||
/**
|
||||
* @brief I2S clock tuning result
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int32_t curr_mclk_hz; /*!< The current MCLK frequency after tuned */
|
||||
int32_t delta_mclk_hz; /*!< The current changed MCLK frequency comparing to the initial MCLK frequency */
|
||||
uint32_t water_mark; /*!< The water mark of the internal buffer, in percent */
|
||||
} i2s_tuning_info_t;
|
||||
|
||||
/**
|
||||
* @brief Event data structure for LP I2S
|
||||
*/
|
||||
|
@ -1016,3 +1016,78 @@ TEST_CASE("I2S_PDM2PCM_existence_test", "[i2s]")
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("I2S_rate_tunning", "[i2s]")
|
||||
{
|
||||
i2s_chan_handle_t tx_handle;
|
||||
i2s_chan_handle_t rx_handle;
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
|
||||
.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(SAMPLE_BITS, I2S_SLOT_MODE_STEREO),
|
||||
.gpio_cfg = I2S_TEST_MASTER_DEFAULT_PIN,
|
||||
};
|
||||
std_cfg.gpio_cfg.din = std_cfg.gpio_cfg.dout; // GPIO loopback
|
||||
#if SOC_CLK_APLL_SUPPORTED
|
||||
std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
|
||||
#endif
|
||||
|
||||
TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
|
||||
TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
|
||||
|
||||
TEST_ESP_OK(i2s_channel_enable(rx_handle));
|
||||
TEST_ESP_OK(i2s_channel_enable(tx_handle));
|
||||
|
||||
i2s_tuning_info_t tune_init_res = {};
|
||||
TEST_ESP_OK(i2s_channel_tune_rate(tx_handle, NULL, &tune_init_res));
|
||||
i2s_tuning_info_t tune_info = {};
|
||||
|
||||
// Add/subtract case
|
||||
int32_t tune_val = 400;
|
||||
i2s_tuning_config_t tune_addsub_cfg = {
|
||||
.tune_mode = I2S_TUNING_MODE_ADDSUB,
|
||||
.tune_mclk_val = tune_val,
|
||||
.max_delta_mclk = 1000,
|
||||
.min_delta_mclk = -1000,
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_tune_rate(tx_handle, &tune_addsub_cfg, &tune_info));
|
||||
printf("# Addsub case: init mclk: %"PRIu32" tune val: %"PRId32" curr mclk: %"PRId32" delta mclk: %"PRId32"\n",
|
||||
tune_init_res.curr_mclk_hz, tune_val, tune_info.curr_mclk_hz, tune_info.delta_mclk_hz);
|
||||
TEST_ASSERT_UINT32_WITHIN(50, tune_init_res.curr_mclk_hz + tune_val, tune_info.curr_mclk_hz);
|
||||
|
||||
// Set case
|
||||
tune_val = tune_init_res.curr_mclk_hz - 500;
|
||||
i2s_tuning_config_t tune_set_cfg = {
|
||||
.tune_mode = I2S_TUNING_MODE_SET,
|
||||
.tune_mclk_val = tune_val,
|
||||
.max_delta_mclk = 1000,
|
||||
.min_delta_mclk = -1000,
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_tune_rate(tx_handle, &tune_set_cfg, &tune_info));
|
||||
printf("# Set case: init mclk: %"PRIu32" tune val: %"PRId32" curr mclk: %"PRId32" delta mclk: %"PRId32"\n",
|
||||
tune_init_res.curr_mclk_hz, tune_val, tune_info.curr_mclk_hz, tune_info.delta_mclk_hz);
|
||||
TEST_ASSERT_UINT32_WITHIN(50, tune_val, tune_info.curr_mclk_hz);
|
||||
|
||||
// Out of range case
|
||||
tune_set_cfg.min_delta_mclk = -400;
|
||||
TEST_ESP_OK(i2s_channel_tune_rate(tx_handle, &tune_set_cfg, &tune_info));
|
||||
printf("# Out of range case: init mclk: %"PRIu32" tune val: %"PRId32" curr mclk: %"PRId32" delta mclk: %"PRId32"\n",
|
||||
tune_init_res.curr_mclk_hz, tune_val, tune_info.curr_mclk_hz, tune_info.delta_mclk_hz);
|
||||
TEST_ASSERT_UINT32_WITHIN(50, tune_init_res.curr_mclk_hz + tune_set_cfg.min_delta_mclk, tune_info.curr_mclk_hz);
|
||||
|
||||
// Reset case
|
||||
i2s_tuning_config_t tune_reset_cfg = {
|
||||
.tune_mode = I2S_TUNING_MODE_RESET,
|
||||
};
|
||||
TEST_ESP_OK(i2s_channel_tune_rate(tx_handle, &tune_reset_cfg, &tune_info));
|
||||
printf("# Reset case: init mclk: %"PRIu32" tune val: %"PRId32" curr mclk: %"PRId32" delta mclk: %"PRId32"\n",
|
||||
tune_init_res.curr_mclk_hz, tune_val, tune_info.curr_mclk_hz, tune_info.delta_mclk_hz);
|
||||
TEST_ASSERT_EQUAL_UINT32(tune_init_res.curr_mclk_hz, tune_info.curr_mclk_hz);
|
||||
|
||||
TEST_ESP_OK(i2s_channel_disable(tx_handle));
|
||||
TEST_ESP_OK(i2s_channel_disable(rx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(tx_handle));
|
||||
TEST_ESP_OK(i2s_del_channel(rx_handle));
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -388,6 +388,28 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the TX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_tx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the RX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_rx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start I2S TX
|
||||
*
|
||||
@ -396,8 +418,7 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
i2s_ll_tx_update(hw);
|
||||
hw->tx_conf.tx_start = 1;
|
||||
}
|
||||
|
||||
@ -409,8 +430,7 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
i2s_ll_rx_update(hw);
|
||||
hw->rx_conf.rx_start = 1;
|
||||
}
|
||||
|
||||
|
@ -420,6 +420,28 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the TX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_tx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the RX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_rx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start I2S TX
|
||||
*
|
||||
@ -428,8 +450,7 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
i2s_ll_tx_update(hw);
|
||||
hw->tx_conf.tx_start = 1;
|
||||
}
|
||||
|
||||
@ -441,8 +462,7 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
i2s_ll_rx_update(hw);
|
||||
hw->rx_conf.rx_start = 1;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
@ -410,6 +410,28 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the TX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_tx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the RX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_rx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start I2S TX
|
||||
*
|
||||
@ -418,8 +440,7 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
i2s_ll_tx_update(hw);
|
||||
hw->tx_conf.tx_start = 1;
|
||||
}
|
||||
|
||||
@ -431,8 +452,7 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
i2s_ll_rx_update(hw);
|
||||
hw->rx_conf.rx_start = 1;
|
||||
}
|
||||
|
||||
|
@ -420,6 +420,28 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the TX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_tx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the RX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_rx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start I2S TX
|
||||
*
|
||||
@ -428,8 +450,7 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
i2s_ll_tx_update(hw);
|
||||
hw->tx_conf.tx_start = 1;
|
||||
}
|
||||
|
||||
@ -441,8 +462,7 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
i2s_ll_rx_update(hw);
|
||||
hw->rx_conf.rx_start = 1;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
@ -418,6 +418,28 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the TX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_tx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the RX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_rx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start I2S TX
|
||||
*
|
||||
@ -426,8 +448,7 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
i2s_ll_tx_update(hw);
|
||||
hw->tx_conf.tx_start = 1;
|
||||
}
|
||||
|
||||
@ -439,8 +460,7 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
i2s_ll_rx_update(hw);
|
||||
hw->rx_conf.rx_start = 1;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -679,6 +679,28 @@ static inline void _i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define i2s_ll_rx_set_mclk(...) (void)__DECLARE_RCC_ATOMIC_ENV; _i2s_ll_rx_set_mclk(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Update the TX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_tx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the RX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_rx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start I2S TX
|
||||
*
|
||||
@ -687,8 +709,7 @@ static inline void _i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t
|
||||
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
i2s_ll_tx_update(hw);
|
||||
hw->tx_conf.tx_start = 1;
|
||||
}
|
||||
|
||||
@ -700,8 +721,7 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
i2s_ll_rx_update(hw);
|
||||
hw->rx_conf.rx_start = 1;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -394,6 +394,27 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
i2s_ll_rx_set_raw_clk_div(hw, mclk_div->integer, div_x, div_y, div_z, div_yn1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the TX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_tx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the RX configuration
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
static inline void i2s_ll_rx_update(i2s_dev_t *hw)
|
||||
{
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start I2S TX
|
||||
@ -403,8 +424,7 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, const hal_utils_clk_div_t *
|
||||
static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->tx_conf.tx_update = 1;
|
||||
while (hw->tx_conf.tx_update);
|
||||
i2s_ll_tx_update(hw);
|
||||
hw->tx_conf.tx_start = 1;
|
||||
}
|
||||
|
||||
@ -416,8 +436,7 @@ static inline void i2s_ll_tx_start(i2s_dev_t *hw)
|
||||
static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
{
|
||||
// Have to update registers before start
|
||||
hw->rx_conf.rx_update = 1;
|
||||
while (hw->rx_conf.rx_update);
|
||||
i2s_ll_rx_update(hw);
|
||||
hw->rx_conf.rx_start = 1;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -68,68 +68,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);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -159,7 +159,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__)
|
||||
@ -170,8 +170,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
|
||||
|
||||
/**
|
||||
@ -180,8 +181,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
|
||||
|
@ -320,6 +320,14 @@ Configuration
|
||||
|
||||
Users can initialize a channel by calling corresponding functions (i.e., :func:`i2s_channel_init_std_mode`, :func:`i2s_channel_init_pdm_rx_mode`, :func:`i2s_channel_init_pdm_tx_mode`, or :func:`i2s_channel_init_tdm_mode`) to a specific mode. If the configurations need to be updated after initialization, users have to first call :cpp:func:`i2s_channel_disable` to ensure that the channel has stopped, and then call corresponding ``reconfig`` functions, like :cpp:func:`i2s_channel_reconfig_std_slot`, :cpp:func:`i2s_channel_reconfig_std_clock`, and :cpp:func:`i2s_channel_reconfig_std_gpio`.
|
||||
|
||||
Advanced API
|
||||
^^^^^^^^^^^^
|
||||
|
||||
To satisfy the high quality audio requiment, following advanced APIs are provided:
|
||||
|
||||
- :cpp:func:`i2s_channel_preload_data`: Preloading audio data into the I2S internal cache, enabling the TX channel to immediately send data upon activation, thereby reducing the initial audio output delay.
|
||||
- :cpp:func:`i2s_channel_tune_rate`: Dynamically fine-tune the audio rate at runtime to match the speed of the audio data producer and consumer, thereby preventing the accumulation or shortage of intermediate buffered data caused by rate mismatches.
|
||||
|
||||
IRAM Safe
|
||||
^^^^^^^^^
|
||||
|
||||
|
@ -320,6 +320,14 @@ I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输
|
||||
|
||||
用户可以通过调用相应函数(即 :func:`i2s_channel_init_std_mode`、 :func:`i2s_channel_init_pdm_rx_mode`、 :func:`i2s_channel_init_pdm_tx_mode` 或 :func:`i2s_channel_init_tdm_mode`)将通道初始化为特定模式。如果初始化后需要更新配置,必须先调用 :cpp:func:`i2s_channel_disable` 以确保通道已经停止运行,然后再调用相应的 'reconfig' 函数,例如 :cpp:func:`i2s_channel_reconfig_std_slot`、 :cpp:func:`i2s_channel_reconfig_std_clock` 和 :cpp:func:`i2s_channel_reconfig_std_gpio`。
|
||||
|
||||
进阶 API
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
为满足高质量音频需求,驱动提供了以下进阶 API:
|
||||
|
||||
- :cpp:func:`i2s_channel_preload_data`: 用于预加载音频数据到 I2S 内部缓存,使得 TX 通道使能后能够立即发送数据,以此降低音频初始输出延迟。
|
||||
- :cpp:func:`i2s_channel_tune_rate`: 用于在运行时动态微调音频速率,以匹配音频数据生产者和消费者的速度,从而防止因速率不匹配导致的中间缓存数据累积或不足。
|
||||
|
||||
IRAM 安全
|
||||
^^^^^^^^^
|
||||
|
||||
|
@ -25,7 +25,7 @@ def test_i2s_pdm_tx_example(dut: Dut) -> None:
|
||||
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)
|
||||
r'\[mdiv\] ([0-9]+) ([0-9]+)/([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)
|
||||
|
@ -21,12 +21,12 @@ def test_i2s_basic_example(dut: Dut) -> None:
|
||||
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)
|
||||
r'\[mdiv\] ([0-9]+) ([0-9]+)/([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)
|
||||
r'\[mdiv\] ([0-9]+) ([0-9]+)/([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',
|
||||
|
@ -19,12 +19,12 @@ def test_i2s_tdm_example(dut: Dut) -> None:
|
||||
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)
|
||||
r'\[mdiv\] ([0-9]+) ([0-9]+)/([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)
|
||||
r'\[mdiv\] ([0-9]+) ([0-9]+)/([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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user