feat(i2s): release i2s tx sync APIs

This commit is contained in:
Chen Chen
2026-06-26 09:36:16 +08:00
parent c2c7ae0b3d
commit cf975c7a38
14 changed files with 746 additions and 246 deletions
@@ -313,6 +313,93 @@ 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`:用于读取 TX BCLK/FIFO 同步计数器。该 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_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``。
使用该功能的一般步骤如下:
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 事件周期性触发同步检查。
以下示例展示了如何使用 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 = 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::
``I2S_ETM_TASK_SYNC_FIFO`` 触发后,硬件会自动清零 TX FIFO/BCLK 同步计数器。
.. _i2s-iram-safe:
IRAM 安全