API GUIDE / JPEGD / Middleware
G32S10 JPEGD Middleware 接口
Middleware 层接口:输入 JPEG 和输出 buffer 在 im_jpegd_decode() 返回前必须保持有效。
1. 公开数据类型
1.1 im_jpegd_frame_t
| 字段 | 类型 | 说明 |
|---|---|---|
jpeg_addr | const void * | 完整 JPEG 文件地址,包含 SOI 和所有头 |
jpeg_size | uint32_t | 完整 JPEG 文件大小 |
dst_y_addr | void * | 输出 Y 或 packed RAW 基地址 |
dst_uv_addr | void * | NV12 UV 地址;NULL 使用紧凑布局 |
dst_buf_size | uint32_t | 输出 buffer 可写容量 |
width | uint32_t | 成功解码后填写的图像宽度 |
height | uint32_t | 成功解码后填写的图像高度 |
output_size | uint32_t | 成功解码后填写的 RAW 字节数 |
输入 JPEG 和输出 buffer 在 im_jpegd_decode() 返回前必须保持有效。
2. 默认值和必填字段
2.1 im_jpegd_job_struct_init()
void im_jpegd_job_struct_init(im_jpegd_job_t *job);
该接口只初始化 job,不初始化 frame。主要默认值:
| 字段 | 默认值 |
|---|---|
out_format |
HAL_JPEGD_PIX_FMT_NV12 |
runtime.re.restart_markers_en |
false |
runtime.timeout_en |
false |
runtime.timeout_threshold |
0x00ffffff |
runtime.timeout_ms |
1000 |
runtime.reset_cycle |
10 |
2.2 frame 必填字段
调用 im_jpegd_decode() 前必须填写:
jpeg_addrjpeg_sizedst_y_addrdst_buf_size
NV12 时可以填写 dst_uv_addr,也可以留 NULL 使用紧凑布局。
3. 初始化和反初始化
3.1 im_jpegd_init()
error_code_t im_jpegd_init(uint32_t timeout_ms);
流程:
检查内部状态必须为
UNINITIALIZED。申请并使能
IRQ_JPEG_DEC。使用 HAL 默认配置初始化 JPEGD。
注册 MID frame-event callback。
保存 frame-end 等待超时,进入
INITIALIZED。
timeout_ms == 0 时使用内部默认值 1000 ms。
该超时只控制 MID 等待 frame-end 的时间,不使用 job.runtime.timeout_ms。
返回:
| 返回值 | 原因 |
|---|---|
IM_OK | 初始化成功 |
IM_ERR_BUSY | 会话已存在 |
| 其他错误 | IRQ 或 HAL 初始化失败 |
IRQ 初始化成功但 HAL 初始化失败时,IRQ 会被关闭。
3.2 im_jpegd_deinit()
error_code_t im_jpegd_deinit(void);
注销 MID callback,调用 hal_jpegd_deinit(),关闭 IRQ 和 waiter,并清零
JPEGD 运行时上下文。
已经在 UNINITIALIZED 时直接返回 IM_OK。否则返回
hal_jpegd_deinit() 的结果。
4. 解码流程
error_code_t im_jpegd_decode(const im_jpegd_job_t *job, im_jpegd_frame_t *frame);
4.1 输入检查
以下任一条件成立时返回 IM_ERR_INVALID_PARAM:
job == NULLframe == NULLframe->jpeg_addr == NULLframe->jpeg_size == 0frame->dst_y_addr == NULLframe->dst_buf_size == 0
之后再检查会话状态:
UNINITIALIZED:IM_ERR_NOT_READYRUNNING:IM_ERR_BUSY
5. 输出格式、尺寸和 buffer
5.1 对齐、stride 和输出大小
| 格式 | MCU 宽/高 | stride | 输出大小 |
|---|---|---|---|
Y | 8/8 | ALIGN_UP(width, 8) | stride * ALIGN_UP(height, 8) |
NV12 | 16/16 | ALIGN_UP(width, 16) | stride * ALIGN_UP(height, 16) * 3 / 2 |
YUYV | 16/8 | ALIGN_UP(width, 16) * 2 | stride * ALIGN_UP(height, 8) |
当前 JPEGD HAL 的其他主要限制:
| 项 | 当前限制 |
|---|---|
| 解码宽高 | 8-1920 / 8-1440 |
dst_y_addr / UV 地址 | 非空且至少 16-byte 对齐 |
out_format | NV12、YUYV 或单 Y |
| QT/Huffman 表指针 | 全部非空 |
reset_cycle | 0-15 |
restart_mcu_num | 启用 restart 时必须大于 0 且小于等于 MCU 总数 |
dst_buf_size 必须覆盖 HAL 计算出的完整输出大小,否则 HAL 在启动前或取结果
时返回 IM_ERR_NO_SPACE。
6. 超时和 restart 配置
存在两个不同层次的超时:
| 配置 | 作用 |
|---|---|
im_jpegd_init(timeout_ms) |
MID 等待 frame-end 的软件超时 |
job.runtime.timeout_en / timeout_threshold |
JPEGD 硬件 timeout 检测 |
job.runtime.timeout_ms 不控制 MID 的 waiter,实际等待时间由
im_jpegd_init() 的参数决定。
restart marker 信息来自 JPEG DRI:
runtime.re.restart_markers_enruntime.re.restart_mcu_num
每次 decode 时 MID 会覆盖调用方 job 中的这两个字段。
7. 错误处理和自动恢复
7.1 中断事件
MID callback 只在内部状态为 RUNNING 时记录事件并唤醒 waiter。
| HAL 事件 | MID 结果 |
|---|---|
HAL_JPEGD_IRQ_EVENT_FRAME_DONE |
成功 |
HAL_JPEGD_IRQ_EVENT_TIMEOUT |
IM_ERR_TIMEOUT |
| 其他事件 | IM_ERR_IO |
软件等待超时也返回 IM_ERR_TIMEOUT。
7.2 活跃操作失败
解析完成后,如果 sequence 配置、启动、等待或取结果失败,MID 会:
- 将内部状态置为
INITIALIZED。 - 调用
hal_jpegd_reset()尝试复位 JPEGD。 - 返回触发失败的原始错误码。
如果 reset 也失败,MID 只记录日志,仍然返回原始错误;内部状态已经是
INITIALIZED。因此硬件复位失败这一附加错误不会覆盖第一次失败的原因。
JPEG 头解析失败发生在配置和启动之前,不会触发硬件 reset。
7.3 没有公开 reset 接口
JPEGD MID 当前没有 im_jpegd_reset()。活跃解码失败后的复位由
im_jpegd_decode() 内部自动完成。需要彻底重建时使用
im_jpegd_deinit() + im_jpegd_init()。
8. 时钟接口
error_code_t im_jpegd_set_clk_rate(uint32_t rate_hz);
uint32_t im_jpegd_get_clk_rate(void);
状态约束:
| 状态 | im_jpegd_set_clk_rate() |
|---|---|
UNINITIALIZED |
IM_ERR_NOT_READY |
INITIALIZED / CONFIGURED |
允许设置 |
RUNNING |
IM_ERR_BUSY |
rate_hz == 0 选择不超过控制器上限的最高频率。父时钟固定为 MPLL,分频比
向上取整。CGU_JPEG 与 JPEGE 共用,两个 MID 同时修改时最后写入者生效。
im_jpegd_get_clk_rate() 不检查会话状态,与 decode 共用互斥锁;无法测量时
返回 0。每次 decode 都会重新配置 sequence,因此在不同解码之间调整时钟
不需要额外重新配置。
9. 典型用法
9.1 先解析头以计算输出容量
im_jpegd_header_parse_result_t parsed;
uint32_t stride;
uint32_t lines;
uint32_t output_size;
ret = im_jpegd_parse_header(jpeg_data, jpeg_size, &parsed);
if (ret != IM_OK) {
return ret;
}
stride = ALIGN_UP(parsed.param.width, 16U);
lines = ALIGN_UP(parsed.param.height, 16U);
output_size = stride * lines * 3U / 2U;im_jpegd_decode() 内部会再次解析头部,因此预解析只适合用于分配输出 buffer
或提前校验,不是完成解码的必要步骤。
9.2 设置时钟
ret = im_jpegd_init(0U);
if (ret != IM_OK) {
return ret;
}
ret = im_jpegd_set_clk_rate(200000000U);
if (ret == IM_OK) {
actual_hz = im_jpegd_get_clk_rate();
}