API GUIDE / AIC / HAL

AIC HAL 接口

1. HAL 公开类型

1.1 Stream、模式和 format

hal_aic_stream_t、hal_aic_transfer_mode_t、hal_aic_wire_mode_t、 hal_aic_codec_source_t 和 hal_aic_clock_role_t 定义见上一节。

hal_aic_format_t:

值 说明
HAL_AIC_FORMAT_I2S 标准 I2S
HAL_AIC_FORMAT_MSB_JUSTIFIED MSB/left-justified

hal_aic_sample_width_t:

值 有效 bit
HAL_AIC_SAMPLE_WIDTH_8 8
HAL_AIC_SAMPLE_WIDTH_16 16
HAL_AIC_SAMPLE_WIDTH_18 18
HAL_AIC_SAMPLE_WIDTH_20 20
HAL_AIC_SAMPLE_WIDTH_24 24

1.2 hal_aic_connection_config_t

typedef struct {
    hal_aic_wire_mode_t wire_mode;
    hal_aic_codec_source_t playback_codec;
    hal_aic_codec_source_t capture_codec;
    hal_aic_clock_role_t playback_clock_role;
    hal_aic_clock_role_t capture_clock_role;
} hal_aic_connection_config_t;

该配置是 reset-persistent routing policy。修改前 AIC 必须没有 active stream。

1.3 hal_aic_playback_config_t

typedef struct {
    hal_aic_sample_width_t sample_width;
    hal_aic_channel_order_t channel_order;
    hal_aic_underrun_output_t underrun_output;
    uint8_t channels;
    bool packed_16bit;
    bool packed_lr_swap;
    bool mono_to_stereo;
    bool endian_swap;
} hal_aic_playback_config_t;

约束:

  1. channels 只能是 1 或 2。
  2. packed_16bit 和 endian_swap 只允许 sample_width == 16。
  3. packed_lr_swap 只允许 16-bit、packed 和 stereo。
  4. RIGHT_FIRST 只允许 stereo。
  5. Mono-to-stereo 是硬件复制,只应在 stereo 模式下使用。

1.4 hal_aic_capture_config_t

typedef struct {
    hal_aic_sample_width_t sample_width;
    hal_aic_capture_channel_t channel;
    hal_aic_endian_t endian;
    bool echo_cancellation;
} hal_aic_capture_config_t;

echo_cancellation 只允许与 HAL_AIC_CAPTURE_LEFT 组合。

1.5 State、event 和 FIFO status

hal_aic_state_t:

状态 说明
RESET handle 未完成 hal_aic_init() 或时钟已释放
READY 无 active stream,可以配置和 start
ACTIVE 至少一个 HAL stream active
ERROR reset 或严重硬件错误,需要重新 init 或 reset 恢复

hal_aic_event_t:

值 适用方向
HAL_AIC_EVENT_FIFO_SERVICE playback/capture/loop
HAL_AIC_EVENT_UNDERRUN playback
HAL_AIC_EVENT_OVERRUN capture/loop

hal_aic_fifo_status_t 返回当前 level 和以上 event mask。查询不会清除 error flag。

1.6 hal_aic_handle_t

handle 包含:

字段 说明
instance 必须是 AIC
state HAL 生命周期
active_stream_mask active stream 位图
error_code latched HAL_AIC_ERROR_*
clock_enabled AIC module gate 状态
tx_i2s_clock_enabled / rx_i2s_clock_enabled CGU 状态
shared_i2s_clock 4-wire 共享时钟
user_data caller context
playback/capture/loop callback IRQ callback

2. HAL 生命周期接口

2.1 hal_aic_handle_init

error_code_t hal_aic_handle_init(hal_aic_handle_t *haic,
                                 aic_regs_t *instance);

只初始化 caller-owned handle,不访问硬件。成功 state 为 RESET。

2.2 hal_aic_init

error_code_t hal_aic_init(hal_aic_handle_t *haic);

获取 AIC module gate,关闭 runtime controls,清 FIFO error,进入 READY。

注意:

  • 不执行 AIC reset。
  • 不配置 pinmux。
  • 不配置 I2S CGU,CGU 由 hal_aic_clock_enable() 管理。
  • 失败时返回 CPM 错误。

2.3 hal_aic_configure_connection

error_code_t hal_aic_configure_connection(
    hal_aic_handle_t *haic,
    const hal_aic_connection_config_t *config);

在非 RESET 且无 active stream 时配置 wire mode、codec source 和 clock roles。

返回:

返回值 条件
IM_OK 配置已写入
IM_ERR_INVALID_PARAM 空参数或组合非法
IM_ERR_NOT_READY handle 尚未 init
IM_ERR_BUSY 仍有 stream active

2.4 hal_aic_clock_enable

error_code_t hal_aic_clock_enable(hal_aic_handle_t *haic);

在真实 CPM 下选择 HAL_CPM_ID_CLK_SCLK_A parent,并准备 TX/RX I2S CGU。初始 rate 为 48 kHz × 256。该接口是阻塞调用,只能在 task context 使用。

2.5 hal_aic_clock_set_sample_rate

error_code_t hal_aic_clock_set_sample_rate(
    hal_aic_handle_t *haic,
    hal_aic_stream_t stream,
    uint32_t sample_rate_hz);

stream 只允许 playback 或 capture。4-wire 会同步更新 TX/RX divider;6-wire 只更新 目标方向。sample rate 乘 256 后必须能用 32-bit Hz 表示。

2.6 hal_aic_clock_disable

error_code_t hal_aic_clock_disable(hal_aic_handle_t *haic);

释放 RX/TX CGU 和 AIC module gate。任何 stream active 时返回 IM_ERR_BUSY。

2.7 hal_aic_deinit

error_code_t hal_aic_deinit(hal_aic_handle_t *haic);

强制关闭 AIC runtime controls,清 FIFO error,释放 handle 持有的所有时钟。此接口不 drain、不 flush FIFO;上层应先 stop stream 和 DMA。成功回到 RESET。

2.8 内部 codec reset

error_code_t hal_aic_internal_codec_reset_assert(
    hal_aic_handle_t *haic);

error_code_t hal_aic_internal_codec_reset_deassert(
    hal_aic_handle_t *haic);

控制 AICFR.CODEC_SRST。允许在 hal_aic_init() 前调用,但 AIC clock 必须有效, 且不能有 active stream。脉冲宽度和 codec settle delay 由 codec/BSP 控制。

3. HAL 配置与数据接口

3.1 hal_aic_reset

error_code_t hal_aic_reset(hal_aic_handle_t *haic,
                           hal_aic_stream_t stream,
                           uint32_t timeout_us);

timeout_us == 0 只做一次即时完成检查。reset 期间需要 BIT_CLK。成功会清除 stream 配置,调用方之后必须重新配置 format、sample width 和 channel 参数。

3.2 hal_aic_configure_format

error_code_t hal_aic_configure_format(hal_aic_handle_t *haic,
                                      hal_aic_format_t format);

配置 I2S 或 MSB-justified。当前实现同时写 I2SCR.FIRSTCHN_LEFT=1,这是实际运行所 需的控制位。stream active 时返回 IM_ERR_BUSY。

3.3 hal_aic_sample_signedness_configure

error_code_t hal_aic_sample_signedness_configure(
    hal_aic_handle_t *haic,
    hal_aic_sample_signedness_t signedness);

控制共享 MSB inversion:

  • SIGNED:关闭 ASVTSU。
  • UNSIGNED:打开 ASVTSU,每个样本 MSB 反转。

3.4 hal_aic_configure_playback

error_code_t hal_aic_configure_playback(
    hal_aic_handle_t *haic,
    const hal_aic_playback_config_t *config);

配置 OSS、channel count、PACK16、LR swap、M2S、ENDIAN swap 和 underrun sample。 playback 或 loop active 时返回 IM_ERR_BUSY。

3.5 hal_aic_configure_capture

error_code_t hal_aic_configure_capture(
    hal_aic_handle_t *haic,
    const hal_aic_capture_config_t *config);

配置 ISS、mono channel、endian 和 echo-cancellation capture。capture active 时返回 IM_ERR_BUSY。

3.6 FIFO threshold

error_code_t hal_aic_fifo_threshold_configure(
    hal_aic_handle_t *haic,
    hal_aic_stream_t stream,
    uint8_t threshold);

threshold 是 entry 数,不是寄存器 field。HAL 内部把 TX 转换为 TFTH=threshold/2, RX 转换为 RFTH=threshold/2-1,LOOP 转换为 TFLTH=threshold/2-1。

3.7 FIFO status 和错误

error_code_t hal_aic_fifo_status_get(
    const hal_aic_handle_t *haic,
    hal_aic_stream_t stream,
    hal_aic_fifo_status_t *status);

error_code_t hal_aic_fifo_error_clear(
    hal_aic_handle_t *haic,
    hal_aic_stream_t stream);

error_code_t hal_aic_fifo_error_irq_configure(
    hal_aic_handle_t *haic,
    hal_aic_stream_t stream,
    bool enable);

Playback 清 TX underrun,capture/loop 清对应 overrun。错误位是 write-zero-to-clear, 必须串行化调用与 hal_aic_irq_handler()。

3.8 FIFO data address

error_code_t hal_aic_fifo_data_address_get(
    const hal_aic_handle_t *haic,
    hal_aic_stream_t stream,
    uintptr_t *address);

playback/capture 返回 AICDR,loop 返回 AICLR。该接口不访问硬件,DMA 地址转换 由上层完成。

3.9 FIFO flush

error_code_t hal_aic_fifo_flush(hal_aic_handle_t *haic,
                                hal_aic_stream_t stream,
                                uint32_t timeout_us);

只支持 playback/capture。playback flush 在 loop active 时也返回 IM_ERR_BUSY。 flush 需要 BIT_CLK。函数会临时 enable AIC 并在结束后恢复原 enable 状态。

3.10 Stream start/stop

error_code_t hal_aic_stream_start(hal_aic_handle_t *haic,
                                  hal_aic_stream_t stream,
                                  hal_aic_transfer_mode_t mode);

error_code_t hal_aic_stream_stop(hal_aic_handle_t *haic,
                                 hal_aic_stream_t stream);

start:

  • 已 active 返回 IM_ERR_BUSY。
  • 第一个 active stream 会 enable AIC。
  • capture 在 start 前清 RX error 并 flush RX FIFO。
  • playback 不自动预填 TX FIFO。
  • DMA mode 只开启 AIC DMA request,不启动外部 DMA。

stop:

  • 立即关闭 stream enable、FIFO IRQ 或 DMA request。
  • capture stop 会逐项弹出 RX FIFO。
  • 最后一个 stream stop 后关闭 AIC module enable。
  • 不停止外部 DMA channel。

3.11 FIFO 直接访问

void hal_aic_playback_fifo_write(hal_aic_handle_t *haic,
                                 uint32_t data);

uint32_t hal_aic_capture_fifo_read(
    const hal_aic_handle_t *haic);

uint32_t hal_aic_loop_fifo_read(
    const hal_aic_handle_t *haic);

这些是 unchecked single-MMIO API。调用前必须通过 FIFO level 保证有空间或非空。

4. HAL 中断与 callback

4.1 Callback 注册

error_code_t hal_aic_callback_register(
    hal_aic_handle_t *haic,
    hal_aic_callback_id_t callback_id,
    hal_aic_callback_t callback);

callback == NULL 会恢复 no-op callback。callback 在 AIC IRQ context 执行,不得阻塞、 sleep 或执行 PCM 算法。

4.2 IRQ handler

void hal_aic_irq_handler(hal_aic_handle_t *haic);

行为:

  1. 读取 AICCR enable mask 和 AICSR。
  2. 分别汇总 playback/capture/loop events。
  3. 清除 TX underrun、RX overrun、loop overrun。
  4. latch HAL_AIC_ERROR_*。
  5. 调用对应 callback。

FIFO service 是 level-sensitive,只有 callback 实际填/读 FIFO 后才能消除。

5. 典型调用流程

5.1 HAL playback/capture

hal_aic_handle_t aic;
hal_aic_connection_config_t connection = {
    .wire_mode = HAL_AIC_WIRE_MODE_4,
    .playback_codec = HAL_AIC_CODEC_EXTERNAL,
    .capture_codec = HAL_AIC_CODEC_EXTERNAL,
    .playback_clock_role = HAL_AIC_CLOCK_MASTER,
    .capture_clock_role = HAL_AIC_CLOCK_MASTER,
};

hal_aic_handle_init(&aic, AIC);
hal_aic_init(&aic);
hal_aic_configure_connection(&aic, &connection);
hal_aic_clock_enable(&aic);
hal_aic_clock_set_sample_rate(&aic,
                              HAL_AIC_STREAM_PLAYBACK,
                              16000U);
hal_aic_reset(&aic, HAL_AIC_STREAM_PLAYBACK, 10000U);
hal_aic_configure_format(&aic, HAL_AIC_FORMAT_I2S);
hal_aic_sample_signedness_configure(&aic,
                                    HAL_AIC_SAMPLE_SIGNED);

之后配置 playback/capture、FIFO threshold、IRQ 和 DMA,再调用 hal_aic_stream_start()。

5.2 HAL LOOP

uintptr_t loop_address;

hal_aic_fifo_threshold_configure(&aic,
                                 HAL_AIC_STREAM_LOOP,
                                 16U);
hal_aic_fifo_data_address_get(&aic,
                              HAL_AIC_STREAM_LOOP,
                              &loop_address);

hal_aic_stream_start(&aic,
                     HAL_AIC_STREAM_PLAYBACK,
                     HAL_AIC_TRANSFER_DMA);
hal_aic_stream_start(&aic,
                     HAL_AIC_STREAM_LOOP,
                     HAL_AIC_TRANSFER_DMA);

loop_address 是 AICLR。外部 DMA 按 period 从该地址读取 reference PCM。playback、 loop 和 capture 同时运行时分别使用独立 buffer。

LOOP 停止时先停外部 DMA,再调用:

hal_aic_stream_stop(&aic, HAL_AIC_STREAM_LOOP);

5.3 HAL stop

hal_aic_stream_stop(&aic, HAL_AIC_STREAM_PLAYBACK);
hal_aic_deinit(&aic);

外部 DMA 必须由上层先 stop,HAL 只关闭 AIC DMA request。