ISP模块参数统一对外接口插值逻辑(二)

ISP模块参数统一对外接口插值逻辑(二) 目录1. 两边变量原始类型2. 强制类型转换 (uint8_t*) 的作用3. 为什么要统一用 uint8_t* 存储所有模块的标定参数池4. 内存布局直观演示5. 对比其他模块验证通用性6. 总结在上一篇博文中ISP模块参数统一对外接口插值逻辑一重点讲解了整个ISP Pipeline中使用统一接口进行插值的原理。这篇博文主要讲解其中的一些细节。在标定初始化如下代码段中static uint32_t lsc_cal_gain_list[] {100, 400, 800, 1600, 3200}; // 升序标定增益 static ISP_LSC_ATTR lsc_cal_param_pool[5]; // 5组LSC标定参数 ISP_CAL_HEAD_T g_lsc_cal_head { .cal_cnt 5, .p_gain_arr lsc_cal_gain_list, .p_param_pool (uint8_t*)lsc_cal_param_pool, .param_single_size sizeof(ISP_LSC_ATTR), .enable_interp 1, // LSC随增益插值 .gain_sorted_ok 1 // 增益数组升序校验通过 };工程启动时从 Flash / 配置文件填充lsc_cal_param_pool每组 ISO 对应的完整 LSC 参数DPC 模块配置enable_interp0静态坏点不随增益插值。对其中.p_param_pool (uint8_t*)lsc_cal_param_pool, 这个赋值的理解如下1. 两边变量原始类型// 标定表头结构体成员 uint8_t* p_param_pool; // LSC标定参数数组多组完整LSC结构体 static ISP_LSC_ATTR lsc_cal_param_pool[];lsc_cal_param_pool结构体数组一块连续内存存放 N 档 ISO 对应的整套 LSC 参数p_param_pool结构体里的通用指针设计为uint8_t*字节指针 / 无类型内存指针目的是统一管理所有不同大小的 ISP 结构体LSC/DRC/Gamma/DPC 尺寸全不一样。2. 强制类型转换(uint8_t*)的作用场景矛盾直接赋值会报类型不匹配// 报错AK_ISP_LSC_ATTR* 不能直接赋值给 uint8_t* p_param_pool lsc_cal_param_pool;数组名lsc_cal_param_pool等价于ISP_LSC_ATTR *是带结构体类型的指针 而p_param_pool是字节裸指针 uint8_t*两者类型不同编译器类型校验失败。转换的本质(uint8_t*)lsc_cal_param_pool 把「指向 LSC 结构体的类型指针」强制转换成「按单字节寻址的通用内存指针」只改变编译器对内存的解析方式不改变内存地址本身。举个直观例子 假设数组首地址是0x20000000ISP_LSC_ATTR *每次指针 1偏移sizeof(ISP_LSC_ATTR)字节一整套 LSC 参数长度uint8_t *每次指针 1只偏移 1 个字节纯内存块操作3. 为什么要统一用 uint8_t* 存储所有模块的标定参数池工程里每个 ISP 模块结构体尺寸完全不同LSCsizeof(ISP_LSC_ATTR)几百字节带二维网格DRCsizeof(ISP_DRC_REG)几十字节YUV Gamma LUT256 点数组几百字节DPC小型结构体如果给ISP_CAL_HEAD_T为每个模块单独定义结构体指针会出现结构体重复定义代码冗余爆炸二分后计算第 N 组参数地址逻辑无法通用。统一字节指针的通用寻址公式核心用途代码中获取第left_idx档标定参数时// 计算第 left_idx 组参数起始地址 uint8_t* p_cal_low p_cal-p_param_pool left_idx * p_cal-param_single_size;param_single_size当前模块单个结构体的字节长度sizeof(ISP_LSC_ATTR)p_param_pool是uint8_t*指针加法 字节偏移可以通过「下标 × 单组长度」精准跳转到任意一档标定参数的内存起始位置。不管这个模块是 LSC、DRC 还是 Gamma这套寻址公式完全通用不需要为每种结构体写单独的数组下标访问逻辑。4. 内存布局直观演示static ISP_LSC_ATTR lsc_cal_param_pool[5]; // 5档标定参数连续内存 // 内存排布 // [0x00 ~ SZ-1] 第0档ISO参数 // [SZ ~ 2*SZ-1] 第1档ISO参数 // [2*SZ ~ 3*SZ-1] 第2档ISO参数 // SZ sizeof(AK_ISP_LSC_ATTR) ISP_CAL_HEAD_T g_lsc_cal_head { .cal_cnt 5, .p_gain_arr lsc_cal_gain_list, // 把结构体数组首地址转为字节指针存入通用内存池指针 .p_param_pool (uint8_t*)lsc_cal_param_pool, .param_single_size sizeof(ISP_LSC_ATTR), .enable_interp 1, .gain_sorted_ok 1 };当代码需要取第 2 档标定参数uint32_t SZ g_lsc_cal_head.param_single_size; uint8_t* p_2nd_param g_lsc_cal_head.p_param_pool 2 * SZ; // p_2nd_param 直接指向 lsc_cal_param_pool[2] 的内存首地址 // 使用时再强转回结构体指针 const ISP_LSC_ATTR *p_para (const ISP_LSC_ATTR *)p_2nd_param;5. 对比其他模块验证通用性DRC 模块标定定义static ISP_DRC_ATTR drc_cal_param_pool[2]; ISP_CAL_HEAD_T g_drc_cal_head { .cal_cnt 2, .p_gain_arr drc_cal_gain_list, // 同样强转 uint8_t*和LSC共用一套寻址逻辑 .p_param_pool (uint8_t*)drc_cal_param_pool, .param_single_size sizeof(ISP_DRC_ATTR), .enable_interp 0, .gain_sorted_ok 1 };DRC、LSC 两套完全不同的结构体都存入uint8_t* p_param_pool二分查找、参数拷贝、插值回调的上层代码不需要做任何区分一套逻辑适配全部 ISP 模块。6. 总结lsc_cal_param_pool是结构体数组自带类型ISP_LSC_ATTR *p_param_pool是通用字节裸指针 uint8_t*用于统一管理所有尺寸不同的 ISP 标定参数内存(uint8_t*)强制转换消除类型不匹配让代码可以通过「下标 × 单组长度」的通用字节偏移算法任意跳转到任意一档标定参数实现多模块统一调度。