API GUIDE / I2D / HAL
G32S10 I2D HAL 接口
HAL 层接口:90°/270° 时输出宽高互换,HAL 用这一点校验目标 stride 并计算布局。
1. 分层关系与依赖
1.1 HAL 中每类操作调用的 LL 接口
| HAL 操作 | 用到的 LL 接口 |
|---|---|
hal_i2d_init() | enable_clock_gate、safe_reset、set_outstanding_limit、set_timeout_value、enable/disable_timeout_restart、set_irq_mask、clear_irq |
hal_i2d_start() | set_mode、set_image_size、set_source_addr、set_destination_addr、set_source_stride、set_destination_stride、update_shadow、clear_irq、safe_reset、start |
hal_i2d_stop() / hal_i2d_deinit() | stop、safe_reset、disable_irq、clear_irq |
hal_i2d_irq_handler() | get_irq_status、get_irq_mask、clear_irq |
hal_i2d_irq_config() | set_irq_mask |
hal_i2d_layout_get() | 无(纯软件计算,不访问硬件) |
2. 构建配置与文件组织
2.1 Kconfig
menuconfig DRIVER_I2D bool "I2D driver" default n help Build the G32S10 I2D image rotation driver.
打开
CONFIG_DRIVER_I2D=y才会编译hal_i2d.c。HAL 依赖 CPM(
CONFIG_DRIVER_CPM=y);回调示例还依赖 IRQ 框架 (CONFIG_DRIVER_IRQ=y)。LL 只依赖头文件,但直接操作寄存器前必须先打开时钟(见 §6.8)。
2.2 CMake
if(CONFIG_DRIVER_I2D)
imde_component_register(NAME g32s10_i2d SRCS
"${CMAKE_CURRENT_LIST_DIR}/hal_i2d.c")
endif()3. 资源模型与公开类型
3.1 公开类型
3.1.1 hal_i2d_format_t
| 值 | 寄存器值 | 说明 | bpp | 宽高对齐 |
|---|---|---|---|---|
HAL_I2D_FORMAT_NV12 | 0 | NV12 半平面 YUV 4:2:0 | 1 | 8 |
HAL_I2D_FORMAT_RAW8 | 1 | RAW8/Y8 单平面 | 1 | 8 |
HAL_I2D_FORMAT_RGB565 | 2 | RGB565 packed | 2 | 4 |
HAL_I2D_FORMAT_ARGB8888 | 3 | ARGB8888 packed | 4 | 2 |
HAL_I2D_FORMAT_YUV422 | 4 | YUV422 packed(YUYV) | 2 | 4 |
3.1.2 hal_i2d_rotate_t
| 值 | 寄存器值 | 说明 |
|---|---|---|
HAL_I2D_ROTATE_0 | 0 | 不旋转 |
HAL_I2D_ROTATE_90 | 1 | 顺时针 90° |
HAL_I2D_ROTATE_180 | 2 | 180° |
HAL_I2D_ROTATE_270 | 3 | 顺时针 270° |
90°/270° 时输出宽高互换,HAL 用这一点校验目标 stride 并计算布局。
3.1.3 hal_i2d_state_t
| 值 | 说明 |
|---|---|
HAL_I2D_STATE_RESET | 句柄未初始化 |
HAL_I2D_STATE_READY | 已初始化且空闲;hal_i2d_start() 只在该状态接受调用 |
HAL_I2D_STATE_BUSY | 正在处理一帧 |
HAL_I2D_STATE_TIMEOUT | 记录到硬件超时 |
HAL_I2D_STATE_ERROR | 记录到 outstanding 错误 |
3.1.4 hal_i2d_irq_t / hal_i2d_event_t
| HAL 位 | 值 | 硬件位 | 对应 event |
|---|---|---|---|
HAL_I2D_IRQ_FRAME_DONE | 1 << 0 | IRQ[0] | HAL_I2D_EVENT_FRAME_DONE |
HAL_I2D_IRQ_TIMEOUT | 1 << 1 | IRQ[1] | HAL_I2D_EVENT_TIMEOUT |
HAL_I2D_IRQ_OUTSTANDING | 1 << 2 | IRQ[2] | HAL_I2D_EVENT_OUTSTANDING |
HAL 的 IRQ 位与 event 值刻意保持一致,便于寄存器 mask 直接映射。
3.1.5 hal_i2d_error_t
| 标志 | 值 | 说明 |
|---|---|---|
HAL_I2D_ERROR_NONE | 0 | 无错误 |
HAL_I2D_ERROR_INVALID_PARAM | 1 << 0 | 启动时布局/参数校验失败 |
HAL_I2D_ERROR_TIMEOUT | 1 << 1 | 硬件超时 |
HAL_I2D_ERROR_OUTSTANDING | 1 << 2 | outstanding 上限中断 |
错误标志是锁存的或运算结果,只能通过 hal_i2d_error_clear() 清除。
3.1.6 hal_i2d_init_config_t
| 字段 | 类型 | 说明 |
|---|---|---|
instance | i2d_regs_t * | 必须为 I2D |
outstanding_limit | uint32_t | AXI 读 outstanding 上限,1..255 |
timeout_value | uint32_t | 硬件超时上限;0 表示关闭硬件超时 |
timeout_restart | bool | true 时超时后硬件自动重启 |
frame_done_irq_enable | bool | 是否放行 frame-done 中断 |
timeout_irq_enable | bool | 是否放行 timeout 中断 |
outstanding_irq_enable | bool | 是否放行 outstanding 中断 |
3.1.7 hal_i2d_config_t
| 字段 | 类型 | 说明 |
|---|---|---|
src_buf / dst_buf | void * | 源/目标图像起始地址,非空 |
src_uv_buf / dst_uv_buf | void * | NV12 源/目标 UV 平面地址;NULL 时 HAL 推导;其它格式必须为 NULL |
width / height | uint32_t | 源图宽高 |
format | hal_i2d_format_t | 源和目标共用的像素格式 |
rotate | hal_i2d_rotate_t | 旋转角度 |
horizontal_mirror / vertical_flip | bool | 水平镜像 / 垂直翻转 |
src_stride_y / dst_stride_y | uint32_t | Y 平面 stride;0 表示由 HAL 推导 |
src_stride_uv / dst_stride_uv | uint32_t | NV12 UV 平面 stride;0 表示由 HAL 推导;其它格式必须为 0 |
3.1.8 hal_i2d_layout_t
hal_i2d_layout_get() 的输出,供调用方做容量检查与 cache 维护:
| 字段 | 说明 |
|---|---|
src_y_addr / src_uv_addr | 源 Y/UV 平面地址 |
dst_y_addr / dst_uv_addr | 目标 Y/UV 平面地址 |
src_y_stride / src_uv_stride | 源 Y/UV stride |
dst_y_stride / dst_uv_stride | 目标 Y/UV stride |
src_y_size / src_uv_size | 源 Y/UV 平面字节数 |
dst_y_size / dst_uv_size | 目标 Y/UV 平面字节数 |
src_buffer_size / dst_buffer_size | 源/目标总字节数(Y + UV) |
3.1.9 hal_i2d_handle_t
| 字段 | 说明 |
|---|---|
instance | 绑定的寄存器基地址 |
init | hal_i2d_init() 保存的初始化配置副本 |
state | hal_i2d_state_t 运行时状态 |
error_code | 锁存的 HAL_I2D_ERROR_* |
callback | 可选的中断事件回调 |
user_ctx | 回调上下文 |
3.2 默认值
hal_i2d_init_config_default() 不访问硬件:
| 字段 | 默认值 |
|---|---|
instance | I2D |
outstanding_limit | HAL_I2D_DEFAULT_OUTSTANDING = 0x40 |
timeout_value | UINT32_MAX |
timeout_restart | true |
frame_done_irq_enable | true |
timeout_irq_enable | true |
outstanding_irq_enable | false |
hal_i2d_config_default() 清零整个结构体,只把 format 设为 NV12、rotate 设为ROTATE_0。宽高、地址与 stride 必须在使用前填写。
3.3 缓冲区所有权与对齐
源/目标 buffer 完全由调用方分配、填充和释放;HAL 只拿地址。
容量至少是
hal_i2d_layout_get()返回的src_buffer_size/dst_buffer_size。 90°/270° 时两者可能不同,按各自的值准备,不要假设相等。推荐按
HAL_I2D_BUFFER_ALIGN(64 字节)对齐——它同时是 cache 行大小。三个 官方示例都申请size + 64再向上对齐,硬件地址取对齐后的指针。同一块目标 buffer 在 I2D 完成前不能交给 CPU 读或另一路 DMA 写;完成后再做 cache invalidate 才能读。
4. 接口规范
4.1 调用顺序速查
准备源/目标 buffer 并维护 cache
hal_i2d_start
轮询 handler 或等待 SoC IRQ 完成
invalidate 目标 buffer 并取结果
异常时调用 hal_i2d_stop / hal_i2d_error_clear
5. 行为约束与实现说明
5.1 布局推导公式
hal_i2d_layout_get() 的规则:
| 项 | 规则 |
|---|---|
bpp | ARGB8888 = 4;RGB565/YUV422 = 2;NV12/RAW8 = 1 |
输出宽 out_w | 90°/270° 为 height,否则 width |
输出高 out_h | 90°/270° 为 width,否则 height |
src_y_stride | src_stride_y != 0 ? src_stride_y : width * bpp |
dst_y_stride | dst_stride_y != 0 ? dst_stride_y : out_w * bpp |
src_uv_stride | 仅 NV12:src_stride_uv != 0 ? src_stride_uv : width |
dst_uv_stride | 仅 NV12:dst_stride_uv != 0 ? dst_stride_uv : out_w |
| 源 Y 行数 | NV12 为 ALIGN_UP(height, 16),其它格式为 height |
| 目标 Y 行数 | NV12 为 ALIGN_UP(out_h, 16),其它格式为 out_h |
| NV12 源 UV 地址 | 显式 src_uv_buf,否则 src_y_addr + src_y_stride * 源 Y 行数 |
| NV12 目标 UV 地址 | 显式 dst_uv_buf,否则 dst_y_addr + dst_y_stride * 目标 Y 行数 |
src_y_size | src_y_stride * 源 Y 行数 |
src_uv_size | 仅 NV12:src_uv_stride * (height / 2),否则 0 |
dst_y_size | dst_y_stride * 目标 Y 行数 |
dst_uv_size | 仅 NV12:dst_uv_stride * (out_h / 2),否则 0 |
*_buffer_size | 对应 Y + UV 之和(非 NV12 的 UV 为 0) |
算例:
| 配置 | 输出 | 源 stride / 大小 | 目标 stride / 大小 |
|---|---|---|---|
| 640x480 YUV422,90° | 480x640 | 1280 / 614,400 | 960 / 614,400 |
| 1280x1280 ARGB8888,90° | 1280x1280 | 5120 / 6,553,600 | 5120 / 6,553,600 |
| 1920x1080 NV12,90° | 1080x1920 | 1920 / 3,125,760 | 1080 / 3,110,400 |
| 1920x1080 RAW8,90° | 1080x1920 | 1920 / 2,073,600 | 1080 / 2,073,600 |
两个容易错的点:
NV12 的 Y 平面按 16 行对齐。1080 行会占 1088 行:
1920 * 1088 = 2,088,960,UV 从这之后开始;直接用width * height推 UV 地址会错位。90°/270° 后源/目标大小不同。NV12 1920x1080 转 90° 时源 3,125,760、 目标 3,110,400;按
max(src, dst)分配即可,但 cache 维护要按各自的真实大小做。
5.2 参数校验规则
hal_i2d_start() 与 hal_i2d_layout_get() 共用同一套校验:
| 检查项 | 约束 |
|---|---|
config、layout | 非空 |
src_buf、dst_buf | 非空 |
format | 五个枚举值之一 |
rotate | 四个枚举值之一 |
width / height | 8..3840 / 8..2160 |
| 对齐 | 宽、高必须整除格式对齐:ARGB8888 = 2;RGB565/YUV422 = 4;NV12/RAW8 = 8 |
src_stride_y | 非 0 时 <= 0xFFFF 且 >= width * bpp |
dst_stride_y | 非 0 时 <= 0xFFFF 且 >= out_w * bpp |
src_stride_uv | NV12 且非 0 时 <= 0xFFFF 且 >= width |
dst_stride_uv | NV12 且非 0 时 <= 0xFFFF 且 >= out_w |
| 非 NV12 格式 | 两个 UV 指针必须为 NULL,两个 UV stride 必须为 0 |
config_default() 的默认格式是 NV12,因此不填 src_uv_buf 时 HAL 会按 Y 平面推导
UV 地址。如果要处理单平面/打包格式,必须显式设置 format,否则校验和布局都会
按 NV12 处理。
5.3 中断分发、事件回调与屏蔽语义
irq_state是锁存状态,读不清除;清除要写irq_clear。irq_mask是「1 = 屏蔽」。HAL 初始化按frame_done_irq_enable/timeout_irq_enable/outstanding_irq_enable写一次掩码;之后可以用hal_i2d_irq_config()改。hal_i2d_irq_handler()只处理latched & enabled;被屏蔽的位保持锁存,留给 轮询读irq_state的人。回调按 TIMEOUT → OUTSTANDING → FRAME_DONE 顺序逐个触发;一次入口可能回调多次。
回调里只做「置标志/计数/唤醒」;cache 维护与日志放到任务上下文。
6. 典型用法:从零开始
本节假设你手上有一块能跑 SDK 的板子,想把「内存里的图 → 旋转/镜像 → 内存里的图」 直接做在 HAL 或 LL 上。
6.1 第二步:算布局
hal_i2d_layout_get() 是纯软件计算,可以在 hal_i2d_init() 之前调用。还不知道
buffer 地址时用占位地址(函数只校验非空,不解引用):
static error_code_t i2d_probe(uint32_t width, uint32_t height,
hal_i2d_format_t format, hal_i2d_rotate_t rotate,
hal_i2d_layout_t *layout)
{
hal_i2d_config_t probe;
(void)hal_i2d_config_default(&probe);
probe.src_buf = (void *)(uintptr_t)HAL_I2D_BUFFER_ALIGN; /* 占位 */
probe.dst_buf = (void *)(uintptr_t)(HAL_I2D_BUFFER_ALIGN * 2U); /* 占位 */
probe.width = width;
probe.height = height;
probe.format = format;
probe.rotate = rotate;
return hal_i2d_layout_get(&probe, layout);
}例:640x480 YUV422 顺时针 90°,得到 480x640、源 stride 1280 / 目标 stride 960、 两边各 614,400 字节。算例见 §6.3。
7. 示例程序解读
三个示例结构相同,只是「谁负责等待、谁负责时钟」不同:
| 文件 | 层级 | 等待方式 | 时钟 | 布局 | 入口 |
|---|---|---|---|---|---|
i2d_blocking_transform_example.c | HAL | 任务里轮询 hal_i2d_irq_handler() | HAL | HAL | example_i2d_blocking_transform_run() |
i2d_callback_transform_example.c | HAL | IRQ_I2D + HAL 回调 | HAL | HAL | example_i2d_callback_transform_run() |
i2d_ll_example.c | LL | 轮询 irq_state | 自己开/关 | 自己算 | example_i2d_ll_run() |
8. 集成到你自己的工程
8.1 三种等待形态怎么选
| 场景 | 形态 |
|---|---|
| bring-up、单帧验证、测试项 | 任务轮询 hal_i2d_irq_handler() |
| 正式业务、任务需要睡眠 | SoC IRQ + 回调 + 信号量 |
| 调试寄存器、验证中断路径 | LL 轮询 irq_state |
不要混用轮询与 ISR:两者都会清 IRQ_STATE,事件会被对方吃掉。