From 4b6d71447cda00db1f97b1b7581c4bfce0520581 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Thu, 20 Jul 2023 17:00:48 +0800 Subject: [PATCH 1/2] feat(i2s): supported external clock source input --- components/driver/i2s/i2s_common.c | 50 +++++++------------ components/driver/i2s/i2s_pdm.c | 6 +++ components/driver/i2s/i2s_private.h | 4 +- components/driver/i2s/i2s_std.c | 20 ++++---- components/driver/i2s/i2s_tdm.c | 15 +++--- .../driver/i2s/include/driver/i2s_std.h | 11 +++- .../driver/i2s/include/driver/i2s_tdm.h | 10 +++- components/hal/esp32c3/include/hal/i2s_ll.h | 6 +++ components/hal/esp32c6/include/hal/i2s_ll.h | 6 +++ components/hal/esp32h2/include/hal/i2s_ll.h | 6 +++ components/hal/esp32s3/include/hal/i2s_ll.h | 6 +++ components/soc/esp32/i2s_periph.c | 2 + components/soc/esp32c3/i2s_periph.c | 1 + .../soc/esp32c3/include/soc/clk_tree_defs.h | 3 +- components/soc/esp32c6/i2s_periph.c | 1 + .../soc/esp32c6/include/soc/clk_tree_defs.h | 3 +- components/soc/esp32h2/i2s_periph.c | 1 + .../soc/esp32h2/include/soc/clk_tree_defs.h | 3 +- components/soc/esp32s2/i2s_periph.c | 1 + components/soc/esp32s3/i2s_periph.c | 2 + .../soc/esp32s3/include/soc/clk_tree_defs.h | 3 +- components/soc/include/soc/i2s_periph.h | 1 + 22 files changed, 104 insertions(+), 57 deletions(-) diff --git a/components/driver/i2s/i2s_common.c b/components/driver/i2s/i2s_common.c index 7aefd0da7f..d1e7f2dbde 100644 --- a/components/driver/i2s/i2s_common.c +++ b/components/driver/i2s/i2s_common.c @@ -43,6 +43,7 @@ #include "i2s_private.h" #include "clk_ctrl_os.h" +#include "esp_clk_tree.h" #include "esp_intr_alloc.h" #include "esp_check.h" #include "esp_attr.h" @@ -435,39 +436,16 @@ static uint32_t i2s_set_get_apll_freq(uint32_t mclk_freq_hz) } #endif -// [clk_tree] TODO: replace the following switch table by clk_tree API uint32_t i2s_get_source_clk_freq(i2s_clock_src_t clk_src, uint32_t mclk_freq_hz) { - switch (clk_src) - { + uint32_t clk_freq = 0; #if SOC_I2S_SUPPORTS_APLL - case I2S_CLK_SRC_APLL: + if (clk_src == I2S_CLK_SRC_APLL) { return i2s_set_get_apll_freq(mclk_freq_hz); -#endif -#if SOC_I2S_SUPPORTS_XTAL - case I2S_CLK_SRC_XTAL: - (void)mclk_freq_hz; - return esp_clk_xtal_freq(); -#endif -#if SOC_I2S_SUPPORTS_PLL_F160M - case I2S_CLK_SRC_PLL_160M: - (void)mclk_freq_hz; - return I2S_LL_PLL_F160M_CLK_FREQ; -#endif -#if SOC_I2S_SUPPORTS_PLL_F96M - case I2S_CLK_SRC_PLL_96M: - (void)mclk_freq_hz; - return I2S_LL_PLL_F96M_CLK_FREQ; -#endif -#if SOC_I2S_SUPPORTS_PLL_F64M - case I2S_CLK_SRC_PLL_64M: - (void)mclk_freq_hz; - return I2S_LL_PLL_F64M_CLK_FREQ; -#endif - default: - // Invalid clock source - return 0; } +#endif + esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_freq); + return clk_freq; } #if SOC_GDMA_SUPPORTED @@ -711,7 +689,7 @@ void i2s_gpio_loopback_set(gpio_num_t gpio, uint32_t out_sig_idx, uint32_t in_si } } -esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, bool is_invert) +esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, i2s_clock_src_t clk_src, bool is_invert) { if (gpio_num == I2S_GPIO_UNUSED) { return ESP_OK; @@ -721,6 +699,7 @@ esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, b ESP_ERR_INVALID_ARG, TAG, "ESP32 only support to set GPIO0/GPIO1/GPIO3 as mclk signal, error GPIO number:%d", gpio_num); bool is_i2s0 = id == I2S_NUM_0; + bool is_apll = clk_src == I2S_CLK_SRC_APLL; if (gpio_num == GPIO_NUM_0) { gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); gpio_ll_iomux_pin_ctrl(is_apll ? 0xFFF6 : (is_i2s0 ? 0xFFF0 : 0xFFFF)); @@ -733,9 +712,16 @@ esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, b } #else ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid"); - i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_out_sig, false, is_invert); -#endif - ESP_LOGD(TAG, "MCLK is pinned to GPIO%d on I2S%d", id, gpio_num); +#if SOC_I2S_HW_VERSION_2 + if (clk_src == I2S_CLK_SRC_EXTERNAL) { + i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_in_sig, true, is_invert); + } else +#endif // SOC_I2S_HW_VERSION_2 + { + i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_out_sig, false, is_invert); + } +#endif // CONFIG_IDF_TARGET_ESP32 + ESP_LOGD(TAG, "MCLK is pinned to GPIO%d on I2S%d", gpio_num, id); return ESP_OK; } diff --git a/components/driver/i2s/i2s_pdm.c b/components/driver/i2s/i2s_pdm.c index e8d2f43dc2..7991f6e969 100644 --- a/components/driver/i2s/i2s_pdm.c +++ b/components/driver/i2s/i2s_pdm.c @@ -57,6 +57,9 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx { esp_err_t ret = ESP_OK; i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)(handle->mode_info); +#if SOC_I2S_HW_VERSION_2 + ESP_RETURN_ON_FALSE(clk_cfg->clk_src != I2S_CLK_SRC_EXTERNAL, ESP_ERR_INVALID_ARG, TAG, "not support external clock source in pdm mode"); +#endif ESP_RETURN_ON_FALSE(clk_cfg->up_sample_fs <= 480, ESP_ERR_INVALID_ARG, TAG, "up_sample_fs should be within 480"); i2s_hal_clock_info_t clk_info; @@ -342,6 +345,9 @@ static esp_err_t i2s_pdm_rx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_rx { esp_err_t ret = ESP_OK; i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)(handle->mode_info); +#if SOC_I2S_HW_VERSION_2 + ESP_RETURN_ON_FALSE(clk_cfg->clk_src != I2S_CLK_SRC_EXTERNAL, ESP_ERR_INVALID_ARG, TAG, "not support external clock source in pdm mode"); +#endif i2s_hal_clock_info_t clk_info; /* Calculate clock parameters */ diff --git a/components/driver/i2s/i2s_private.h b/components/driver/i2s/i2s_private.h index 747f0acc03..23dfe79f5f 100644 --- a/components/driver/i2s/i2s_private.h +++ b/components/driver/i2s/i2s_private.h @@ -191,13 +191,13 @@ void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input, * * @param id I2S port id * @param gpio_num GPIO number - * @param is_apll Is using APLL as clock source + * @param clk_src The clock source of this I2S port * @param is_invert Is invert the GPIO * @return * - ESP_OK Set mclk output gpio success * - ESP_ERR_INVALID_ARG Invalid GPIO number */ -esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, bool is_invert); +esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, i2s_clock_src_t clk_src, bool is_invert); /** * @brief Attach data out signal and data in signal to a same gpio diff --git a/components/driver/i2s/i2s_std.c b/components/driver/i2s/i2s_std.c index 1a70e5852a..2570e3b4b2 100644 --- a/components/driver/i2s/i2s_std.c +++ b/components/driver/i2s/i2s_std.c @@ -45,11 +45,16 @@ static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std clk_info->bclk = rate * handle->total_slot * slot_bits; 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); +#else clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk); +#endif clk_info->mclk_div = clk_info->sclk / clk_info->mclk; /* Check if the configuration is correct */ - ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large for the current clock source"); return ESP_OK; } @@ -151,6 +156,9 @@ static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_c i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false); } + /* Set mclk pin */ + ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, std_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed"); + if (handle->role == I2S_ROLE_SLAVE) { /* For "tx + slave" mode, select TX signal index for ws and bck */ if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) { @@ -165,13 +173,6 @@ static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_c i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv); } } else { - /* mclk only available in master mode */ -#if SOC_I2S_SUPPORTS_APLL - bool is_apll = std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL; -#else - bool is_apll = false; -#endif - ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, is_apll, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed"); /* For "rx + master" mode, select RX signal index for ws and bck */ if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) { #if SOC_I2S_HW_VERSION_2 @@ -208,7 +209,6 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf handle->mode_info = calloc(1, sizeof(i2s_std_config_t)); ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations"); ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_REGISTER, ESP_ERR_INVALID_STATE, err, TAG, "the channel has initialized already"); - ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins"); /* i2s_set_std_slot should be called before i2s_set_std_clock while initializing, because clock is relay on the slot */ ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot"); #if SOC_I2S_SUPPORTS_APLL @@ -219,6 +219,8 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf } #endif ESP_GOTO_ON_ERROR(i2s_std_set_clock(handle, &std_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock"); + /* i2s_std_set_gpio should be called after i2s_std_set_clock as mclk relies on the clock source */ + ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins"); ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed"); #if SOC_I2S_HW_VERSION_2 /* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */ diff --git a/components/driver/i2s/i2s_tdm.c b/components/driver/i2s/i2s_tdm.c index b1e89990eb..5484881dd9 100644 --- a/components/driver/i2s/i2s_tdm.c +++ b/components/driver/i2s/i2s_tdm.c @@ -56,11 +56,12 @@ 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 = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk); + 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); clk_info->mclk_div = clk_info->sclk / clk_info->mclk; /* Check if the configuration is correct */ - ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large"); + ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large for the current clock source"); return ESP_OK; } @@ -146,6 +147,7 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c ESP_ERR_INVALID_ARG, TAG, "bclk invalid"); ESP_RETURN_ON_FALSE((gpio_cfg->ws == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->ws)), ESP_ERR_INVALID_ARG, TAG, "ws invalid"); + i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info); /* Loopback if dout = din */ if (gpio_cfg->dout != -1 && gpio_cfg->dout == gpio_cfg->din) { @@ -158,6 +160,9 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false); } + /* Set mclk pin */ + ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, tdm_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed"); + if (handle->role == I2S_ROLE_SLAVE) { /* For "tx + slave" mode, select TX signal index for ws and bck */ if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) { @@ -172,8 +177,6 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv); } } else { - /* mclk only available in master mode */ - ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, false, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed"); /* For "rx + master" mode, select RX signal index for ws and bck */ if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) { #if SOC_I2S_HW_VERSION_2 @@ -188,7 +191,6 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c } } /* Update the mode info: gpio configuration */ - i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info); memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t)); return ESP_OK; @@ -212,7 +214,6 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf } handle->mode_info = calloc(1, sizeof(i2s_tdm_config_t)); ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations"); - ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, &tdm_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins"); /* i2s_set_tdm_slot should be called before i2s_set_tdm_clock while initializing, because clock is relay on the slot */ ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, &tdm_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot"); #if SOC_I2S_SUPPORTS_APLL @@ -223,6 +224,8 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf } #endif ESP_GOTO_ON_ERROR(i2s_tdm_set_clock(handle, &tdm_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock"); + /* i2s_tdm_set_gpio should be called after i2s_tdm_set_clock as mclk relies on the clock source */ + ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, &tdm_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins"); ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed"); #if SOC_I2S_HW_VERSION_2 diff --git a/components/driver/i2s/include/driver/i2s_std.h b/components/driver/i2s/include/driver/i2s_std.h index 6930e3b51a..adf8013acc 100644 --- a/components/driver/i2s/include/driver/i2s_std.h +++ b/components/driver/i2s/include/driver/i2s_std.h @@ -237,7 +237,14 @@ typedef struct { typedef struct { /* General fields */ uint32_t sample_rate_hz; /*!< I2S sample rate */ - i2s_clock_src_t clk_src; /*!< Choose clock source */ + i2s_clock_src_t clk_src; /*!< Choose clock source, see 'soc_periph_i2s_clk_src_t' for the supported clock sources. + * selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock inputted via MCLK pin, + */ +#if SOC_I2S_HW_VERSION_2 + uint32_t ext_clk_freq_hz; /*!< External clock source frequency in Hz, only take effect when 'clk_src = I2S_CLK_SRC_EXTERNAL', otherwise this field will be ignored, + * Please make sure the frequency inputted is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * 2' + */ +#endif i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate * Default is 256 in the helper macro, it can satisfy most of cases, * but please set this field a multiple of '3' (like 384) when using 24-bit data width, @@ -249,7 +256,7 @@ typedef struct { * @brief I2S standard mode GPIO pins configuration */ typedef struct { - gpio_num_t mclk; /*!< MCK pin, output */ + gpio_num_t mclk; /*!< MCK pin, output by default, input if the clock source is selected to 'I2S_CLK_SRC_EXTERNAL' */ gpio_num_t bclk; /*!< BCK pin, input in slave role, output in master role */ gpio_num_t ws; /*!< WS pin, input in slave role, output in master role */ gpio_num_t dout; /*!< DATA pin, output */ diff --git a/components/driver/i2s/include/driver/i2s_tdm.h b/components/driver/i2s/include/driver/i2s_tdm.h index 759473eead..beb915a12f 100644 --- a/components/driver/i2s/include/driver/i2s_tdm.h +++ b/components/driver/i2s/include/driver/i2s_tdm.h @@ -156,7 +156,13 @@ typedef struct { typedef struct { /* General fields */ uint32_t sample_rate_hz; /*!< I2S sample rate */ - i2s_clock_src_t clk_src; /*!< Choose clock source */ + i2s_clock_src_t clk_src; /*!< Choose clock source, see 'soc_periph_i2s_clk_src_t' for the supported clock sources. + * selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock inputted via MCLK pin, + * please make sure the frequency inputted is equal or greater than 'sample_rate_hz * mclk_multiple' + */ + uint32_t ext_clk_freq_hz; /*!< External clock source frequency in Hz, only take effect when 'clk_src = I2S_CLK_SRC_EXTERNAL', otherwise this field will be ignored + * Please make sure the frequency inputted is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * slot_num' + */ i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate, only take effect for master role */ uint32_t bclk_div; /*!< The division from mclk to bclk, only take effect for slave role, it shouldn't be smaller than 8. Increase this field when data sent by slave lag behind */ } i2s_tdm_clk_config_t; @@ -165,7 +171,7 @@ typedef struct { * @brief I2S TDM mode GPIO pins configuration */ typedef struct { - gpio_num_t mclk; /*!< MCK pin, output */ + gpio_num_t mclk; /*!< MCK pin, output by default, input if the clock source is selected to 'I2S_CLK_SRC_EXTERNAL' */ gpio_num_t bclk; /*!< BCK pin, input in slave role, output in master role */ gpio_num_t ws; /*!< WS pin, input in slave role, output in master role */ gpio_num_t dout; /*!< DATA pin, output */ diff --git a/components/hal/esp32c3/include/hal/i2s_ll.h b/components/hal/esp32c3/include/hal/i2s_ll.h index e007248f49..cf492a72db 100644 --- a/components/hal/esp32c3/include/hal/i2s_ll.h +++ b/components/hal/esp32c3/include/hal/i2s_ll.h @@ -207,6 +207,9 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_160M: hw->tx_clkm_conf.tx_clk_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + hw->tx_clkm_conf.tx_clk_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; @@ -229,6 +232,9 @@ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_160M: hw->rx_clkm_conf.rx_clk_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + hw->rx_clkm_conf.rx_clk_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; diff --git a/components/hal/esp32c6/include/hal/i2s_ll.h b/components/hal/esp32c6/include/hal/i2s_ll.h index c36e5d7ea6..347767cecc 100644 --- a/components/hal/esp32c6/include/hal/i2s_ll.h +++ b/components/hal/esp32c6/include/hal/i2s_ll.h @@ -217,6 +217,9 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_160M: PCR.i2s_tx_clkm_conf.i2s_tx_clkm_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + PCR.i2s_tx_clkm_conf.i2s_tx_clkm_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; @@ -240,6 +243,9 @@ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_160M: PCR.i2s_rx_clkm_conf.i2s_rx_clkm_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + PCR.i2s_rx_clkm_conf.i2s_rx_clkm_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; diff --git a/components/hal/esp32h2/include/hal/i2s_ll.h b/components/hal/esp32h2/include/hal/i2s_ll.h index 2d7b4fce29..d8a0b7ad04 100644 --- a/components/hal/esp32h2/include/hal/i2s_ll.h +++ b/components/hal/esp32h2/include/hal/i2s_ll.h @@ -221,6 +221,9 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_64M: PCR.i2s_tx_clkm_conf.i2s_tx_clkm_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + PCR.i2s_tx_clkm_conf.i2s_tx_clkm_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; @@ -247,6 +250,9 @@ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_64M: PCR.i2s_rx_clkm_conf.i2s_rx_clkm_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + PCR.i2s_rx_clkm_conf.i2s_rx_clkm_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; diff --git a/components/hal/esp32s3/include/hal/i2s_ll.h b/components/hal/esp32s3/include/hal/i2s_ll.h index 02de004fbe..4ae55469c4 100644 --- a/components/hal/esp32s3/include/hal/i2s_ll.h +++ b/components/hal/esp32s3/include/hal/i2s_ll.h @@ -208,6 +208,9 @@ static inline void i2s_ll_tx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_160M: hw->tx_clkm_conf.tx_clk_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + hw->tx_clkm_conf.tx_clk_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; @@ -230,6 +233,9 @@ static inline void i2s_ll_rx_clk_set_src(i2s_dev_t *hw, i2s_clock_src_t src) case I2S_CLK_SRC_PLL_160M: hw->rx_clkm_conf.rx_clk_sel = 2; break; + case I2S_CLK_SRC_EXTERNAL: + hw->rx_clkm_conf.rx_clk_sel = 3; + break; default: HAL_ASSERT(false && "unsupported clock source"); break; diff --git a/components/soc/esp32/i2s_periph.c b/components/soc/esp32/i2s_periph.c index d2c0c227a1..83c2920af6 100644 --- a/components/soc/esp32/i2s_periph.c +++ b/components/soc/esp32/i2s_periph.c @@ -13,6 +13,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = -1, // Unavailable + .mck_in_sig = -1, // Unavailable .m_tx_bck_sig = I2S0O_BCK_OUT_IDX, .m_rx_bck_sig = I2S0I_BCK_OUT_IDX, @@ -32,6 +33,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { }, { .mck_out_sig = -1, // Unavailable + .mck_in_sig = -1, // Unavailable .m_tx_bck_sig = I2S1O_BCK_OUT_IDX, .m_rx_bck_sig = I2S1I_BCK_OUT_IDX, diff --git a/components/soc/esp32c3/i2s_periph.c b/components/soc/esp32c3/i2s_periph.c index 88937899fd..0aad6d6cad 100644 --- a/components/soc/esp32c3/i2s_periph.c +++ b/components/soc/esp32c3/i2s_periph.c @@ -13,6 +13,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = I2S_MCLK_OUT_IDX, + .mck_in_sig = I2S_MCLK_IN_IDX, .m_tx_bck_sig = I2SO_BCK_OUT_IDX, .m_rx_bck_sig = I2SI_BCK_OUT_IDX, diff --git a/components/soc/esp32c3/include/soc/clk_tree_defs.h b/components/soc/esp32c3/include/soc/clk_tree_defs.h index 894c872a83..a77af08347 100644 --- a/components/soc/esp32c3/include/soc/clk_tree_defs.h +++ b/components/soc/esp32c3/include/soc/clk_tree_defs.h @@ -216,7 +216,7 @@ typedef enum { /** * @brief Array initializer for all supported clock sources of I2S */ -#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL} +#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL, I2S_CLK_SRC_EXTERNAL} /** * @brief I2S clock source enum @@ -225,6 +225,7 @@ typedef enum { I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */ I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ I2S_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + I2S_CLK_SRC_EXTERNAL = -1, /*!< Select external clock as source clock */ } soc_periph_i2s_clk_src_t; /////////////////////////////////////////////////I2C//////////////////////////////////////////////////////////////////// diff --git a/components/soc/esp32c6/i2s_periph.c b/components/soc/esp32c6/i2s_periph.c index 89f49e2617..3965e936ee 100644 --- a/components/soc/esp32c6/i2s_periph.c +++ b/components/soc/esp32c6/i2s_periph.c @@ -13,6 +13,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = I2S_MCLK_OUT_IDX, + .mck_in_sig = I2S_MCLK_IN_IDX, .m_tx_bck_sig = I2SO_BCK_OUT_IDX, .m_rx_bck_sig = I2SI_BCK_OUT_IDX, diff --git a/components/soc/esp32c6/include/soc/clk_tree_defs.h b/components/soc/esp32c6/include/soc/clk_tree_defs.h index 3cc9c17d99..bb349aef8f 100644 --- a/components/soc/esp32c6/include/soc/clk_tree_defs.h +++ b/components/soc/esp32c6/include/soc/clk_tree_defs.h @@ -271,7 +271,7 @@ typedef enum { /** * @brief Array initializer for all supported clock sources of I2S */ -#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL} +#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL, I2S_CLK_SRC_EXTERNAL} /** * @brief I2S clock source enum @@ -280,6 +280,7 @@ typedef enum { I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */ I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ I2S_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + I2S_CLK_SRC_EXTERNAL = -1, /*!< Select external clock as source clock */ } soc_periph_i2s_clk_src_t; /////////////////////////////////////////////////I2C//////////////////////////////////////////////////////////////////// diff --git a/components/soc/esp32h2/i2s_periph.c b/components/soc/esp32h2/i2s_periph.c index daa8057277..4183de00a6 100644 --- a/components/soc/esp32h2/i2s_periph.c +++ b/components/soc/esp32h2/i2s_periph.c @@ -13,6 +13,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = I2S_MCLK_OUT_IDX, + .mck_in_sig = I2S_MCLK_IN_IDX, .m_tx_bck_sig = I2SO_BCK_OUT_IDX, .m_rx_bck_sig = I2SI_BCK_OUT_IDX, diff --git a/components/soc/esp32h2/include/soc/clk_tree_defs.h b/components/soc/esp32h2/include/soc/clk_tree_defs.h index 6fbb932525..d70a46721e 100644 --- a/components/soc/esp32h2/include/soc/clk_tree_defs.h +++ b/components/soc/esp32h2/include/soc/clk_tree_defs.h @@ -269,7 +269,7 @@ typedef enum { /** * @brief Array initializer for all supported clock sources of I2S */ -#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F96M, SOC_MOD_CLK_PLL_F64M, SOC_MOD_CLK_XTAL} +#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F96M, SOC_MOD_CLK_PLL_F64M, SOC_MOD_CLK_XTAL, I2S_CLK_SRC_EXTERNAL} /** * @brief I2S clock source enum @@ -279,6 +279,7 @@ typedef enum { I2S_CLK_SRC_PLL_96M = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the source clock */ I2S_CLK_SRC_PLL_64M = SOC_MOD_CLK_PLL_F64M, /*!< Select PLL_F64M as the source clock */ I2S_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + I2S_CLK_SRC_EXTERNAL = -1, /*!< Select external clock as source clock */ } soc_periph_i2s_clk_src_t; /////////////////////////////////////////////////I2C//////////////////////////////////////////////////////////////////// diff --git a/components/soc/esp32s2/i2s_periph.c b/components/soc/esp32s2/i2s_periph.c index 2bee28695a..25b4804cb2 100644 --- a/components/soc/esp32s2/i2s_periph.c +++ b/components/soc/esp32s2/i2s_periph.c @@ -13,6 +13,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = CLK_I2S_MUX_IDX, + .mck_in_sig = -1, // Unavailable .m_tx_bck_sig = I2S0O_BCK_OUT_IDX, .m_rx_bck_sig = I2S0I_BCK_OUT_IDX, diff --git a/components/soc/esp32s3/i2s_periph.c b/components/soc/esp32s3/i2s_periph.c index 64233b9d9a..4966264483 100644 --- a/components/soc/esp32s3/i2s_periph.c +++ b/components/soc/esp32s3/i2s_periph.c @@ -13,6 +13,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { { .mck_out_sig = I2S0_MCLK_OUT_IDX, + .mck_in_sig = I2S0_MCLK_IN_IDX, .m_tx_bck_sig = I2S0O_BCK_OUT_IDX, .m_rx_bck_sig = I2S0I_BCK_OUT_IDX, @@ -36,6 +37,7 @@ const i2s_signal_conn_t i2s_periph_signal[SOC_I2S_NUM] = { }, { .mck_out_sig = I2S1_MCLK_OUT_IDX, + .mck_in_sig = I2S1_MCLK_IN_IDX, .m_tx_bck_sig = I2S1O_BCK_OUT_IDX, .m_rx_bck_sig = I2S1I_BCK_OUT_IDX, diff --git a/components/soc/esp32s3/include/soc/clk_tree_defs.h b/components/soc/esp32s3/include/soc/clk_tree_defs.h index 1ef1c17a06..626edc29aa 100644 --- a/components/soc/esp32s3/include/soc/clk_tree_defs.h +++ b/components/soc/esp32s3/include/soc/clk_tree_defs.h @@ -262,7 +262,7 @@ typedef enum { /** * @brief Array initializer for all supported clock sources of I2S */ -#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL} +#define SOC_I2S_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL, I2S_CLK_SRC_EXTERNAL} /** * @brief I2S clock source enum @@ -271,6 +271,7 @@ typedef enum { I2S_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default source clock */ I2S_CLK_SRC_PLL_160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ I2S_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + I2S_CLK_SRC_EXTERNAL = -1, /*!< Select external clock as source clock */ } soc_periph_i2s_clk_src_t; /////////////////////////////////////////////////I2C//////////////////////////////////////////////////////////////////// diff --git a/components/soc/include/soc/i2s_periph.h b/components/soc/include/soc/i2s_periph.h index d1e83b91f1..683576bf2b 100644 --- a/components/soc/include/soc/i2s_periph.h +++ b/components/soc/include/soc/i2s_periph.h @@ -24,6 +24,7 @@ extern "C" { */ typedef struct { const uint8_t mck_out_sig; + const uint8_t mck_in_sig; const uint8_t m_tx_bck_sig; const uint8_t m_rx_bck_sig; From 22bb5729a45b22df77a2ee8fa31eb0825cc48126 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Fri, 21 Jul 2023 11:58:03 +0800 Subject: [PATCH 2/2] ci(i2s): add external clock input multi_dev test cases --- components/driver/.build-test-rules.yml | 4 +- .../driver/i2s/include/driver/i2s_std.h | 4 +- .../{i2s_tdm => i2s_multi_dev}/CMakeLists.txt | 2 +- .../{i2s_tdm => i2s_multi_dev}/README.md | 0 .../main/CMakeLists.txt | 2 +- .../main/test_app_main.c | 0 .../main/test_i2s_multi_dev.c} | 300 +++++++++++------- .../pytest_i2s_multi_dev.py} | 4 +- .../sdkconfig.defaults | 0 9 files changed, 187 insertions(+), 129 deletions(-) rename components/driver/test_apps/i2s_test_apps/{i2s_tdm => i2s_multi_dev}/CMakeLists.txt (92%) rename components/driver/test_apps/i2s_test_apps/{i2s_tdm => i2s_multi_dev}/README.md (100%) rename components/driver/test_apps/i2s_test_apps/{i2s_tdm => i2s_multi_dev}/main/CMakeLists.txt (65%) rename components/driver/test_apps/i2s_test_apps/{i2s_tdm => i2s_multi_dev}/main/test_app_main.c (100%) rename components/driver/test_apps/i2s_test_apps/{i2s_tdm/main/test_i2s_tdm_full_duplex.c => i2s_multi_dev/main/test_i2s_multi_dev.c} (56%) rename components/driver/test_apps/i2s_test_apps/{i2s_tdm/pytest_i2s_tdm_full_duplex.py => i2s_multi_dev/pytest_i2s_multi_dev.py} (71%) rename components/driver/test_apps/i2s_test_apps/{i2s_tdm => i2s_multi_dev}/sdkconfig.defaults (100%) diff --git a/components/driver/.build-test-rules.yml b/components/driver/.build-test-rules.yml index fc4e3f03bd..18dd6591b7 100644 --- a/components/driver/.build-test-rules.yml +++ b/components/driver/.build-test-rules.yml @@ -24,9 +24,9 @@ components/driver/test_apps/i2s_test_apps/i2s: disable: - if: SOC_I2S_SUPPORTED != 1 -components/driver/test_apps/i2s_test_apps/i2s_tdm: +components/driver/test_apps/i2s_test_apps/i2s_multi_dev: disable: - - if: SOC_I2S_SUPPORTS_TDM != 1 + - if: SOC_I2S_HW_VERSION_2 != 1 components/driver/test_apps/i2s_test_apps/legacy_i2s_adc_dac: disable: diff --git a/components/driver/i2s/include/driver/i2s_std.h b/components/driver/i2s/include/driver/i2s_std.h index adf8013acc..3fb0ab05c4 100644 --- a/components/driver/i2s/include/driver/i2s_std.h +++ b/components/driver/i2s/include/driver/i2s_std.h @@ -238,11 +238,11 @@ typedef struct { /* General fields */ uint32_t sample_rate_hz; /*!< I2S sample rate */ i2s_clock_src_t clk_src; /*!< Choose clock source, see 'soc_periph_i2s_clk_src_t' for the supported clock sources. - * selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock inputted via MCLK pin, + * selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock input via MCLK pin, */ #if SOC_I2S_HW_VERSION_2 uint32_t ext_clk_freq_hz; /*!< External clock source frequency in Hz, only take effect when 'clk_src = I2S_CLK_SRC_EXTERNAL', otherwise this field will be ignored, - * Please make sure the frequency inputted is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * 2' + * Please make sure the frequency input is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * 2' */ #endif i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate diff --git a/components/driver/test_apps/i2s_test_apps/i2s_tdm/CMakeLists.txt b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/CMakeLists.txt similarity index 92% rename from components/driver/test_apps/i2s_test_apps/i2s_tdm/CMakeLists.txt rename to components/driver/test_apps/i2s_test_apps/i2s_multi_dev/CMakeLists.txt index d4ac1c8599..fa66d91f0e 100644 --- a/components/driver/test_apps/i2s_test_apps/i2s_tdm/CMakeLists.txt +++ b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/CMakeLists.txt @@ -10,4 +10,4 @@ set(EXTRA_COMPONENT_DIRS ) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(i2s_tdm_full_duplex_test) +project(i2s_multi_dev_test) diff --git a/components/driver/test_apps/i2s_test_apps/i2s_tdm/README.md b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/README.md similarity index 100% rename from components/driver/test_apps/i2s_test_apps/i2s_tdm/README.md rename to components/driver/test_apps/i2s_test_apps/i2s_multi_dev/README.md diff --git a/components/driver/test_apps/i2s_test_apps/i2s_tdm/main/CMakeLists.txt b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/CMakeLists.txt similarity index 65% rename from components/driver/test_apps/i2s_test_apps/i2s_tdm/main/CMakeLists.txt rename to components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/CMakeLists.txt index 356755d43c..b6f45ab7cb 100644 --- a/components/driver/test_apps/i2s_test_apps/i2s_tdm/main/CMakeLists.txt +++ b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "test_app_main.c" "test_i2s_tdm_full_duplex.c" +idf_component_register(SRCS "test_app_main.c" "test_i2s_multi_dev.c" PRIV_REQUIRES unity driver test_utils INCLUDE_DIRS "." WHOLE_ARCHIVE diff --git a/components/driver/test_apps/i2s_test_apps/i2s_tdm/main/test_app_main.c b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/test_app_main.c similarity index 100% rename from components/driver/test_apps/i2s_test_apps/i2s_tdm/main/test_app_main.c rename to components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/test_app_main.c diff --git a/components/driver/test_apps/i2s_test_apps/i2s_tdm/main/test_i2s_tdm_full_duplex.c b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/test_i2s_multi_dev.c similarity index 56% rename from components/driver/test_apps/i2s_test_apps/i2s_tdm/main/test_i2s_tdm_full_duplex.c rename to components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/test_i2s_multi_dev.c index 3af9830e49..4a26b391b5 100644 --- a/components/driver/test_apps/i2s_test_apps/i2s_tdm/main/test_i2s_tdm_full_duplex.c +++ b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/main/test_i2s_multi_dev.c @@ -5,6 +5,7 @@ */ #include #include +#include #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" @@ -15,15 +16,19 @@ #include "test_utils.h" #include "driver/gpio.h" #include "driver/i2s_tdm.h" +#include "driver/i2s_std.h" +#include "soc/i2s_struct.h" -static const char *TAG = "i2s_tdm_full_duplex_test"; +static const char *TAG = "i2s_multi_dev_test"; #define TEST_I2S_FRAME_SIZE (128) // Frame numbers in every writing / reading #define TEST_I2S_ARRAY_LENGTH (1024) // The loop data length for verification +#define TEST_I2S_MAX_DATA (256) // The maximum data value in the data buffer #define TEST_I2S_MAX_FAIL_CNT (3) // Max broken packet count #define TEST_I2S_FRAME_TIMEOUT_SEC (10.0f) // Timeout seconds of waiting for a correct frame #define TEST_I2S_NUM (I2S_NUM_0) // ESP32-C3 has only I2S0 +#define TEST_I2S_MCK_IO (GPIO_NUM_0) #define TEST_I2S_BCK_IO (GPIO_NUM_4) #define TEST_I2S_WS_IO (GPIO_NUM_5) #if CONFIG_IDF_TARGET_ESP32H2 @@ -34,53 +39,17 @@ static const char *TAG = "i2s_tdm_full_duplex_test"; #define TEST_I2S_DI_IO (GPIO_NUM_7) // DI and DO gpio will be reversed on slave runner #endif // CONFIG_IDF_TARGET_ESP32H2 -typedef struct { - TaskHandle_t maintask_handle; - QueueHandle_t tx_queue; - i2s_chan_handle_t tx_channel_handle; - i2s_data_bit_width_t tx_data_bit_width; - i2s_tdm_slot_mask_t tdm_slot_mask; -} test_i2s_tdm_write_task_args_t; - typedef struct { uint32_t *buffer; uint32_t buffer_size; } test_i2s_tdm_write_buffer_t; -static void test_i2s_tdm_master_write_task(void *args) -{ - test_i2s_tdm_write_task_args_t *task_args = (test_i2s_tdm_write_task_args_t*)args; - - /* Allocate I2S tx buffer */ - uint32_t channel_count = 32 - __builtin_clz(task_args->tdm_slot_mask); - uint32_t tx_buffer_size = TEST_I2S_FRAME_SIZE * channel_count * (task_args->tx_data_bit_width / 8); - ESP_LOGI(TAG, "Allocating I2S TDM master tx buffer, size=%ld", tx_buffer_size); - uint32_t *tx_buffer = malloc(tx_buffer_size); - TEST_ASSERT(tx_buffer); - - uint32_t data_cnt = 0; - size_t bytes_written = 0; - - /* Block here waiting for the main thread receiving Slave Ready signals */ - xTaskNotifyWait(0, ULONG_MAX, NULL, portMAX_DELAY); - - ESP_LOGI(TAG, "I2S TDM master send start"); - while (xTaskNotifyWait(0, ULONG_MAX, NULL, 0) == pdFALSE) { // if main task sends terminate signal, exit the loop - /* Fill in the tx buffer */ - for (uint32_t i = 0; i < tx_buffer_size / sizeof(uint32_t); i ++) { - tx_buffer[i] = data_cnt; - data_cnt++; - data_cnt %= TEST_I2S_ARRAY_LENGTH; - } - TEST_ESP_OK(i2s_channel_write(task_args->tx_channel_handle, tx_buffer, tx_buffer_size, - &bytes_written, 1000)); - } - - ESP_LOGI(TAG, "Freeing I2S TDM master tx buffer"); - free(tx_buffer); - - xTaskNotifyGive(task_args->maintask_handle); // notify main task that cleanup is done - vTaskSuspend(NULL); // wait to be deleted +#define TEST_I2S_DEFAULT_GPIO(mclk_pin, is_master) { \ + .mclk = mclk_pin, \ + .bclk = TEST_I2S_BCK_IO, \ + .ws = TEST_I2S_WS_IO, \ + .dout = is_master ? TEST_I2S_DO_IO : TEST_I2S_DI_IO, \ + .din = is_master ? TEST_I2S_DI_IO : TEST_I2S_DO_IO, \ } static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_width, i2s_tdm_slot_mask_t slot_mask) @@ -102,93 +71,70 @@ static void test_i2s_tdm_master(uint32_t sample_rate, i2s_data_bit_width_t bit_w i2s_tdm_config_t i2s_tdm_config = { .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(sample_rate), .slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(bit_width, I2S_SLOT_MODE_STEREO, slot_mask), - .gpio_cfg = { - .mclk = GPIO_NUM_NC, - .bclk = TEST_I2S_BCK_IO, - .ws = TEST_I2S_WS_IO, - .dout = TEST_I2S_DO_IO, - .din = TEST_I2S_DI_IO - }, + .gpio_cfg = TEST_I2S_DEFAULT_GPIO(GPIO_NUM_NC, true), }; i2s_tdm_config.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384; TEST_ESP_OK(i2s_channel_init_tdm_mode(i2s_tdm_tx_handle, &i2s_tdm_config)); TEST_ESP_OK(i2s_channel_init_tdm_mode(i2s_tdm_rx_handle, &i2s_tdm_config)); - /* Create TDM write task */ - TaskHandle_t subtask_handle = NULL; - /* Make the variable static in case it become invalid in the write task */ - static test_i2s_tdm_write_task_args_t task_args; - task_args.tx_channel_handle = i2s_tdm_tx_handle; - task_args.maintask_handle = xTaskGetCurrentTaskHandle(); - task_args.tx_data_bit_width = bit_width; - task_args.tdm_slot_mask = slot_mask; - xTaskCreate(test_i2s_tdm_master_write_task, "I2S TDM Write Task", 4096, &task_args, 5, &subtask_handle); - - /* Allocate I2S rx buffer */ uint32_t channel_count = 32 - __builtin_clz(slot_mask); - uint32_t rx_buffer_size = channel_count * TEST_I2S_FRAME_SIZE * (bit_width / 8); - ESP_LOGI(TAG, "Allocating I2S TDM master rx buffer, size=%"PRIu32, rx_buffer_size); - uint32_t *rx_buffer = malloc(rx_buffer_size); + size_t buf_size = channel_count * TEST_I2S_FRAME_SIZE * (bit_width / 8); + /* Allocate I2S rx buffer */ + ESP_LOGI(TAG, "Allocating I2S TDM master rx buffer, size=%u", buf_size); + uint32_t *rx_buffer = malloc(buf_size); TEST_ASSERT(rx_buffer); + /* Allocate I2S tx buffer */ + ESP_LOGI(TAG, "Allocating I2S TDM master tx buffer, size=%u", buf_size); + uint32_t *tx_buffer = malloc(buf_size); + TEST_ASSERT(tx_buffer); + /* Fill in the tx buffer */ + for (uint32_t i = 0, data_cnt = 0; i < buf_size / sizeof(uint32_t); i ++) { + tx_buffer[i] = data_cnt; + data_cnt++; + data_cnt %= TEST_I2S_MAX_DATA; + } + size_t w_bytes = buf_size; + while (w_bytes != 0) { + ESP_ERROR_CHECK(i2s_channel_preload_data(i2s_tdm_tx_handle, tx_buffer, buf_size, &w_bytes)); + } - uint32_t count = 1; - bool is_start = false; - uint8_t fail_cnt = 0; - size_t bytes_read = 0; - float time = 0; + unity_wait_for_signal("Slave Ready"); TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle)); + vTaskDelay(pdMS_TO_TICKS(100)); // Waiting the data be steady on line unity_send_signal("Master Ready"); - unity_wait_for_signal("Slave Ready"); /* Slave is ready, start the writing task */ ESP_LOGI(TAG, "I2S TDM master receive & send start"); esp_err_t read_ret = ESP_OK; - xTaskNotifyGive(subtask_handle); - while (count < TEST_I2S_ARRAY_LENGTH && fail_cnt < TEST_I2S_MAX_FAIL_CNT && time < TEST_I2S_FRAME_TIMEOUT_SEC) { - read_ret = i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, rx_buffer_size, &bytes_read, 1000); - if (read_ret != ESP_OK) { - break; + uint32_t count = 1; + uint8_t fail_cnt = 0; + size_t bytes_read = 0; + for (fail_cnt = 0; fail_cnt < TEST_I2S_MAX_FAIL_CNT && count < TEST_I2S_MAX_DATA; fail_cnt++) { + if (i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, buf_size, &bytes_read, 1000) != ESP_OK) { + continue; } - for (int i = 0; i < rx_buffer_size / sizeof(uint32_t); i++) { + for (int i = 0; i < buf_size && count < TEST_I2S_MAX_DATA; i++) { if (rx_buffer[i] == count) { count++; - if (count >= TEST_I2S_ARRAY_LENGTH) { - break; - } - if (!is_start) { - is_start = true; - } - } else if (is_start) { + } else if (count != 1) { ESP_LOGE(TAG, "Failed at index: %d real: %" PRIu32 " expect: %" PRIu32, i, rx_buffer[i], count); - is_start = false; count = 1; - fail_cnt++; } } - time += (float)TEST_I2S_MAX_FAIL_CNT / (float)sample_rate; } - unity_send_signal("Master Finished"); - ESP_LOGI(TAG, "Send signal to terminate subtask"); - xTaskNotifyGive(subtask_handle); // notify subtask to exit - xTaskNotifyWait(0, ULONG_MAX, NULL, portMAX_DELAY); // wait subtask to do some cleanups - ESP_LOGI(TAG, "Deleting subtask"); - unity_utils_task_delete(subtask_handle); // delete subtask - - ESP_LOGI(TAG, "I2S TDM master send stop"); + ESP_LOGI(TAG, "I2S TDM master stop"); TEST_ESP_OK(i2s_channel_disable(i2s_tdm_tx_handle)); - ESP_LOGI(TAG, "I2S TDM master receive stop"); TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle)); - - ESP_LOGI(TAG, "Freeing I2S TDM master rx buffer"); free(rx_buffer); - ESP_LOGI(TAG, "Deleting i2s tx and rx channels"); + free(tx_buffer); TEST_ESP_OK(i2s_del_channel(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_del_channel(i2s_tdm_tx_handle)); + ESP_LOGI(TAG, "I2S TDM master resources freed"); TEST_ASSERT_TRUE_MESSAGE(read_ret == ESP_OK, "Master read timeout "); - TEST_ASSERT_TRUE_MESSAGE(fail_cnt < TEST_I2S_MAX_FAIL_CNT, "Broken data received "); - TEST_ASSERT_TRUE_MESSAGE(time < TEST_I2S_FRAME_TIMEOUT_SEC, "Waiting for valid data timeout "); + TEST_ASSERT_TRUE_MESSAGE(fail_cnt < TEST_I2S_MAX_FAIL_CNT, "Exceed retry times "); + TEST_ASSERT_EQUAL_UINT32(TEST_I2S_MAX_DATA, count); } static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_width, i2s_tdm_slot_mask_t slot_mask) @@ -210,13 +156,7 @@ static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_wi i2s_tdm_config_t i2s_tdm_config = { .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(sample_rate), .slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(bit_width, I2S_SLOT_MODE_STEREO, slot_mask), - .gpio_cfg = { - .mclk = GPIO_NUM_NC, - .bclk = TEST_I2S_BCK_IO, - .ws = TEST_I2S_WS_IO, - .dout = TEST_I2S_DI_IO, - .din = TEST_I2S_DO_IO // on slave, swap DI and DO pin - }, + .gpio_cfg = TEST_I2S_DEFAULT_GPIO(GPIO_NUM_NC, false), }; if (sample_rate >= 96000) { i2s_tdm_config.clk_cfg.bclk_div = 12; @@ -226,39 +166,35 @@ static void test_i2s_tdm_slave(uint32_t sample_rate, i2s_data_bit_width_t bit_wi /* Allocate I2S rx buffer */ uint32_t channel_count = 32 - __builtin_clz(slot_mask); - uint32_t rx_buffer_size = TEST_I2S_FRAME_SIZE * channel_count * (bit_width / 8); - ESP_LOGI(TAG, "Allocating I2S TDM slave buffer, size=%ld", rx_buffer_size); - uint32_t *rx_buffer = malloc(rx_buffer_size); - TEST_ASSERT(rx_buffer); + uint32_t buffer_size = TEST_I2S_FRAME_SIZE * channel_count * (bit_width / 8); + ESP_LOGI(TAG, "Allocating I2S TDM slave buffer, size=%"PRIu32, buffer_size); + uint32_t *echo_buffer = malloc(buffer_size); + TEST_ASSERT(echo_buffer); - TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle)); - TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle)); unity_send_signal("Slave Ready"); unity_wait_for_signal("Master Ready"); + TEST_ESP_OK(i2s_channel_enable(i2s_tdm_rx_handle)); + TEST_ESP_OK(i2s_channel_enable(i2s_tdm_tx_handle)); ESP_LOGI(TAG, "I2S TDM slave receive & send start"); size_t bytes_read = 0, bytes_written = 0; /* Loop until reading or writing failed, which indicates the master has finished and deleted the I2S peripheral */ while (true) { - if (i2s_channel_read(i2s_tdm_rx_handle, rx_buffer, rx_buffer_size, &bytes_read, 500) != ESP_OK) { + if (i2s_channel_read(i2s_tdm_rx_handle, echo_buffer, buffer_size, &bytes_read, 500) != ESP_OK) { break; } - if (i2s_channel_write(i2s_tdm_tx_handle, rx_buffer, rx_buffer_size, &bytes_written, 500) != ESP_OK) { + if (i2s_channel_write(i2s_tdm_tx_handle, echo_buffer, buffer_size, &bytes_written, 500) != ESP_OK) { break; } } - unity_wait_for_signal("Master Finished"); ESP_LOGI(TAG, "I2S TDM slave receive stop"); TEST_ESP_OK(i2s_channel_disable(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_channel_disable(i2s_tdm_tx_handle)); - - ESP_LOGI(TAG, "Freeing I2S TDM slave buffer"); - free(rx_buffer); - - ESP_LOGI(TAG, "Deleting i2s tx and rx channels"); + free(echo_buffer); TEST_ESP_OK(i2s_del_channel(i2s_tdm_rx_handle)); TEST_ESP_OK(i2s_del_channel(i2s_tdm_tx_handle)); + ESP_LOGI(TAG, "I2S TDM slave resources freed"); } static void test_i2s_tdm_master_48k_32bits_4slots(void) @@ -334,3 +270,125 @@ static void test_i2s_tdm_slave_96k_16bits_4slots(void) TEST_CASE_MULTIPLE_DEVICES("I2S_TDM_full_duplex_test_in_96k_16bits_4slots", "[I2S_TDM]", test_i2s_tdm_master_96k_16bits_4slots, test_i2s_tdm_slave_96k_16bits_4slots); #endif // !CONFIG_IDF_TARGET_ESP32H2 + +static void test_i2s_external_clk_src(bool is_master, bool is_external) +{ + i2s_chan_handle_t tx_handle; + i2s_chan_handle_t rx_handle; + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE); + TEST_ESP_OK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle)); + i2s_std_config_t std_cfg = { + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100), + .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO), + .gpio_cfg = TEST_I2S_DEFAULT_GPIO(TEST_I2S_MCK_IO, is_master), + }; + if (is_external) { + std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_EXTERNAL; + std_cfg.clk_cfg.ext_clk_freq_hz = 11289600; + } + TEST_ESP_OK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); + if (is_master && !is_external) { + i2s_std_slot_config_t slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO); + memcpy(&std_cfg.slot_cfg, &slot_cfg, sizeof(i2s_std_slot_config_t)); + } + TEST_ESP_OK(i2s_channel_init_std_mode(rx_handle, &std_cfg)); + + if (is_master) { + if (!is_external) { + // Delay bclk to get compensate the data delay + I2S0.rx_timing.rx_bck_out_dm = 1; + } + uint8_t mst_tx_data[4] = {0x12, 0x34, 0x56, 0x78}; + size_t w_bytes = 4; + while (w_bytes == 4) { + ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, mst_tx_data, 4, &w_bytes)); + } + } + + uint8_t *recv_buff = (uint8_t *)calloc(1, TEST_I2S_ARRAY_LENGTH); + TEST_ASSERT(recv_buff); + + unity_send_signal(is_master ? "Master Ready" : "Slave Ready"); + unity_wait_for_signal(is_master ? "Slave Ready" : "Master Ready"); + if (is_external) { + unity_wait_for_signal("External Clock Ready"); + } + TEST_ESP_OK(i2s_channel_enable(tx_handle)); + TEST_ESP_OK(i2s_channel_enable(rx_handle)); + if (!is_external) { + unity_send_signal("External Clock Ready"); + } + + bool is_success = false; + size_t bytes_read = 0; + if (is_master) { + if (is_external) { + unity_send_signal("Master Data Ready"); + } + // Wait the Slave data ready on line + unity_wait_for_signal("Slave Data Ready"); + vTaskDelay(pdMS_TO_TICKS(100)); // Wait another 100 ms for the data to be steady in DMA buffer + TEST_ESP_OK(i2s_channel_read(rx_handle, recv_buff, TEST_I2S_ARRAY_LENGTH, &bytes_read, 1000)); + } else { + if (!is_external) { + unity_wait_for_signal("Master Data Ready"); + } + TEST_ESP_OK(i2s_channel_read(rx_handle, recv_buff, TEST_I2S_ARRAY_LENGTH, &bytes_read, 1000)); + // Fill the DMA buffer + for (int i = 0; i < 6; i++) { + TEST_ESP_OK(i2s_channel_write(tx_handle, recv_buff, TEST_I2S_ARRAY_LENGTH, &bytes_read, 1000)); + } + // Send the signal indicates the data have been ready on line + unity_send_signal("Slave Data Ready"); + } + // Check the data + for (int i = 0; i < TEST_I2S_ARRAY_LENGTH; i++) { + if (recv_buff[i] == 0x12 && recv_buff[i + 1] == 0x34 && + recv_buff[i + 2] == 0x56 && recv_buff[i + 3] == 0x78) { + is_success = true; + break; + } + printf("%x ", recv_buff[i]); + } + printf("\n"); + + if (is_master) { + unity_send_signal("Master Finished"); + } else { + unity_wait_for_signal("Master Finished"); + } + // Disable and free the resources + TEST_ESP_OK(i2s_channel_disable(rx_handle)); + TEST_ESP_OK(i2s_channel_disable(tx_handle)); + free(recv_buff); + TEST_ESP_OK(i2s_del_channel(rx_handle)); + TEST_ESP_OK(i2s_del_channel(tx_handle)); + // Assert whether the test success + TEST_ASSERT(is_success); +} + +static void test_i2s_master_clock_out(void) +{ + test_i2s_external_clk_src(true, false); +} + +static void test_i2s_slave_clock_in(void) +{ + test_i2s_external_clk_src(false, true); +} + +TEST_CASE_MULTIPLE_DEVICES("I2S_external_clock_master_output_slave_input", "[I2S]", + test_i2s_master_clock_out, test_i2s_slave_clock_in); + +static void test_i2s_master_clock_in(void) +{ + test_i2s_external_clk_src(true, true); +} + +static void test_i2s_slave_clock_out(void) +{ + test_i2s_external_clk_src(false, false); +} + +TEST_CASE_MULTIPLE_DEVICES("I2S_external_clock_master_input_slave_output", "[I2S]", + test_i2s_master_clock_in, test_i2s_slave_clock_out); diff --git a/components/driver/test_apps/i2s_test_apps/i2s_tdm/pytest_i2s_tdm_full_duplex.py b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/pytest_i2s_multi_dev.py similarity index 71% rename from components/driver/test_apps/i2s_test_apps/i2s_tdm/pytest_i2s_tdm_full_duplex.py rename to components/driver/test_apps/i2s_test_apps/i2s_multi_dev/pytest_i2s_multi_dev.py index 458e6aa28a..81fe933e11 100644 --- a/components/driver/test_apps/i2s_test_apps/i2s_tdm/pytest_i2s_tdm_full_duplex.py +++ b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/pytest_i2s_multi_dev.py @@ -11,5 +11,5 @@ import pytest @pytest.mark.parametrize('count', [ 2, ], indirect=True) -def test_i2s_tdm_full_duplex(case_tester) -> None: # type: ignore - case_tester.run_all_cases(timeout=30) +def test_i2s_multi_dev(case_tester) -> None: # type: ignore + case_tester.run_all_multi_dev_cases(reset=True, timeout=30) diff --git a/components/driver/test_apps/i2s_test_apps/i2s_tdm/sdkconfig.defaults b/components/driver/test_apps/i2s_test_apps/i2s_multi_dev/sdkconfig.defaults similarity index 100% rename from components/driver/test_apps/i2s_test_apps/i2s_tdm/sdkconfig.defaults rename to components/driver/test_apps/i2s_test_apps/i2s_multi_dev/sdkconfig.defaults