feat(i2s): support new sync feature on H4

This commit is contained in:
laokaiyao
2025-07-15 15:44:07 +08:00
parent 0a110478ea
commit 424991faaf
7 changed files with 255 additions and 4 deletions

View File

@@ -8,6 +8,13 @@ menu "ESP-Driver:I2S Configurations"
Ensure the I2S interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).
config I2S_CTRL_FUNC_IN_IRAM
bool "Place I2S control functions into IRAM"
default n
help
Place I2S control functions into IRAM,
so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
config I2S_ENABLE_DEBUG_LOG
bool "Enable I2S debug log"
default n

View File

@@ -47,6 +47,7 @@
#include "driver/gpio.h"
#include "esp_private/gpio.h"
#include "esp_private/i2s_sync.h"
#include "driver/i2s_common.h"
#include "i2s_private.h"
@@ -1483,3 +1484,46 @@ void i2s_sync_reset_fifo_count(i2s_chan_handle_t tx_handle)
i2s_ll_tx_reset_fifo_sync_counter(tx_handle->controller->hal.dev);
}
#endif // SOC_I2S_SUPPORTS_TX_SYNC_CNT
#if SOC_I2S_SUPPORTS_ETM_SYNC
uint32_t i2s_sync_get_fifo_sync_diff_count(i2s_chan_handle_t tx_handle)
{
return i2s_ll_tx_get_fifo_sync_diff_count(tx_handle->controller->hal.dev);
}
void i2s_sync_reset_fifo_sync_diff_count(i2s_chan_handle_t tx_handle)
{
i2s_ll_tx_reset_fifo_sync_diff_counter(tx_handle->controller->hal.dev);
}
esp_err_t i2s_sync_enable_hw_fifo_sync(i2s_chan_handle_t tx_handle, bool enable)
{
if (tx_handle->dir == I2S_DIR_RX) {
return ESP_ERR_NOT_SUPPORTED;
}
i2s_ll_tx_enable_hw_fifo_sync(tx_handle->controller->hal.dev, enable);
return ESP_OK;
}
esp_err_t i2s_sync_config_hw_fifo_sync(i2s_chan_handle_t tx_handle, const i2s_sync_fifo_sync_config_t *config)
{
if (!(tx_handle && config)) {
return ESP_ERR_INVALID_ARG;
}
if (tx_handle->dir == I2S_DIR_RX) {
return ESP_ERR_NOT_SUPPORTED;
}
if (config->sw_high_thresh < config->hw_low_thresh) {
return ESP_ERR_INVALID_ARG;
}
i2s_ll_tx_set_etm_sync_ideal_cnt(tx_handle->controller->hal.dev, config->ideal_cnt);
i2s_ll_tx_set_fifo_sync_diff_conter_sw_threshold(tx_handle->controller->hal.dev, config->sw_high_thresh);
i2s_ll_tx_set_fifo_sync_diff_conter_hw_threshold(tx_handle->controller->hal.dev, config->hw_low_thresh);
i2s_ll_tx_set_hw_fifo_sync_suppl_mode(tx_handle->controller->hal.dev, (uint32_t)config->suppl_mode);
if (config->suppl_mode == I2S_SYNC_SUPPL_MODE_STATIC_DATA) {
i2s_ll_tx_set_hw_fifo_sync_static_suppl_data(tx_handle->controller->hal.dev, config->suppl_data);
}
return ESP_OK;
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -84,6 +84,11 @@ esp_err_t i2s_new_etm_task(i2s_chan_handle_t handle, const i2s_etm_task_config_t
{
ESP_RETURN_ON_FALSE(handle && config && out_task, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(config->task_type < I2S_ETM_TASK_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid task type");
#if SOC_I2S_SUPPORTS_ETM_SYNC
ESP_RETURN_ON_FALSE(config->task_type != I2S_ETM_TASK_SYNC_CHECK || handle->dir == I2S_DIR_TX,
ESP_ERR_NOT_SUPPORTED, TAG, "rx is not supported");
#endif
i2s_etm_task_t *task = heap_caps_calloc(1, sizeof(i2s_etm_task_t), ETM_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(task, ESP_ERR_NO_MEM, TAG, "no memory for ETM task");

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -31,6 +31,7 @@ extern "C" {
/**
* @brief Get the counter number of BCLK ticks
* @note The BCLK tick count reflects the real data that have sent on line
* @note It will be reset automatically when `I2S_ETM_TASK_SYNC_CHECK` is triggered
*
* @param[in] tx_handle The I2S tx channel handle
* @return
@@ -43,6 +44,7 @@ uint32_t i2s_sync_get_bclk_count(i2s_chan_handle_t tx_handle);
* @note The FIFO count reflects how many slots have processed
* Normally, fifo_cnt = slot_bit_width * bclk_cnt
* If fifo_cnt < slot_bit_width * bclk_cnt, that means some data are still stuck in the I2S controller
* @note It will be reset automatically when `I2S_ETM_TASK_SYNC_CHECK` is triggered
*
* @param[in] tx_handle The I2S tx channel handle
* @return
@@ -66,6 +68,78 @@ void i2s_sync_reset_fifo_count(i2s_chan_handle_t tx_handle);
#endif // SOC_I2S_SUPPORTS_TX_SYNC_CNT
#if SOC_I2S_SUPPORTS_ETM_SYNC
/**
* @brief I2S hardware FIFO synchronization supplement mode
* @note When the FIFO sync difference count is out of threshold, the hardware will supplement data automatically
* This type is to specify which data will be supplemented
*/
typedef enum {
I2S_SYNC_SUPPL_MODE_LAST_DATA = 0, /*!< Supplement with the last transmitted data */
I2S_SYNC_SUPPL_MODE_STATIC_DATA = 1, /*!< Supplement with static data specified in config */
} i2s_sync_suppl_mode_t;
/**
* @brief I2S hardware FIFO synchronization configuration
* @note This configuration is used for multi I2S port synchronization via ETM
*/
typedef struct {
uint32_t hw_low_thresh; /*!< Lower threshold for FIFO sync difference counter
- If difference count < hw_low_thresh, do nothing
- If difference count >= hw_low_thresh, the hardware will supplement data automatically */
uint32_t sw_high_thresh; /*!< Upper threshold for FIFO sync difference counter
- If difference count <= sw_high_thresh, the hardware supplement data automatically
- If difference count > sw_high_thresh, sync interrupt triggered and
the software is responsible to decide how to handle this severe asynchronization */
uint32_t ideal_cnt; /*!< Ideal count for FIFO sync difference counter, it depends on the ETM sync task interval and the data rate */
i2s_sync_suppl_mode_t suppl_mode; /*!< Data supplement mode when FIFO sync difference is out of threshold */
uint32_t suppl_data; /*!< Static supplement data, only valid when suppl_mode is I2S_SYNC_SUPPL_MODE_STATIC_DATA */
} i2s_sync_fifo_sync_config_t;
/**
* @brief Get the counter number of FIFO sync difference
* @note The FIFO sync difference count reflects the difference between current FIFO count and ideal count
*
* @param[in] tx_handle The I2S tx channel handle
* @return
* - FIFO sync difference count
*/
uint32_t i2s_sync_get_fifo_sync_diff_count(i2s_chan_handle_t tx_handle);
/**
* @brief Reset the FIFO sync difference counter
*
* @param[in] tx_handle The I2S tx channel handle
*/
void i2s_sync_reset_fifo_sync_diff_count(i2s_chan_handle_t tx_handle);
/**
* @brief Enable or disable hardware FIFO synchronization
* @note When enabled, hardware will automatically supplement data when FIFO sync difference is greater than hw_low_thresh
*
* @param[in] tx_handle The I2S tx channel handle
* @param[in] enable true to enable, false to disable
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_SUPPORTED if called on RX channel
*/
esp_err_t i2s_sync_enable_hw_fifo_sync(i2s_chan_handle_t tx_handle, bool enable);
/**
* @brief Configure hardware FIFO synchronization parameters
* @note This function configures the thresholds and supplement mode for hardware FIFO sync
*
* @param[in] tx_handle The I2S tx channel handle
* @param[in] config Configuration for hardware FIFO synchronization
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if invalid arguments
* - ESP_ERR_NOT_SUPPORTED if called on RX channel
*/
esp_err_t i2s_sync_config_hw_fifo_sync(i2s_chan_handle_t tx_handle, const i2s_sync_fifo_sync_config_t *config);
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,14 @@
[mapping:i2s_driver]
archive: libesp_driver_i2s.a
entries:
if I2S_CTRL_FUNC_IN_IRAM = y:
if SOC_I2S_SUPPORTS_TX_SYNC_CNT = y:
i2s_common: i2s_sync_get_bclk_count (noflash)
i2s_common: i2s_sync_get_fifo_count (noflash)
i2s_common: i2s_sync_reset_bclk_count (noflash)
i2s_common: i2s_sync_reset_fifo_count (noflash)
if SOC_I2S_SUPPORTS_ETM_SYNC = y:
i2s_common: i2s_sync_get_fifo_sync_diff_count (noflash)
i2s_common: i2s_sync_reset_fifo_sync_diff_count (noflash)
i2s_common: i2s_sync_enable_hw_fifo_sync (noflash)
i2s_common: i2s_sync_config_hw_fifo_sync (noflash)

View File

@@ -60,7 +60,7 @@ extern "C" {
[I2S_DIR_RX - 1] = { \
[I2S_ETM_TASK_START] = I2S0_TASK_START_RX, \
[I2S_ETM_TASK_STOP] = I2S0_TASK_STOP_RX, \
[I2S_ETM_TASK_SYNC_CHECK] = I2S0_TASK_SYNC_CHECK, \
[I2S_ETM_TASK_SYNC_CHECK] = -1, \
}, \
[I2S_DIR_TX - 1] = { \
[I2S_ETM_TASK_START] = I2S0_TASK_START_TX, \
@@ -1245,6 +1245,113 @@ static inline uint32_t i2s_ll_tx_get_bclk_sync_count(i2s_dev_t *hw)
return hw->bck_cnt.tx_bck_cnt;
}
/**
* @brief Enable TX FIFO synchronization hardware mode
*
* @param hw Peripheral I2S hardware instance address.
* @param enable Set true to enable hardware mode
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_enable_hw_fifo_sync(i2s_dev_t *hw, bool enable)
{
hw->hw_sync_conf.tx_hw_sync_en = enable;
}
/**
* @brief Get TX FIFO synchronization difference count value
*
* @param hw Peripheral I2S hardware instance address.
* @return
* fifo count value
*/
__attribute__((always_inline))
static inline uint32_t i2s_ll_tx_get_fifo_sync_diff_count(i2s_dev_t *hw)
{
return hw->cnt_diff.tx_cnt_diff;
}
/**
* @brief Reset TX FIFO synchronization difference counter
*
* @param hw Peripheral I2S hardware instance address.
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_reset_fifo_sync_diff_counter(i2s_dev_t *hw)
{
hw->cnt_diff.tx_cnt_diff_rst = 1;
hw->cnt_diff.tx_cnt_diff_rst = 0;
}
/**
* @brief Set TX FIFO synchronization difference counter software threshold
* @note It determines the up threshold that the hardware synchronize the data automatically.
* - If diff_count <= sw_threshold, the hardware will synchronize the data automatically.
* - If diff_count > sw_threshold, the automatic synchronization is not proper for this case,
* interrupt will be triggered to let the software decide how to handle this case.
*
* @param hw Peripheral I2S hardware instance address.
* @param thresh The threshold that send
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_set_fifo_sync_diff_conter_sw_threshold(i2s_dev_t *hw, uint32_t thresh)
{
hw->sync_sw_thres.tx_cnt_diff_sw_thres = thresh;
}
/**
* @brief Set TX FIFO synchronization difference counter hardware threshold
* @note It determines the down threshold that the hardware synchronize the data automatically.
* - If diff_count < hw_threshold, synchronization check pass, do nothing
* - If diff_count >= hw_threshold, the hardware will synchronize the data automatically.
*
* @param hw Peripheral I2S hardware instance address.
* @param thresh The threshold that send
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_set_fifo_sync_diff_conter_hw_threshold(i2s_dev_t *hw, uint32_t thresh)
{
hw->sync_hw_thres.tx_cnt_diff_hw_thres = thresh;
}
/**
* @brief Set TX FIFO synchronization hardware data supplementation mode
* @note It determines the supplementation data when the actual sent data is less than the `diff_count - threshold`
*
* @param hw Peripheral I2S hardware instance address.
* @param mode Data supplementation mode
* - 0: Supplement the last data
* - 1: Supplement the data configured in `hw_sync_data` reg
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_set_hw_fifo_sync_suppl_mode(i2s_dev_t *hw, uint32_t mode)
{
hw->hw_sync_conf.tx_hw_sync_suppl_mode = mode;
}
/**
* @brief Set TX FIFO synchronization hardware supplementation data when `tx_hw_sync_suppl_mode` is 1
*
* @param hw Peripheral I2S hardware instance address.
* @param data Data to be supplemented when `tx_hw_sync_suppl_mode` is 1
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_set_hw_fifo_sync_static_suppl_data(i2s_dev_t *hw, uint32_t data)
{
hw->hw_sync_data.tx_hw_sync_suppl_data = data;
}
/**
* @brief Set the TX ETM synchronization ideal count
*
* @param hw Peripheral I2S hardware instance address.
* @param ideal_cnt The ideal FIFO count when I2S_ETM_TASK_SYNC_CHECK triggered.
*/
__attribute__((always_inline))
static inline void i2s_ll_tx_set_etm_sync_ideal_cnt(i2s_dev_t *hw, uint32_t ideal_cnt)
{
hw->ideal_cnt.tx_ideal_cnt = ideal_cnt;
}
/**
* @brief Set the TX ETM threshold of REACH_THRESH event
*

View File

@@ -234,7 +234,7 @@ typedef enum {
I2S_ETM_TASK_START, /*!< Start the I2S channel */
I2S_ETM_TASK_STOP, /*!< Stop the I2S channel */
#if SOC_I2S_SUPPORTS_ETM_SYNC
I2S_ETM_TASK_SYNC_CHECK, /*!< Stop the I2S channel */
I2S_ETM_TASK_SYNC_CHECK, /*!< Check the I2S TX channel sync status */
#endif
I2S_ETM_TASK_MAX, /*!< Maximum number of tasks */
} i2s_etm_task_type_t;