mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feat/i2s_tx_sync_esp31s31_v6.1' into 'release/v6.1'
feat(i2s): support TX FIFO sync (v6.1) See merge request espressif/esp-idf!50338
This commit is contained in:
@@ -313,6 +313,109 @@ 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 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
|
||||
|
||||
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_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. It can be called while the TX channel is running,
|
||||
but TX FIFO synchronization must be disabled.
|
||||
- :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. Enabling TX FIFO synchronization resets the TX FIFO/BCLK synchronization
|
||||
counters. 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`. Registering the callback only updates the handler;
|
||||
the TX sync interrupt's enable/disable is controlled by :cpp:func:`i2s_channel_enable_tx_fifo_sync`.
|
||||
|
||||
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. This step can be performed while the TX channel is running, but TX FIFO synchronization must be disabled before reconfiguration. ``auto_suppl_thresh`` is
|
||||
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. This call resets the TX FIFO/BCLK synchronization
|
||||
counters, so the first ETM synchronization check uses a new count window.
|
||||
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:
|
||||
|
||||
.. 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)
|
||||
{
|
||||
// Applications can use event->diff_count to adjust the data source, choose a compensation policy, or report it to upper layers.
|
||||
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_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_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,
|
||||
};
|
||||
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.
|
||||
To avoid a synchronization check using partially updated configuration, call :cpp:func:`i2s_channel_enable_tx_fifo_sync`
|
||||
with ``enable`` set to ``false`` before reconfiguring TX FIFO synchronization. If an ETM event source may still
|
||||
trigger during reconfiguration, disable the ETM channel or pause the event source as needed.
|
||||
|
||||
.. _i2s-iram-safe:
|
||||
|
||||
IRAM Safe
|
||||
|
||||
@@ -313,6 +313,108 @@ I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输
|
||||
- :cpp:func:`i2s_channel_preload_data`: 用于预加载音频数据到 I2S 内部缓存,使得 TX 通道使能后能够立即发送数据,以此降低音频初始输出延迟。
|
||||
- :cpp:func:`i2s_channel_tune_rate`: 用于在运行时动态微调音频速率,以匹配音频数据生产者和消费者的速度,从而防止因速率不匹配导致的中间缓存数据累积或不足。
|
||||
|
||||
.. only:: SOC_I2S_SUPPORTS_TX_SYNC_CNT
|
||||
|
||||
- :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
|
||||
|
||||
TX FIFO 同步
|
||||
""""""""""""
|
||||
|
||||
{IDF_TARGET_NAME} 支持 I2S TX FIFO 同步功能,可用于通过 ETM 周期性触发 ``I2S_ETM_TASK_SYNC_FIFO`` 任务,检查 TX FIFO 实际发送的数据计数与期望计数之间的偏差。该功能适用于需要多个 I2S TX 端口或外部时序源保持同步的场景。
|
||||
|
||||
TX FIFO 同步相关 API 包括:
|
||||
|
||||
- :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`:配置期望计数、自动补偿阈值、
|
||||
手动补偿阈值以及硬件补偿方式。该 API 可在 TX 通道运行时调用,
|
||||
但此时 TX FIFO 同步功能必须处于关闭状态。
|
||||
- :cpp:func:`i2s_channel_enable_tx_fifo_sync`:使能或关闭 TX FIFO 同步功能。使能后,
|
||||
硬件自动补偿和手动补偿中断同时激活。
|
||||
关闭后,两者同时停用。使能 TX FIFO 同步时会重置 TX FIFO/BCLK 同步计数器。
|
||||
该 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``。注册回调只更新 handler;
|
||||
TX sync 中断的开关由 :cpp:func:`i2s_channel_enable_tx_fifo_sync` 控制。
|
||||
|
||||
使用该功能的一般步骤如下:
|
||||
|
||||
1. 创建并初始化 I2S TX 通道。
|
||||
2. 调用 :cpp:func:`i2s_channel_config_tx_fifo_sync` 配置 :cpp:type:`i2s_tx_fifo_sync_config_t`。``ideal_cnt``
|
||||
为每次 ETM 同步检查时期望发送的数据个数。该步骤可在 TX 通道运行时执行,但重新配置前必须先关闭 TX FIFO 同步功能。``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``,同时激活硬件自动补偿
|
||||
和手动补偿中断。该调用会重置 TX FIFO/BCLK 同步计数器,因此第一次 ETM 同步检查会使用新的计数窗口。
|
||||
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``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "driver/i2s_common.h"
|
||||
#include "driver/i2s_etm.h"
|
||||
#include "driver/gptimer.h"
|
||||
#include "esp_etm.h"
|
||||
|
||||
/* 假设已经创建并初始化 I2S TX 通道、GPTimer 和 ETM 通道 */
|
||||
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 决定是否调整数据源、补偿策略或上报给上层处理
|
||||
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_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_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,
|
||||
};
|
||||
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::
|
||||
|
||||
``I2S_ETM_TASK_SYNC_FIFO`` 触发后,硬件会自动清零 TX FIFO/BCLK 同步计数器。
|
||||
为避免同步检查使用到正在更新中的配置,重新配置 TX FIFO 同步前应调用
|
||||
:cpp:func:`i2s_channel_enable_tx_fifo_sync` 并将 ``enable`` 设为 ``false``。
|
||||
如果重新配置期间 ETM 事件源仍可能触发,可根据需要关闭 ETM 通道或暂停事件源。
|
||||
|
||||
.. _i2s-iram-safe:
|
||||
|
||||
IRAM 安全
|
||||
|
||||
Reference in New Issue
Block a user