From b8218f4fe126c2523fbda76610d65b62f70fac81 Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Tue, 9 Jun 2026 17:18:01 +0800 Subject: [PATCH] refactor(i2s): combine separate callback registers into single one --- components/esp_driver_i2s/i2s_common.c | 216 +++++++++++------- components/esp_driver_i2s/i2s_pdm.c | 3 + components/esp_driver_i2s/i2s_private.h | 19 +- components/esp_driver_i2s/i2s_std.c | 5 + components/esp_driver_i2s/i2s_tdm.c | 5 + .../include/driver/i2s_common.h | 85 +++---- .../esp_driver_i2s/include/driver/i2s_types.h | 4 +- components/esp_driver_i2s/linker.lf | 2 +- .../test_apps/i2s/main/test_i2s.c | 24 +- docs/en/api-reference/peripherals/i2s.rst | 47 ++-- docs/zh_CN/api-reference/peripherals/i2s.rst | 50 ++-- 11 files changed, 263 insertions(+), 197 deletions(-) diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 7f6407f1be9..9f67e48386c 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -76,6 +76,12 @@ static const char *TAG = "i2s_common"; +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC +static void s_i2s_channel_update_tx_sync_callback(i2s_chan_handle_t tx_handle, + i2s_tx_fifo_sync_callback_t cb, + void *user_data); +#endif + __attribute__((always_inline)) inline void *i2s_dma_calloc(i2s_chan_handle_t handle, size_t num, size_t size) { @@ -432,10 +438,20 @@ esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2 { I2S_NULL_POINTER_CHECK(TAG, handle); I2S_NULL_POINTER_CHECK(TAG, callbacks); - /* on_recv/on_sent are dispatched from the DMA ISR. If this channel does not use the DMA memory path, no such callback fires. */ - ESP_RETURN_ON_FALSE(I2S_CHANNEL_USES_DMA(handle), ESP_ERR_NOT_SUPPORTED, TAG, - "event callbacks require the DMA memory data path on this channel"); esp_err_t ret = ESP_OK; + bool has_dma_event_cb = callbacks->on_recv || callbacks->on_recv_q_ovf || + callbacks->on_sent || callbacks->on_send_q_ovf; + bool update_dma_cb = I2S_CHANNEL_USES_DMA(handle); +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + i2s_tx_fifo_sync_callback_t sync_cb = callbacks->on_tx_sync_evt; + bool update_sync_cb = sync_cb || (handle->dir == I2S_DIR_TX && handle->on_tx_sync); +#endif + ESP_RETURN_ON_FALSE(!has_dma_event_cb || update_dma_cb, + ESP_ERR_NOT_SUPPORTED, TAG, + "DMA event callbacks require the DMA memory data path on this channel"); +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + ESP_RETURN_ON_FALSE(!update_sync_cb || handle->dir == I2S_DIR_TX, ESP_ERR_INVALID_ARG, TAG, "channel is not TX"); +#endif #if CONFIG_I2S_ISR_IRAM_SAFE if (callbacks->on_recv) { ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callbacks->on_recv), ESP_ERR_INVALID_ARG, TAG, "on_recv callback not in IRAM"); @@ -449,15 +465,31 @@ esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2 if (callbacks->on_send_q_ovf) { ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callbacks->on_send_q_ovf), ESP_ERR_INVALID_ARG, TAG, "on_send_q_ovf callback not in IRAM"); } +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + if (sync_cb) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(sync_cb), ESP_ERR_INVALID_ARG, TAG, "sync callback not in IRAM"); + } +#endif if (user_data) { ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM"); } + #endif xSemaphoreTake(handle->mutex, portMAX_DELAY); ESP_GOTO_ON_FALSE(handle->state < I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, err, TAG, "invalid state, I2S has enabled"); - memcpy(&(handle->callbacks), callbacks, sizeof(i2s_event_callbacks_t)); - handle->user_data = user_data; + if (update_dma_cb) { + handle->callbacks.on_recv = callbacks->on_recv; + handle->callbacks.on_recv_q_ovf = callbacks->on_recv_q_ovf; + handle->callbacks.on_sent = callbacks->on_sent; + handle->callbacks.on_send_q_ovf = callbacks->on_send_q_ovf; + handle->user_data = user_data; + } +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + if (update_sync_cb) { + s_i2s_channel_update_tx_sync_callback(handle, sync_cb, user_data); + } +#endif err: xSemaphoreGive(handle->mutex); return ret; @@ -1162,6 +1194,15 @@ esp_err_t i2s_del_channel(i2s_chan_handle_t handle) i2s_dir_t __attribute__((unused)) dir = handle->dir; bool is_bound = true; +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + if (handle->i2s_intr) { + i2s_ll_enable_interrupt(handle->controller->hal.dev, I2S_LL_TX_SYNC_INT_EVENT, false); + esp_intr_disable(handle->i2s_intr); + esp_intr_free(handle->i2s_intr); + handle->i2s_intr = NULL; + } +#endif + #if SOC_I2S_SUPPORTS_APLL /* Must switch back to D2CLK on ESP32-S2, * because the clock of some registers are bound to APLL, @@ -1221,14 +1262,6 @@ esp_err_t i2s_del_channel(i2s_chan_handle_t handle) if (handle->binary) { vSemaphoreDeleteWithCaps(handle->binary); } -#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC - if (handle->sync_intr) { - i2s_ll_enable_interrupt(handle->controller->hal.dev, I2S_LL_TX_SYNC_INT_EVENT, false); - esp_intr_disable(handle->sync_intr); - esp_intr_free(handle->sync_intr); - handle->sync_intr = NULL; - } -#endif #if SOC_I2S_HW_VERSION_1 i2s_obj->chan_occupancy = 0; #else @@ -1650,31 +1683,35 @@ static inline esp_err_t i2s_check_tx_handle(i2s_chan_handle_t tx_handle) return ESP_OK; } -esp_err_t i2s_channel_get_sync_count(i2s_chan_handle_t tx_handle, uint32_t *bclk_count, uint32_t *fifo_count, - bool reset) -{ - ESP_RETURN_ON_ERROR(i2s_check_tx_handle(tx_handle), TAG, "invalid TX handle"); - i2s_dev_t *hw = tx_handle->controller->hal.dev; - if (bclk_count) { - *bclk_count = i2s_ll_tx_get_bclk_sync_count(hw); - } - if (fifo_count) { - *fifo_count = i2s_ll_tx_get_fifo_sync_count(hw); - } - if (reset) { - i2s_ll_tx_reset_bclk_sync_counter(hw); - i2s_ll_tx_reset_fifo_sync_counter(hw); - } - return ESP_OK; -} - #if SOC_I2S_SUPPORTS_TX_FIFO_SYNC __attribute__((always_inline)) static inline int32_t i2s_sign_extend_sync_diff(uint32_t diff) { return (diff & BIT(30)) ? (int32_t)(diff | BIT(31)) : (int32_t)diff; } +#endif +esp_err_t i2s_channel_get_sync_count(i2s_chan_handle_t tx_handle, i2s_sync_count_t *count, bool reset) +{ + ESP_RETURN_ON_ERROR(i2s_check_tx_handle(tx_handle), TAG, "invalid TX handle"); + I2S_NULL_POINTER_CHECK(TAG, count); + i2s_dev_t *hw = tx_handle->controller->hal.dev; + count->bclk_count = i2s_ll_tx_get_bclk_sync_count(hw); + count->fifo_count = i2s_ll_tx_get_fifo_sync_count(hw); +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + count->diff_count = i2s_sign_extend_sync_diff(i2s_ll_tx_get_fifo_sync_diff_count(hw)); +#endif + if (reset) { + i2s_ll_tx_reset_bclk_sync_counter(hw); + i2s_ll_tx_reset_fifo_sync_counter(hw); +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + i2s_ll_tx_reset_fifo_sync_diff_counter(hw); +#endif + } + return ESP_OK; +} + +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC #if CONFIG_I2S_ISR_IRAM_SAFE #define I2S_ISR_HANDLER_ATTR IRAM_ATTR #else @@ -1693,22 +1730,65 @@ static I2S_ISR_HANDLER_ATTR void i2s_isr_handler(void *args) i2s_ll_tx_reset_fifo_sync_diff_counter(hw); i2s_ll_clear_interrupt_status(hw, I2S_LL_TX_SYNC_INT_EVENT); - if (handle->on_sync && handle->on_sync(handle, &evt, handle->sync_user_data)) { + if (handle->on_tx_sync && handle->on_tx_sync(handle, &evt, handle->sync_user_data)) { portYIELD_FROM_ISR(); } } } -esp_err_t i2s_channel_get_sync_diff_count(i2s_chan_handle_t tx_handle, int32_t *diff_count, bool reset) +static void s_i2s_channel_update_tx_sync_callback(i2s_chan_handle_t tx_handle, + i2s_tx_fifo_sync_callback_t cb, + void *user_data) { - ESP_RETURN_ON_ERROR(i2s_check_tx_handle(tx_handle), TAG, "invalid TX handle"); i2s_dev_t *hw = tx_handle->controller->hal.dev; - if (diff_count) { - *diff_count = i2s_sign_extend_sync_diff(i2s_ll_tx_get_fifo_sync_diff_count(hw)); + + portENTER_CRITICAL(&g_i2s.spinlock); + if (cb) { + tx_handle->on_tx_sync = cb; + tx_handle->sync_user_data = user_data; + i2s_ll_clear_interrupt_status(hw, I2S_LL_TX_SYNC_INT_EVENT); + i2s_ll_enable_interrupt(hw, I2S_LL_TX_SYNC_INT_EVENT, true); + } else { + i2s_ll_enable_interrupt(hw, I2S_LL_TX_SYNC_INT_EVENT, false); + tx_handle->on_tx_sync = NULL; + tx_handle->sync_user_data = NULL; } - if (reset) { - i2s_ll_tx_reset_fifo_sync_diff_counter(hw); + portEXIT_CRITICAL(&g_i2s.spinlock); +} + +esp_err_t i2s_init_i2s_intr(i2s_chan_handle_t handle) +{ + esp_err_t ret = ESP_OK; + ESP_RETURN_ON_ERROR(i2s_check_tx_handle(handle), TAG, "invalid TX handle"); + if (handle->i2s_intr) { + return ESP_OK; } + + i2s_dev_t *hw = handle->controller->hal.dev; + int port_id = handle->controller->id; + int intr_flag = handle->intr_prio_flags; +#if CONFIG_I2S_ISR_IRAM_SAFE + intr_flag |= ESP_INTR_FLAG_IRAM; +#endif + + i2s_ll_enable_interrupt(hw, I2S_LL_TX_SYNC_INT_EVENT, false); + i2s_ll_clear_interrupt_status(hw, I2S_LL_TX_SYNC_INT_EVENT); + ret = esp_intr_alloc_intrstatus(i2s_periph_signal[port_id].irq, intr_flag, + (uint32_t)i2s_ll_get_interrupt_status_reg(hw), + I2S_LL_TX_SYNC_INT_EVENT, i2s_isr_handler, handle, + &handle->i2s_intr); + ESP_RETURN_ON_ERROR(ret, TAG, "allocate I2S interrupt failed"); + ret = esp_intr_enable(handle->i2s_intr); + if (ret != ESP_OK) { + esp_intr_free(handle->i2s_intr); + handle->i2s_intr = NULL; + return ret; + } + if (handle->on_tx_sync) { + i2s_ll_clear_interrupt_status(hw, I2S_LL_TX_SYNC_INT_EVENT); + i2s_ll_enable_interrupt(hw, I2S_LL_TX_SYNC_INT_EVENT, true); + } + return ESP_OK; } @@ -1717,10 +1797,9 @@ esp_err_t i2s_channel_config_tx_fifo_sync(i2s_chan_handle_t tx_handle, const i2s esp_err_t ret = ESP_OK; ESP_RETURN_ON_ERROR(i2s_check_tx_handle(tx_handle), TAG, "invalid TX handle"); I2S_NULL_POINTER_CHECK(TAG, config); - ESP_RETURN_ON_FALSE(config->auto_suppl_thresh == 0 || - config->auto_suppl_thresh < config->manual_suppl_thresh, + ESP_RETURN_ON_FALSE(config->auto_suppl_thresh < config->manual_suppl_thresh, ESP_ERR_INVALID_ARG, TAG, - "auto_suppl_thresh must be 0 or smaller than manual_suppl_thresh"); + "auto_suppl_thresh must be smaller than manual_suppl_thresh"); i2s_dev_t *hw = tx_handle->controller->hal.dev; xSemaphoreTake(tx_handle->mutex, portMAX_DELAY); @@ -1733,66 +1812,31 @@ esp_err_t i2s_channel_config_tx_fifo_sync(i2s_chan_handle_t tx_handle, const i2s if (config->suppl_mode == I2S_TX_FIFO_SYNC_SUPPL_MODE_STATIC_DATA) { i2s_ll_tx_set_hw_fifo_sync_static_suppl_data(hw, config->suppl_data); } - i2s_ll_tx_enable_hw_fifo_sync(hw, config->auto_suppl_thresh > 0); + i2s_ll_tx_enable_hw_fifo_sync(hw, false); i2s_ll_tx_update(hw); err: xSemaphoreGive(tx_handle->mutex); return ret; } -esp_err_t i2s_channel_register_intr_event_callback(i2s_chan_handle_t tx_handle, - const i2s_intr_event_callbacks_t *callbacks, void *user_data) +esp_err_t i2s_channel_enable_tx_fifo_sync(i2s_chan_handle_t tx_handle, bool enable) { - esp_err_t ret = ESP_OK; ESP_RETURN_ON_ERROR(i2s_check_tx_handle(tx_handle), TAG, "invalid TX handle"); - I2S_NULL_POINTER_CHECK(TAG, callbacks); - i2s_sync_callback_t callback = callbacks->on_tx_sync; -#if CONFIG_I2S_ISR_IRAM_SAFE - if (callback) { - ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callback), ESP_ERR_INVALID_ARG, TAG, "sync callback not in IRAM"); - } - if (user_data) { - ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM"); - } -#endif + ESP_RETURN_ON_FALSE(tx_handle->i2s_intr, ESP_ERR_INVALID_STATE, TAG, "TX FIFO sync not configured"); i2s_dev_t *hw = tx_handle->controller->hal.dev; - xSemaphoreTake(tx_handle->mutex, portMAX_DELAY); - ESP_GOTO_ON_FALSE(tx_handle->state < I2S_CHAN_STATE_RUNNING, ESP_ERR_INVALID_STATE, err, TAG, - "invalid state, I2S has enabled"); - - if (callback) { - if (!tx_handle->sync_intr) { - int port_id = tx_handle->controller->id; - int intr_flag = ESP_INTR_FLAG_INTRDISABLED | tx_handle->intr_prio_flags; -#if CONFIG_I2S_ISR_IRAM_SAFE - intr_flag |= ESP_INTR_FLAG_IRAM; -#endif - ret = esp_intr_alloc_intrstatus(i2s_periph_signal[port_id].irq, intr_flag, - (uint32_t)i2s_ll_get_interrupt_status_reg(hw), - I2S_LL_TX_SYNC_INT_EVENT, i2s_isr_handler, tx_handle, - &tx_handle->sync_intr); - ESP_GOTO_ON_ERROR(ret, err, TAG, "allocate TX sync interrupt failed"); - ESP_GOTO_ON_ERROR(esp_intr_enable(tx_handle->sync_intr), err, TAG, "enable TX sync interrupt failed"); - } - tx_handle->on_sync = callback; - tx_handle->sync_user_data = user_data; + portENTER_CRITICAL(&g_i2s.spinlock); + i2s_ll_tx_enable_hw_fifo_sync(hw, enable); + if (enable && tx_handle->on_tx_sync) { i2s_ll_clear_interrupt_status(hw, I2S_LL_TX_SYNC_INT_EVENT); i2s_ll_enable_interrupt(hw, I2S_LL_TX_SYNC_INT_EVENT, true); } else { i2s_ll_enable_interrupt(hw, I2S_LL_TX_SYNC_INT_EVENT, false); - if (tx_handle->sync_intr) { - esp_intr_disable(tx_handle->sync_intr); - esp_intr_free(tx_handle->sync_intr); - tx_handle->sync_intr = NULL; - } - tx_handle->on_sync = NULL; - tx_handle->sync_user_data = NULL; } - -err: - xSemaphoreGive(tx_handle->mutex); - return ret; + i2s_ll_tx_update(hw); + portEXIT_CRITICAL(&g_i2s.spinlock); + return ESP_OK; } + #endif // SOC_I2S_SUPPORTS_TX_FIFO_SYNC #endif // SOC_I2S_SUPPORTS_TX_SYNC_CNT diff --git a/components/esp_driver_i2s/i2s_pdm.c b/components/esp_driver_i2s/i2s_pdm.c index 8a0751da416..19fe978c3ad 100644 --- a/components/esp_driver_i2s/i2s_pdm.c +++ b/components/esp_driver_i2s/i2s_pdm.c @@ -240,6 +240,9 @@ esp_err_t i2s_channel_init_pdm_tx_mode(i2s_chan_handle_t handle, const i2s_pdm_t if (I2S_CHANNEL_USES_DMA(handle)) { ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed"); } +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + ESP_GOTO_ON_ERROR(i2s_init_i2s_intr(handle), err, TAG, "initialize I2S interrupt failed"); +#endif i2s_ll_tx_enable_pdm(handle->controller->hal.dev, pdm_tx_cfg->slot_cfg.data_fmt == I2S_PDM_DATA_FMT_PCM); i2s_ll_set_destination(handle->controller->hal.dev, handle->dir, handle->destination); diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 5dbdf601da6..115c2ebb06b 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -90,6 +90,9 @@ typedef struct { i2s_isr_callback_t on_send_q_ovf; /**< Callback of sending queue overflowed event, only for tx channel * The event data includes buffer size that has been overwritten */ +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + i2s_tx_fifo_sync_callback_t on_tx_sync_evt; /**< Callback when the TX sync difference count exceeds the manual threshold */ +#endif } i2s_event_callbacks_internal_t; /** @@ -181,9 +184,9 @@ struct i2s_channel_obj_t { i2s_event_callbacks_internal_t callbacks; /*!< Callback functions */ void *user_data; /*!< User data for callback functions */ #if SOC_I2S_SUPPORTS_TX_FIFO_SYNC - i2s_sync_callback_t on_sync; /*!< TX FIFO sync manual supplement threshold callback */ + i2s_tx_fifo_sync_callback_t on_tx_sync; /*!< TX FIFO sync manual supplement threshold callback */ void *sync_user_data; /*!< User data for TX FIFO sync callback */ - intr_handle_t sync_intr; /*!< I2S peripheral interrupt for TX FIFO sync */ + intr_handle_t i2s_intr; /*!< I2S peripheral interrupt handle */ #endif void (*start)(i2s_chan_handle_t); /*!< start tx/rx channel */ void (*stop)(i2s_chan_handle_t); /*!< stop tx/rx channel */ @@ -244,6 +247,18 @@ extern i2s_platform_t g_i2s; /*!< Global i2s instance for driver internal use * */ esp_err_t i2s_init_dma_intr(i2s_chan_handle_t handle, int intr_flag); +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC +/** + * @brief Initialize I2S peripheral interrupt + * + * @param handle I2S channel handle + * @return + * - ESP_OK Initialize interrupt success + * - ESP_ERR_INVALID_ARG Wrong port id or NULL pointer + */ +esp_err_t i2s_init_i2s_intr(i2s_chan_handle_t handle); +#endif + /** * @brief Free I2S DMA descriptor and DMA buffer * diff --git a/components/esp_driver_i2s/i2s_std.c b/components/esp_driver_i2s/i2s_std.c index db434ff9b59..76e8ef28048 100644 --- a/components/esp_driver_i2s/i2s_std.c +++ b/components/esp_driver_i2s/i2s_std.c @@ -345,6 +345,11 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf if (I2S_CHANNEL_USES_DMA(handle)) { ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed"); } +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + if (handle->dir == I2S_DIR_TX) { + ESP_GOTO_ON_ERROR(i2s_init_i2s_intr(handle), err, TAG, "initialize I2S interrupt failed"); + } +#endif #if SOC_I2S_HW_VERSION_2 /* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */ if (handle->dir == I2S_DIR_TX) { diff --git a/components/esp_driver_i2s/i2s_tdm.c b/components/esp_driver_i2s/i2s_tdm.c index d8f4f209997..96d07070184 100644 --- a/components/esp_driver_i2s/i2s_tdm.c +++ b/components/esp_driver_i2s/i2s_tdm.c @@ -327,6 +327,11 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf if (I2S_CHANNEL_USES_DMA(handle)) { ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed"); } +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + if (handle->dir == I2S_DIR_TX) { + ESP_GOTO_ON_ERROR(i2s_init_i2s_intr(handle), err, TAG, "initialize I2S interrupt failed"); + } +#endif #if SOC_I2S_HW_VERSION_2 /* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */ diff --git a/components/esp_driver_i2s/include/driver/i2s_common.h b/components/esp_driver_i2s/include/driver/i2s_common.h index f78dacc7792..ef4ce0abc0f 100644 --- a/components/esp_driver_i2s/include/driver/i2s_common.h +++ b/components/esp_driver_i2s/include/driver/i2s_common.h @@ -53,6 +53,9 @@ typedef struct { i2s_isr_callback_t on_send_q_ovf; /**< Callback of sending queue overflowed event, only for TX channel * The event data includes buffer size that has been overwritten */ +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + i2s_tx_fifo_sync_callback_t on_tx_sync_evt; /**< Callback when the TX sync difference count exceeds the manual threshold */ +#endif } i2s_event_callbacks_t; /** @@ -244,7 +247,7 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si * - ESP_OK Set event callbacks successfully * - ESP_ERR_INVALID_ARG Set event callbacks failed because of invalid argument * - ESP_ERR_INVALID_STATE Set event callbacks failed because the current channel state is not REGISTERED or READY - * - ESP_ERR_NOT_SUPPORTED Set event callbacks failed because the channel does not use the DMA data path + * - ESP_ERR_NOT_SUPPORTED Set event callbacks failed because the requested event is not supported by this channel */ esp_err_t i2s_channel_register_event_callback(i2s_chan_handle_t handle, const i2s_event_callbacks_t *callbacks, void *user_data); @@ -291,46 +294,41 @@ esp_err_t i2s_channel_preload_data(i2s_chan_handle_t tx_handle, const void *src, 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); #if SOC_I2S_SUPPORTS_TX_SYNC_CNT +/** + * @brief TX synchronization counter values + */ +typedef struct { + uint32_t bclk_count; /*!< BCLK sync counter */ + uint32_t fifo_count; /*!< FIFO sync counter */ +#if SOC_I2S_SUPPORTS_TX_FIFO_SYNC + int32_t diff_count; /*!< Signed difference: I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT */ +#endif +} i2s_sync_count_t; + /** * @brief Get TX synchronization counters * * @note `fifo_count` reflects how many data have been read from TX FIFO. * Normally, `bclk_count = fifo_count * slot_bit_width`. - * Both counters are reset automatically when `I2S_ETM_TASK_SYNC_FIFO` is triggered. + * BCLK/FIFO counters are reset automatically when `I2S_ETM_TASK_SYNC_FIFO` is triggered. + * @note When `SOC_I2S_SUPPORTS_TX_FIFO_SYNC` is supported, `diff_count` is a signed 31-bit value + * equal to `I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT`. * * @param[in] tx_handle I2S TX channel handle - * @param[out] bclk_count Pointer to receive BCLK sync counter, set NULL to ignore - * @param[out] fifo_count Pointer to receive FIFO sync counter, set NULL to ignore - * @param[in] reset Whether to reset both counters after reading + * @param[out] count Pointer to receive TX synchronization counters + * @param[in] reset Whether to reset the counters after reading * @return * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Invalid handle or channel is not TX + * - ESP_ERR_INVALID_ARG Invalid handle, channel is not TX, or NULL count pointer */ -esp_err_t i2s_channel_get_sync_count(i2s_chan_handle_t tx_handle, uint32_t *bclk_count, uint32_t *fifo_count, - bool reset); +esp_err_t i2s_channel_get_sync_count(i2s_chan_handle_t tx_handle, i2s_sync_count_t *count, bool reset); #if SOC_I2S_SUPPORTS_TX_FIFO_SYNC -/** - * @brief Get TX FIFO synchronization difference counter - * - * @note `diff_count` is a signed 31-bit value equal to `I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT`. - * - * @param[in] tx_handle I2S TX channel handle - * @param[out] diff_count Pointer to receive signed difference counter, set NULL to ignore - * @param[in] reset Whether to reset the difference counter after reading - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Invalid handle or channel is not TX - */ -esp_err_t i2s_channel_get_sync_diff_count(i2s_chan_handle_t tx_handle, int32_t *diff_count, bool reset); - /** * @brief TX FIFO synchronization configuration */ typedef struct { - uint32_t auto_suppl_thresh; /*!< Threshold to enable automatic FIFO data supplement; - * 0 disables automatic supplement - */ + uint32_t auto_suppl_thresh; /*!< Threshold for automatic FIFO data supplement */ uint32_t manual_suppl_thresh; /*!< Threshold to trigger the callback for manual FIFO data supplement */ uint32_t ideal_cnt; /*!< Ideal FIFO count when ETM sync task is triggered */ i2s_tx_fifo_sync_suppl_mode_t suppl_mode; /*!< Data supplement mode for automatic supplement */ @@ -339,23 +337,11 @@ typedef struct { */ } i2s_tx_fifo_sync_config_t; -/** - * @brief Group of I2S peripheral interrupt event callbacks - * - * @note The callbacks are all running under ISR environment. - */ -typedef struct { - i2s_sync_callback_t on_tx_sync; /*!< Callback when the sync difference count exceeds - * `manual_suppl_thresh` - */ -} i2s_intr_event_callbacks_t; - /** * @brief Configure TX FIFO synchronization * - * @note Set `auto_suppl_thresh` to 0 to disable automatic hardware supplementation. - * @note When automatic hardware supplementation is enabled, `auto_suppl_thresh` must be smaller than - * `manual_suppl_thresh`. + * @note `auto_suppl_thresh` must be smaller than `manual_suppl_thresh`. + * @note Use i2s_channel_enable_tx_fifo_sync() to activate/deactivate after configuration. * @note Only allowed when channel state is REGISTERED or READY (before channel starts). * * @param[in] tx_handle I2S TX channel handle @@ -368,24 +354,21 @@ typedef struct { esp_err_t i2s_channel_config_tx_fifo_sync(i2s_chan_handle_t tx_handle, const i2s_tx_fifo_sync_config_t *config); /** - * @brief Register I2S peripheral interrupt event callbacks + * @brief Enable or disable TX FIFO synchronization * - * @note `on_tx_sync` is invoked when `tx_cnt_diff` exceeds `manual_suppl_thresh`. - * @note Only allowed when channel state is REGISTERED or READY (before channel starts). - * @note When CONFIG_I2S_ISR_IRAM_SAFE is enabled, the callback and user_data must reside in internal RAM. - * @note Set `callbacks->on_tx_sync` to NULL to deregister the callback and uninstall the TX sync interrupt. + * @note When enabled, both automatic hardware data supplementation and manual interrupt + * are activated simultaneously. When disabled, both are deactivated. + * @note Must be called after i2s_channel_config_tx_fifo_sync(). * - * @param[in] tx_handle I2S TX channel handle - * @param[in] callbacks Group of I2S peripheral interrupt callbacks - * @param[in] user_data User context passed to callback + * @param[in] tx_handle I2S TX channel handle + * @param[in] enable true to enable, false to disable * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Invalid handle or channel is not TX - * - ESP_ERR_INVALID_STATE Channel is already running - * - ESP_ERR_NO_MEM Failed to allocate interrupt + * - ESP_ERR_INVALID_STATE FIFO sync not configured */ -esp_err_t i2s_channel_register_intr_event_callback(i2s_chan_handle_t tx_handle, - const i2s_intr_event_callbacks_t *callbacks, void *user_data); +esp_err_t i2s_channel_enable_tx_fifo_sync(i2s_chan_handle_t tx_handle, bool enable); + #endif // SOC_I2S_SUPPORTS_TX_FIFO_SYNC #endif // SOC_I2S_SUPPORTS_TX_SYNC_CNT diff --git a/components/esp_driver_i2s/include/driver/i2s_types.h b/components/esp_driver_i2s/include/driver/i2s_types.h index 663a160d727..31893a1e51a 100644 --- a/components/esp_driver_i2s/include/driver/i2s_types.h +++ b/components/esp_driver_i2s/include/driver/i2s_types.h @@ -122,11 +122,11 @@ typedef struct { * * @param[in] handle I2S TX channel handle * @param[in] event TX synchronization event data - * @param[in] user_ctx User context registered via `i2s_channel_register_intr_event_callback()` + * @param[in] user_ctx User context registered via `i2s_channel_register_event_callback()` * * @return Whether a high priority task has been waken up by this callback function */ -typedef bool (*i2s_sync_callback_t)(i2s_chan_handle_t handle, const i2s_sync_event_data_t *event, void *user_ctx); +typedef bool (*i2s_tx_fifo_sync_callback_t)(i2s_chan_handle_t handle, const i2s_sync_event_data_t *event, void *user_ctx); #endif // SOC_I2S_SUPPORTS_TX_FIFO_SYNC /** diff --git a/components/esp_driver_i2s/linker.lf b/components/esp_driver_i2s/linker.lf index f241c777540..1db8b498859 100644 --- a/components/esp_driver_i2s/linker.lf +++ b/components/esp_driver_i2s/linker.lf @@ -8,4 +8,4 @@ entries: if SOC_I2S_SUPPORTS_TX_SYNC_CNT = y: i2s_common: i2s_channel_get_sync_count (noflash) if SOC_I2S_SUPPORTS_TX_FIFO_SYNC = y: - i2s_common: i2s_channel_get_sync_diff_count (noflash) + i2s_common: i2s_channel_enable_tx_fifo_sync (noflash) diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c index 356aeace682..900500bf464 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s.c @@ -1348,21 +1348,18 @@ TEST_CASE("I2S TX sync callback is triggered by GPTimer ETM alarm", "[i2s][etm]" .ideal_cnt = I2S_TX_SYNC_TEST_IDEAL_CNT, .suppl_mode = I2S_TX_FIFO_SYNC_SUPPL_MODE_LAST_DATA, }; - i2s_intr_event_callbacks_t intr_cbs = { - .on_tx_sync = i2s_tx_sync_test_callback, + i2s_event_callbacks_t cbs = { + .on_tx_sync_evt = i2s_tx_sync_test_callback, }; TEST_ESP_OK(i2s_channel_config_tx_fifo_sync(tx_handle, &sync_cfg)); - TEST_ESP_OK(i2s_channel_register_intr_event_callback(tx_handle, &intr_cbs, &cb_ctx)); + TEST_ESP_OK(i2s_channel_register_event_callback(tx_handle, &cbs, &cb_ctx)); + TEST_ESP_OK(i2s_channel_enable_tx_fifo_sync(tx_handle, true)); - uint32_t bclk_count = 0; - uint32_t fifo_count = 0; - int32_t diff_count = 0; - TEST_ESP_OK(i2s_channel_get_sync_count(tx_handle, &bclk_count, &fifo_count, true)); - TEST_ESP_OK(i2s_channel_get_sync_count(tx_handle, NULL, NULL, false)); - TEST_ESP_OK(i2s_channel_get_sync_diff_count(tx_handle, &diff_count, true)); - TEST_ESP_OK(i2s_channel_get_sync_diff_count(tx_handle, NULL, false)); + i2s_sync_count_t sync_count = {}; + TEST_ESP_OK(i2s_channel_get_sync_count(tx_handle, &sync_count, true)); + TEST_ESP_OK(i2s_channel_get_sync_count(tx_handle, &sync_count, false)); printf("TX sync API before start: diff=%"PRId32", fifo=%"PRIu32", bclk=%"PRIu32"\n", - diff_count, fifo_count, bclk_count); + sync_count.diff_count, sync_count.fifo_count, sync_count.bclk_count); i2s_etm_task_config_t i2s_task_cfg = { .task_type = I2S_ETM_TASK_SYNC_FIFO, @@ -1399,8 +1396,9 @@ TEST_CASE("I2S TX sync callback is triggered by GPTimer ETM alarm", "[i2s][etm]" TEST_ESP_OK(gptimer_stop(timer)); TEST_ESP_OK(i2s_channel_disable(tx_handle)); - intr_cbs.on_tx_sync = NULL; - TEST_ESP_OK(i2s_channel_register_intr_event_callback(tx_handle, &intr_cbs, NULL)); + TEST_ESP_OK(i2s_channel_enable_tx_fifo_sync(tx_handle, false)); + cbs.on_tx_sync_evt = NULL; + TEST_ESP_OK(i2s_channel_register_event_callback(tx_handle, &cbs, NULL)); TEST_ESP_OK(gptimer_disable(timer)); TEST_ESP_OK(esp_etm_channel_disable(etm_channel)); diff --git a/docs/en/api-reference/peripherals/i2s.rst b/docs/en/api-reference/peripherals/i2s.rst index 8e5733c4752..4c4669643fd 100644 --- a/docs/en/api-reference/peripherals/i2s.rst +++ b/docs/en/api-reference/peripherals/i2s.rst @@ -315,7 +315,9 @@ To satisfy the high quality audio requirement, following advanced APIs are provi .. only:: SOC_I2S_SUPPORTS_TX_SYNC_CNT - - :cpp:func:`i2s_channel_get_sync_count`: Read the TX BCLK/FIFO synchronization counters. This API can also actively clear them through the ``reset`` argument. + - :cpp:func:`i2s_channel_get_sync_count`: Read the TX synchronization counters through + :cpp:type:`i2s_sync_count_t`. When TX FIFO synchronization is supported, ``diff_count`` is also returned. + This API can also actively clear the counters through the ``reset`` argument. .. only:: SOC_I2S_SUPPORTS_TX_FIFO_SYNC @@ -326,12 +328,15 @@ To satisfy the high quality audio requirement, following advanced APIs are provi TX FIFO synchronization related APIs include: - - :cpp:func:`i2s_channel_get_sync_diff_count`: Read the TX FIFO synchronization difference counter. The value is signed and means ``I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT``. + - :cpp:func:`i2s_channel_get_sync_count`: Read the TX synchronization counters through :cpp:type:`i2s_sync_count_t`. + When TX FIFO synchronization is supported, ``diff_count`` is also returned as ``I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT``. - :cpp:func:`i2s_channel_config_tx_fifo_sync`: Configure the expected count, automatic supplement threshold, - manual supplement threshold, and hardware supplement mode. After automatic hardware supplementation is - enabled, hardware automatically supplements or deletes data according to ``diff_count`` so that the actual - count approaches ``ideal_cnt``. - - :cpp:func:`i2s_channel_register_intr_event_callback`: Register the manual supplement threshold interrupt callback. When + manual supplement threshold, and hardware supplement mode. + - :cpp:func:`i2s_channel_enable_tx_fifo_sync`: Enable or disable TX FIFO synchronization. When enabled, + both automatic hardware data supplementation and manual interrupt are activated simultaneously. + When disabled, both are deactivated. This API must be called after + :cpp:func:`i2s_channel_config_tx_fifo_sync`. + - :cpp:func:`i2s_channel_register_event_callback`: Register the manual supplement threshold interrupt callback. When ``diff_count`` exceeds the manual supplement threshold, the driver calls this callback in the ISR and provides ``diff_count`` through :cpp:type:`i2s_sync_event_data_t`. @@ -340,18 +345,15 @@ To satisfy the high quality audio requirement, following advanced APIs are provi 1. Create and initialize an I2S TX channel. 2. Call :cpp:func:`i2s_channel_config_tx_fifo_sync` to configure :cpp:type:`i2s_tx_fifo_sync_config_t`. ``ideal_cnt`` is the expected number of transmitted data units at each ETM synchronization check. ``auto_suppl_thresh`` is - the automatic hardware supplement threshold: set it to ``0`` to disable automatic hardware supplementation, or - set it to a value greater than ``0`` and smaller than ``manual_suppl_thresh`` to enable automatic hardware - supplementation. ``manual_suppl_thresh`` is the threshold for triggering the callback for manual handling. Once - enabled, if the difference exceeds the automatic supplement threshold but has not reached the manual supplement - threshold, hardware automatically supplements or deletes the corresponding amount of data to synchronize with - ``ideal_cnt``. - 3. To handle severe out-of-sync conditions, call :cpp:func:`i2s_channel_register_intr_event_callback` to register a callback. - After the callback is registered, the driver enables the TX synchronization interrupt. If the difference exceeds - the manual supplement threshold, the driver calls this callback in the ISR and provides ``diff_count`` through - :cpp:type:`i2s_sync_event_data_t`. - 4. Call :cpp:func:`i2s_new_etm_task` to create the ``I2S_ETM_TASK_SYNC_FIFO`` task, and connect an external ETM event to this task. - 5. Enable the ETM channel and I2S TX channel, so that ETM events periodically trigger synchronization checks. + the automatic hardware supplement threshold and must be smaller than ``manual_suppl_thresh``. + ``manual_suppl_thresh`` is the threshold for triggering the callback for manual handling. If the difference + exceeds the automatic supplement threshold but has not reached the manual supplement threshold, hardware + automatically supplements or deletes the corresponding amount of data to synchronize with ``ideal_cnt``. + 3. To handle severe out-of-sync conditions, call :cpp:func:`i2s_channel_register_event_callback` to register a callback. + 4. Call :cpp:func:`i2s_channel_enable_tx_fifo_sync` with ``enable`` set to ``true`` to activate both automatic + hardware supplementation and manual interrupt simultaneously. + 5. Call :cpp:func:`i2s_new_etm_task` to create the ``I2S_ETM_TASK_SYNC_FIFO`` task, and connect an external ETM event to this task. + 6. Enable the ETM channel and I2S TX channel, so that ETM events periodically trigger synchronization checks. The following example shows how to use a GPTimer alarm event to trigger the I2S TX FIFO synchronization check, and get ``diff_count`` in the manual supplement threshold interrupt: @@ -372,7 +374,7 @@ To satisfy the high quality audio requirement, following advanced APIs are provi const i2s_sync_event_data_t *event, void *user_ctx) { - // event->diff_count = I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT + // Applications can use event->diff_count to adjust the data source, choose a compensation policy, or report it to upper layers. return false; } @@ -382,11 +384,12 @@ To satisfy the high quality audio requirement, following advanced APIs are provi .auto_suppl_thresh = 32, .suppl_mode = I2S_TX_FIFO_SYNC_SUPPL_MODE_LAST_DATA, }; - i2s_intr_event_callbacks_t intr_cbs = { - .on_tx_sync = i2s_tx_sync_callback, + i2s_event_callbacks_t cbs = { + .on_tx_sync_evt = i2s_tx_sync_callback, }; i2s_channel_config_tx_fifo_sync(tx_handle, &sync_cfg); - i2s_channel_register_intr_event_callback(tx_handle, &intr_cbs, NULL); + i2s_channel_register_event_callback(tx_handle, &cbs, NULL); + i2s_channel_enable_tx_fifo_sync(tx_handle, true); i2s_etm_task_config_t i2s_task_cfg = { .task_type = I2S_ETM_TASK_SYNC_FIFO, diff --git a/docs/zh_CN/api-reference/peripherals/i2s.rst b/docs/zh_CN/api-reference/peripherals/i2s.rst index b479acdb345..06534cbc665 100644 --- a/docs/zh_CN/api-reference/peripherals/i2s.rst +++ b/docs/zh_CN/api-reference/peripherals/i2s.rst @@ -315,7 +315,9 @@ I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输 .. only:: SOC_I2S_SUPPORTS_TX_SYNC_CNT - - :cpp:func:`i2s_channel_get_sync_count`:用于读取 TX BCLK/FIFO 同步计数器。该 API 也可通过 ``reset`` 参数主动清零计数器。 + - :cpp:func:`i2s_channel_get_sync_count`:通过 :cpp:type:`i2s_sync_count_t` 读取 + TX 同步计数器。当支持 TX FIFO 同步时,也会返回 ``diff_count``。 + 该 API 也可通过 ``reset`` 参数主动清零计数器。 .. only:: SOC_I2S_SUPPORTS_TX_FIFO_SYNC @@ -326,25 +328,32 @@ I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输 TX FIFO 同步相关 API 包括: - - :cpp:func:`i2s_channel_get_sync_diff_count`:读取 TX FIFO 同步差值计数器。该值为有符号数,含义为 ``I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT``。 - - :cpp:func:`i2s_channel_config_tx_fifo_sync`:配置期望计数、自动补偿阈值、手动补偿阈值以及硬件补偿方式。 - 启用硬件自动补偿后,硬件会根据 ``diff_count`` 自动补充或删除数据,使实际计数靠近 ``ideal_cnt``。 - - :cpp:func:`i2s_channel_register_intr_event_callback`:注册手动补偿阈值中断回调。当 ``diff_count`` 超过手动补偿阈值时, - 驱动会在 ISR 中调用该回调,并通过 :cpp:type:`i2s_sync_event_data_t` 提供 ``diff_count``。 + - :cpp:func:`i2s_channel_get_sync_count`:通过 :cpp:type:`i2s_sync_count_t` 读取 TX 同步计数器。 + 当支持 TX FIFO 同步时,也会返回 ``diff_count``,其含义为 ``I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT``。 + - :cpp:func:`i2s_channel_config_tx_fifo_sync`:配置期望计数、自动补偿阈值、 + 手动补偿阈值以及硬件补偿方式。 + - :cpp:func:`i2s_channel_enable_tx_fifo_sync`:使能或关闭 TX FIFO 同步功能。使能后, + 硬件自动补偿和手动补偿中断同时激活。 + 关闭后,两者同时停用。该 API 必须在 + :cpp:func:`i2s_channel_config_tx_fifo_sync` 之后调用。 + - :cpp:func:`i2s_channel_register_event_callback`:注册手动补偿阈值中断回调。当 + ``diff_count`` 超过手动补偿阈值时,驱动会在 ISR 中调用该回调,并通过 + :cpp:type:`i2s_sync_event_data_t` 提供 ``diff_count``。 使用该功能的一般步骤如下: 1. 创建并初始化 I2S TX 通道。 - 2. 调用 :cpp:func:`i2s_channel_config_tx_fifo_sync` 配置 :cpp:type:`i2s_tx_fifo_sync_config_t`。其中 ``ideal_cnt`` - 为每次 ETM 同步检查时期望发送的数据个数;``auto_suppl_thresh`` 为硬件自动补偿阈值,设置为 ``0`` 表示 - 关闭硬件自动补偿,设置为大于 ``0`` 且小于 ``manual_suppl_thresh`` 表示开启硬件自动补偿; - ``manual_suppl_thresh`` 为触发回调并交由软件手动处理的阈值。开启后,如果偏差超过自动补偿阈值但尚未达到 - 手动补偿阈值,硬件会自动补充或删除相应数量的数据,以实现与 ``ideal_cnt`` 同步。 - 3. 如需处理严重不同步场景,调用 :cpp:func:`i2s_channel_register_intr_event_callback` 注册回调。注册回调后,驱动会使能 - TX 同步中断;如果偏差超过手动补偿阈值,驱动会在 ISR 中调用该回调,并通过 - :cpp:type:`i2s_sync_event_data_t` 提供 ``diff_count``。 - 4. 调用 :cpp:func:`i2s_new_etm_task` 创建 ``I2S_ETM_TASK_SYNC_FIFO`` 任务,并将外部 ETM 事件连接到该任务。 - 5. 使能 ETM 通道和 I2S TX 通道,由 ETM 事件周期性触发同步检查。 + 2. 调用 :cpp:func:`i2s_channel_config_tx_fifo_sync` 配置 :cpp:type:`i2s_tx_fifo_sync_config_t`。``ideal_cnt`` + 为每次 ETM 同步检查时期望发送的数据个数;``auto_suppl_thresh`` 为 + 硬件自动补偿阈值,必须小于 ``manual_suppl_thresh``。 + ``manual_suppl_thresh`` 为触发回调并交由软件手动处理的阈值。如果偏差 + 超过自动补偿阈值但尚未达到手动补偿阈值,硬件会自动补充或删除相应数量的数据, + 以实现与 ``ideal_cnt`` 同步。 + 3. 如需处理严重不同步场景,调用 :cpp:func:`i2s_channel_register_event_callback` 注册回调。 + 4. 调用 :cpp:func:`i2s_channel_enable_tx_fifo_sync` 并将 ``enable`` 设为 ``true``,同时激活硬件自动补偿 + 和手动补偿中断。 + 5. 调用 :cpp:func:`i2s_new_etm_task` 创建 ``I2S_ETM_TASK_SYNC_FIFO`` 任务,并将外部 ETM 事件连接到该任务。 + 6. 使能 ETM 通道和 I2S TX 通道,由 ETM 事件周期性触发同步检查。 以下示例展示了如何使用 GPTimer alarm event 触发 I2S TX FIFO 同步检查,并在手动补偿阈值中断中获取 ``diff_count``: @@ -365,7 +374,7 @@ I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输 const i2s_sync_event_data_t *event, void *user_ctx) { - // event->diff_count = I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT + // 应用可根据 event->diff_count 决定是否调整数据源、补偿策略或上报给上层处理 return false; } @@ -375,11 +384,12 @@ I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输 .auto_suppl_thresh = 32, .suppl_mode = I2S_TX_FIFO_SYNC_SUPPL_MODE_LAST_DATA, }; - i2s_intr_event_callbacks_t intr_cbs = { - .on_tx_sync = i2s_tx_sync_callback, + i2s_event_callbacks_t cbs = { + .on_tx_sync_evt = i2s_tx_sync_callback, }; i2s_channel_config_tx_fifo_sync(tx_handle, &sync_cfg); - i2s_channel_register_intr_event_callback(tx_handle, &intr_cbs, NULL); + i2s_channel_register_event_callback(tx_handle, &cbs, NULL); + i2s_channel_enable_tx_fifo_sync(tx_handle, true); i2s_etm_task_config_t i2s_task_cfg = { .task_type = I2S_ETM_TASK_SYNC_FIFO,