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;
约束:
channels只能是 1 或 2。packed_16bit和endian_swap只允许sample_width == 16。packed_lr_swap只允许 16-bit、packed 和 stereo。RIGHT_FIRST只允许 stereo。- 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);
行为:
- 读取 AICCR enable mask 和 AICSR。
- 分别汇总 playback/capture/loop events。
- 清除 TX underrun、RX overrun、loop overrun。
- latch
HAL_AIC_ERROR_*。 - 调用对应 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。