feat(i2s): release i2s tx sync APIs

This commit is contained in:
Chen Chen
2026-07-13 14:43:55 +08:00
parent f44f4a82e6
commit c27bd91874
14 changed files with 746 additions and 246 deletions
+94
View File
@@ -313,6 +313,100 @@ To satisfy the high quality audio requirement, following advanced APIs are provi
- :cpp:func:`i2s_channel_preload_data`: Preloading audio data into the I2S internal cache, enabling the TX channel to immediately send data upon activation, thereby reducing the initial audio output delay.
- :cpp:func:`i2s_channel_tune_rate`: Dynamically fine-tuning the audio rate at runtime to match the speed of the audio data producer and consumer, thereby preventing the accumulation or shortage of intermediate buffered data that caused by rate mismatches.
.. 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.
.. only:: SOC_I2S_SUPPORTS_TX_FIFO_SYNC
TX FIFO Synchronization
"""""""""""""""""""""""
{IDF_TARGET_NAME} supports I2S TX FIFO synchronization. It can periodically trigger ``I2S_ETM_TASK_SYNC_FIFO`` task through ETM to check the difference between the actual TX FIFO data count and the expected count. This feature is useful when multiple I2S TX ports or an external timing source need to stay synchronized.
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_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
``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`.
The typical usage steps are:
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 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:
.. code-block:: c
#include "driver/i2s_common.h"
#include "driver/i2s_etm.h"
#include "driver/gptimer.h"
#include "esp_etm.h"
/* Assume the I2S TX channel, GPTimer, and ETM channel have been created and initialized */
i2s_chan_handle_t tx_handle;
gptimer_handle_t timer;
esp_etm_channel_handle_t etm_channel;
static bool IRAM_ATTR i2s_tx_sync_callback(i2s_chan_handle_t handle,
const i2s_sync_event_data_t *event,
void *user_ctx)
{
// event->diff_count = I2S_TX_FIFO_CNT - I2S_TX_FIFO_IDEAL_CNT
return false;
}
i2s_tx_fifo_sync_config_t sync_cfg = {
.ideal_cnt = 1000,
.manual_suppl_thresh = 64,
.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_channel_config_tx_fifo_sync(tx_handle, &sync_cfg);
i2s_channel_register_intr_event_callback(tx_handle, &intr_cbs, NULL);
i2s_etm_task_config_t i2s_task_cfg = {
.task_type = I2S_ETM_TASK_SYNC_FIFO,
};
esp_etm_task_handle_t i2s_sync_task = NULL;
i2s_new_etm_task(tx_handle, &i2s_task_cfg, &i2s_sync_task);
gptimer_etm_event_config_t timer_event_cfg = {
.event_type = GPTIMER_ETM_EVENT_ALARM_MATCH,
};
esp_etm_event_handle_t timer_event = NULL;
gptimer_new_etm_event(timer, &timer_event_cfg, &timer_event);
esp_etm_channel_connect(etm_channel, timer_event, i2s_sync_task);
esp_etm_channel_enable(etm_channel);
.. note::
After ``I2S_ETM_TASK_SYNC_FIFO`` is triggered, hardware automatically clears the TX FIFO/BCLK synchronization counters.
.. _i2s-iram-safe:
IRAM Safe