目录换 Sensor 实战全流程总览哪些能复用哪些必须重写第1步创建新目录和文件第2步改 cmos.h — 全是宏定义查手册填写第3步改 sensor_ctl.c — 完全重写初始化序列3.1 I2C 读写函数 — 改个 I2C 地址就行3.2 初始化序列 — 从 FAE 拿到3.3 standby/restart — 改寄存器地址第4步改 cmos.c — 最核心改得最多4.1 寄存器地址 — 查手册4.2 模式表 — 填入新值4.3 AE 参数 — 逐项计算最关键4.4 Again 映射表 — 最容易出错4.5 Black Level — 从手册查4.6 AWB 参数 — 需要校准4.7 导出对象 — 改名字第5步改 cmos_ex.h — ISP 调优参数第6步改 Makefile第7步应用层对接 — 让系统加载新驱动实战排坑清单总结信息获取渠道二MIPI 配置为什么不在 sensor 驱动里你项目里的 MIPI 配置层次1ext_data_type — MIPI 扩展数据类型层次2combo_dev_attr — SOC 端 MIPI 接收器配置层次3vi_dev_attr — SOC 端 VI 接口配置MIPI 配置的关键参数解读换 sensor 时 MIPI 配置怎么改配错 MIPI 的典型症状换 Sensor 实战全流程假设你要把 IMX989 换成IMX662举个例子我从零开始讲。总览哪些能复用哪些必须重写┌───────────────────────────────────────────────────────────┐│ 换 Sensor 改动量评估 ││ ││ 直接复用不改或微调 必须重写依赖新sensor ││ ────────────────────── ────────────────────────── ││ cmos.c 的回调框架结构 cmos.h 的宏定义 ││ cmos.c 的全局变量声明 sensor_ctl.c 的初始化序列 ││ sensor_register_callback() AE gain 映射表 ││ sensor_unregister_callback() AE 参数VMAX/again/dgain ││ regs_info 双缓冲机制 AWB Planck 曲线参数 ││ ISP 框架调用流程 Black Level 值 ││ Makefile微调名字 寄存器地址定义 ││ cmos_ex.h 的 ISP 模块结构 DNG/CFA 参数 ││ ────────────────────── ────────────────────────── ││ 约 40% 代码可复用 约 60% 需要重写 │└───────────────────────────────────────────────────────────┘第1步创建新目录和文件SDK路径下.../sensor/hi3519dv500/├── modules989/ ← 旧的 IMX989 驱动└── imx662/ ← 新建目录├── imx662_cmos.h├── imx662_cmos.c├── imx662_cmos_ex.h├── imx662_sensor_ctl.c└── Makefile实战建议复制 modules989 整个目录然后全局替换名字再逐个文件改内容。第2步改cmos.h— 全是宏定义查手册填写// 旧IMX989→ 新IMX662对照 // I2C 地址查新 sensor 手册的 Slave Address #define IMX662_I2C_ADDR 0x1A // IMX989 是 0x34IMX662 不同 // 地址/数据字节大多数 Sony sensor 都是 2字节地址1字节数据 #define IMX662_ADDR_BYTE 2 // 通常不变 #define IMX662_DATA_BYTE 1 // 通常不变 // 最大帧长查手册 VMAX 的位宽 #define IMX662_FULL_LINES_MAX 0xFFFFF // 20bit通常不变 // VMAX 值查手册每种模式的推荐 VMAX #define IMX662_VMAX_1920X1080P30_LINEAR (1125) // 从手册查 #define IMX662_VMAX_1920X1080P60_LINEAR (1125) // 从手册查 // 分辨率查手册的有效像素阵列 #define SENSOR_IMX662_WIDTH 1920 // IMX662 是 1080P sensor #define SENSOR_IMX662_HEIGHT 1080 // 模式枚举根据新 sensor 支持的模式定义 typedef enum { IMX662_SENSOR_1920X1080_30FPS_LINEAR_MODE, IMX662_SENSOR_1920X1080_60FPS_LINEAR_MODE, IMX662_MODE_BUTT } imx662_res_mode; // 模式表结构完全复用不需要改 typedef struct { td_u32 ver_lines; td_u32 max_ver_lines; td_float max_fps; td_float min_fps; td_u32 width; td_u32 height; td_u8 sns_mode; ot_wdr_mode wdr_mode; const char *mode_name; } imx662_video_mode_tbl;这是最直接的文件每一行都要改因为全是 sensor 硬件参数。关键数据来源参数查手册位置I2C 地址Serial Communication Interface 章节VMAX 值Readout Mode 章节每种模式的 Frame Length分辨率Image Size 章节FULL_LINES_MAXFrame Length 寄存器的位宽第3步改sensor_ctl.c— 完全重写初始化序列这是工作量最大的文件。初始化序列必须从 Sony FAE 拿到不能自己写。3.1 I2C 读写函数 — 改个 I2C 地址就行// 这部分逻辑完全一样只改宏名和 I2C 地址td_s32 imx662_write_register(ot_vi_pipe vi_pipe, td_u32 addr, td_u32 data) {i2c_data.dev_addr IMX662_I2C_ADDR; // 只改这里i2c_data.reg_addr addr;// ... 其余不变}3.2 初始化序列 — 从 FAE 拿到// FAFA 提供的通常是这样一个表可能叫 IMX662_Register_Set_*.xlsx//// Address | Value | Description// --------|--------|------------// 0x3000 | 0x01 | STANDBY// 0x3002 | 0x00 | XMSTA// 0x3004 | 0x01 | XVSIE// 0x0112 | 0x0A | ADC bit depth 10bit// 0x0340 | 0x04 | VMAX[15:8]// 0x0341 | 0x65 | VMAX[7:0] 1125// ... | ... | ...// 0x0100 | 0x01 | MODE_SEL streaming// 把这个表翻译成代码static td_s32 imx662_linear_1080p30_10bit_init_part1(ot_vi_pipe vi_pipe) {td_s32 ret TD_SUCCESS;ret imx662_write_register(vi_pipe, 0x3000, 0x01);ret imx662_write_register(vi_pipe, 0x3002, 0x00);ret imx662_write_register(vi_pipe, 0x0112, 0x0A);ret imx662_write_register(vi_pipe, 0x0340, 0x04);ret imx662_write_register(vi_pipe, 0x0341, 0x65);// ... 几百行 ...return ret;}实操经验FAE 给的寄存器表可能有 200-500 行不要试图理解每一行——很多是模拟电路偏置、时序微调你只需要识别出关键寄存器0x0100 → MODE_SELstreaming 控制0x0112/0x0113 → 位深0x0340/0x0341 → VMAX0x0202/0x0203 → CIT曝光时间0x0204/0x0205 → AGain0x020E/0x020F → DGain其余全部按表照抄3.3 standby/restart — 改寄存器地址void imx662_standby(ot_vi_pipe vi_pipe) { imx662_write_register(vi_pipe, 0x0100, 0x00); // 大多数 Sony sensor 一样 g_standby[vi_pipe] TD_TRUE; } void imx662_restart(ot_vi_pipe vi_pipe) { imx662_write_register(vi_pipe, 0x0100, 0x01); g_standby[vi_pipe] TD_FALSE; }第4步改cmos.c— 最核心改得最多4.1 寄存器地址 — 查手册// 不同 sensor 曝光/增益寄存器地址不同 // 必须查新 sensor 的手册 // IMX989: // IMX662示例需查手册确认: #define CIT_H 0x0202 #define CIT_H 0x0202 // Sony 系列通常一样 #define CIT_L 0x0203 #define CIT_L 0x0203 #define AGAIN_H 0x0204 #define AGAIN_H 0x0204 // Sony 系列通常一样 #define AGAIN_L 0x0205 #define AGAIN_L 0x0205 #define DGAIN_H 0x020E #define DGAIN_H 0x020E // Sony 系列通常一样 #define DGAIN_L 0x020F #define DGAIN_L 0x020F #define VMAX_H 0x0340 #define VMAX_H 0x0340 #define VMAX_L 0x0341 #define VMAX_L 0x0341Sony 系列的寄存器地址高度统一IMX989/IMX662/IMX678/IMX585 大部分共用。但不能想当然必须确认。4.2 模式表 — 填入新值const imx662_video_mode_tbl g_imx662_mode_tbl[IMX662_MODE_BUTT] { {1125, 0xFFFFF, 30, 0.5, 1920, 1080, 0, OT_WDR_MODE_NONE, IMX662_1920X1080_30FPS_LINEAR}, {1125, 0xFFFFF, 60, 0.5, 1920, 1080, 1, OT_WDR_MODE_NONE, IMX662_1920X1080_60FPS_LINEAR}, };4.3 AE 参数 — 逐项计算最关键static td_void cmos_get_ae_comm_default(...) { // ① full_lines_std从模式表取 ae_sns_dft-full_lines_std sns_state-fl_std; // 不用改 // ② flicker_freq看地区中国/欧洲50Hz美国/日本60Hz ae_sns_dft-flicker_freq 50 * 256; // 不用改 // ③ hmax_times必须重新算 // 公式1e9 / (VMAX × fps) ae_sns_dft-hmax_times (1000000000) / (1125 * 30); // ≈ 29630 ns // ④ again_accu查手册 AGain 步进精度 // Sony sensor 通常 1/256 步进 0.00390625 ae_sns_dft-again_accu.accu_type OT_ISP_AE_ACCURACY_TABLE; ae_sns_dft-again_accu.accuracy 0.00390625; // 通常不变 // ⑤ lines_per500ms重新算 ae_sns_dft-lines_per500ms 1125 * 30 / 2; // 16875 // ... 其余参数逐项填入 } static td_void cmos_get_ae_linear_default(...) { // ⑥ max_int_time重新算 ae_sns_dft-max_int_time ((1125 - 48) 3) 3; // 1072 // 注意48 是 IMX989 的值IMX662 可能不同查手册 CIT limit // ⑦ AGain 范围查手册 ae_sns_dft-max_again 65535; // 查手册 Analog Gain Range ae_sns_dft-min_again 1024; // ⑧ DGain 范围查手册 ae_sns_dft-max_dgain 65535; ae_sns_dft-min_dgain 256; // ⑨ init_exposure先用默认值跑起来再调 ae_sns_dft-init_exposure 30000; // 先给一个合理的估计值 // ⑩ offset查手册 FINE_INTEG_TIME ae_sns_dft-int_time_accu.offset (td_float)(FINE_TIME - 64000) / LINE_LENGTH; // 查手册 0x200/0x201 和 0x342/0x343 的默认值 }4.4 Again 映射表 — 最容易出错不同 sensor 的 AGain 映射公式完全不同IMX989Sony 1.x 系列again_db 16384 - 16384 × 1024 / again_lin公式来源Sony 提供的 Gain Conversion 应用笔记IMX662可能不同可能是线性映射again_db again_lin × 某系数也可能是分段映射必须从 Sony FAE 拿到转换公式如果你用错了公式AE 会算错增益画面要么过曝要么过暗而且 AE 永远收敛不了。4.5 Black Level — 从手册查#define IMX662_BLACK_LEVEL 1024 // 10bit: 1024, 12bit: 4096, 查手册 Optical Black Level4.6 AWB 参数 — 需要校准// Planck 曲线参数必须用新 sensor 实际镜头在标准光源下拍摄校准 // 不能直接抄 awb_sns_dft-p1 ???; // 需要校准 awb_sns_dft-p2 ???; // 需要校准 awb_sns_dft-q1 ???; // 需要校准 awb_sns_dft-a1 ???; // 需要校准 // ...校准方法用标准光源箱D65/A/CWF/TL84每个光源下拍一张 RAW计算 R/G/B 比值拟合 Planck 曲线。这通常是 IQ 团队的工作但你至少要知道这些值不是拍脑袋定的。4.7 导出对象 — 改名字ot_isp_sns_obj g_sns_imx662_obj { .pfn_register_callback sensor_register_callback, .pfn_un_register_callback sensor_unregister_callback, .pfn_standby imx662_standby, .pfn_restart imx662_restart, .pfn_mirror_flip TD_NULL, .pfn_set_blc_clamp TD_NULL, .pfn_write_reg imx662_write_register, .pfn_read_reg imx662_read_register, .pfn_set_bus_info imx662_set_bus_info, .pfn_set_init sensor_set_init };第5步改cmos_ex.h— ISP 调优参数这是工作量最大但优先级最低的文件。可以先用保底值跑起来后面慢慢调。实操策略1. 先把旧 sensor 的 cmos_ex.h 整个复制过来2. 只改确定不同的参数Black Level、DPC 阈值3. 编译、跑通、确认 AE/AWB 基本正常4. 后续再逐模块做 IQ 调优cmos_ex.h 各模块的优先级优先级模块原因 必须改BLC (Black Level)值不对 → 全画面偏色 必须改DPC (坏点校正)不同 sensor 坏点特征不同 建议改CCM (色彩矩阵)不同 sensor 光谱响应不同 建议改Noise Calibration不同 sensor 噪声特性不同 可延后Gamma, Sharpen, NR通用性较强不影响功能 可延后DRC, Dehaze, LDCI跟场景关系大跟 sensor 关系小第6步改 Makefile# 只需要改输出名和源文件名 OUTPUT_NAME libsns_imx662 # 源文件列表 SRCS : imx662_cmos.c imx662_sensor_ctl.c # 其余路径、编译选项完全复用第7步应用层对接 — 让系统加载新驱动你的应用层代码不在 sensor 驱动目录里// 旧 extern ot_isp_sns_obj g_sns_modules989_obj; ot_mpi_isp_sensor_reg_callback(vi_pipe, g_sns_modules989_obj); // 新 extern ot_isp_sns_obj g_sns_imx662_obj; ot_mpi_isp_sensor_reg_callback(vi_pipe, g_sns_imx662_obj);实战排坑清单按时间顺序你一定会遇到这些问题Day 1编译通过但黑屏 ├─ 检查 I2C 地址是否正确用 i2cdetect 扫一下 ├─ 检查 MIPI 配置lane 数、速率、continuous clock ├─ 检查 sensor 是否正确初始化读 0x0000/0x0001 确认通信 └─ 检查 VMAX/位深是否匹配 ISP 配置 Day 2有图像但严重偏色 ├─ Black Level 值不对 → 先确认 sensor 输出的 OB 值 ├─ CFA Pattern 配反了 → RGGB vs GRBG vs GBRG vs BGGR └─ AWB 参数全错 → 先关掉 AWB用固定增益看原始色彩 Day 3图像亮度不对/闪烁 ├─ Again 映射公式错误 → 打印 AE 算出的 again 和实际写入的寄存器值 ├─ init_exposure 不合理 → 手动设一个已知正确的值 └─ offset 不对 → 导致曝光时间跳变 Day 4帧率不对 ├─ VMAX 值不匹配 → 检查模式表和 sensor 实际输出 ├─ MIPI 速率不匹配 → sensor 和 SOC 的 MIPI 配置要一致 └─ pixel clock 计算错误 → 影响 hmax_times Day 5进入 IQ 调优阶段 ├─ 噪声水平 → 校准 noise_calibration 表 ├─ 色彩准确度 → 校准 CCM ├─ 动态范围 → 调 DRC/Gamma └─ 细节表现 → 调 Sharpen/Demosaic总结信息获取渠道你需要的信息去哪里找寄存器初始化序列Sony FAE 提供最关键增益转换公式Sony Gain Conversion NoteVMAX / 帧长限制Sensor Datasheet Readout Mode曝光时间范围Sensor Datasheet Integration TimeBlack Level 值Sensor Datasheet Optical BlackCFA PatternSensor Datasheet Color Filter ArrayI2C 地址Sensor Datasheet Serial InterfaceMIPI 配置Sensor Datasheet MIPI CSI-2AWB 校准参数自行校准标准光源箱ISP 调优参数自行调优IQ 工具一个 sensor 驱动工程师的核心能力 从 FAE 和手册获取正确信息 → 准确填入框架 → 快速排错验证。二MIPI 配置MIPI 配置不在 sensor 驱动目录里而是在应用层的sample_comm_vi.c中。这是很多人找不到的地方。为什么不在 sensor 驱动里sensor 驱动modules989/负责→ 通过 I2C 控制 sensor 内部寄存器→ 提供回调给 ISP/AE/AWBMIPI 配置sample_comm_vi.c负责→ 配置 SOC 这端的 MIPI CSI-2 接收器→ 不是配置 sensor 的 MIPI 发送器它们是两端sensor 端MIPI 发送器由初始化序列里的寄存器配置已经写死在 sensor_ctl.cSOC 端MIPI 接收器由 sample_comm_vi.c 配置你项目里的 MIPI 配置在sample_comm_vi.c中IMX989 相关的 MIPI 配置涉及三个层次层次1ext_data_type — MIPI 扩展数据类型static ext_data_type_t g_mipi_ext_data_type_imx989_10bit_8m_nowdr_attr { .devno 0, // MIPI 设备号 .num MIPI_NUM, // 通道数 .ext_data_bit_width {10, 10, 10}, // 10bit RAW 数据 .ext_data_type {0x30, 0x30, 0x30} // MIPI CSI-2 Data Type };0x30是什么MIPI CSI-2 规范定义的 Data Type 编码0x2b RAW10 (10bit RAW)0x2c RAW12 (12bit RAW)0x2d RAW14 (14bit RAW)0x30 ???0x30 在标准 CSI-2 规范里其实是 User Defined Data Type 8但海思框架用 0x30 表示一种自定义打包格式可能是压缩 RAW 或嵌入式数据层次2combo_dev_attr — SOC 端 MIPI 接收器配置IMX989 没有单独的 combo_dev_attr它使用了default 分支第779行default:(td_void)memcpy_s(combo_attr, sizeof(combo_dev_attr_t),g_mipi_4lane_chn0_sensor_os08a20_12bit_8m_nowdr_attr, sizeof(combo_dev_attr_t));也就是说 IMX989复用了 OS08A20 的 MIPI combo 配置static combo_dev_attr_t g_mipi_4lane_chn0_sensor_os08a20_12bit_8m_nowdr_attr { .devno 0, // MIPI device 0 .input_mode INPUT_MODE_MIPI, // MIPI 输入模式 .data_rate MIPI_DATA_RATE_X1, // 单速率非双速率 .img_rect {0, 0, WIDTH_3840, HEIGHT_2160}, // 图像窗口 .mipi_attr { DATA_TYPE_RAW_12BIT, // 这里写的是12bit OT_MIPI_WDR_MODE_NONE, // 无WDR {0, 1, 2, 3, -1, -1, -1, -1} // 4 lane: Lane0~3 } };层次3vi_dev_attr — SOC 端 VI 接口配置static ot_vi_dev_attr g_mipi_raw_dev_attr { .intf_mode OT_VI_INTF_MODE_MIPI, // MIPI 接口模式 .work_mode OT_VI_WORK_MODE_MULTIPLEX_1, // 1路复用 .component_mask {0xfff00000, 0x00000000}, // 12bit掩码高12位有效 .scan_mode OT_VI_SCAN_PROGRESSIVE, // 逐行扫描 .data_type OT_VI_DATA_TYPE_RAW, // RAW数据输入 .in_size {WIDTH_3840, HEIGHT_2160}, // 输入尺寸 .data_rate OT_DATA_RATE_X1, // 数据速率 };MIPI 配置的关键参数解读换 sensor 时这些参数必须匹配新 sensor┌──────────────────────────────────────────────────────────────────┐ │ combo_dev_attr_t 各字段含义 │ │ │ │ input_mode │ │ INPUT_MODE_MIPI ← 正常 MIPI CSI-2 │ │ INPUT_MODE_SDI ← SDIBroadcast │ │ INPUT_MODE_BT656 ← 并行接口 │ │ │ │ data_rate │ │ MIPI_DATA_RATE_X1 ← 单速率每lane 1像素周期传1样本 │ │ MIPI_DATA_RATE_X2 ← 双速率DDR每lane传2样本/周期 │ │ │ │ img_rect │ │ {x, y, width, height} ← SOC 端裁剪窗口 │ │ 必须和 sensor 输出尺寸一致 │ │ │ │ mipi_attr │ │ DATA_TYPE_RAW_10BIT / 12BIT / 14BIT ← 必须匹配 sensor 输出 │ │ OT_MIPI_WDR_MODE_NONE ← 无 WDR │ │ OT_MIPI_WDR_MODE_VC ← VCVirtual ChannelWDR │ │ lane_id[] ← MIPI lane 映射表 │ │ {0,1,2,3,-1,-1,-1,-1} 使用 4 laneLane编号 0~3 │ │ {0,1,-1,-1,-1,-1,-1,-1} 使用 2 lane │ │ -1 该位置不使用 │ └─────────────────────────────────────────换 sensor 时 MIPI 配置怎么改典型步骤 1. 查新 sensor 手册的 MIPI CSI-2 章节 → lane 数2-lane / 4-lane → 输出位深10bit / 12bit / 14bit → 数据速率模式X1 还是 X2 → continuous clock 是否开启 2. 在 sample_comm_vi.c 里新增一个 combo_dev_attr_t static combo_dev_attr_t g_mipi_2lane_chn0_sensor_imx662_10bit_1080p_nowdr_attr { .devno 0, .input_mode INPUT_MODE_MIPI, .data_rate MIPI_DATA_RATE_X1, .img_rect {0, 0, WIDTH_1920, HEIGHT_1080}, // 改分辨率 .mipi_attr { DATA_TYPE_RAW_10BIT, // 改位深 OT_MIPI_WDR_MODE_NONE, {0, 1, -1, -1, -1, -1, -1, -1} // 改lane数2 lane } }; 3. 在 switch(sns_type) 里加 case 分支 4. ext_data_type 也要加对应的 5. vi_dev_attr 可能需要改 in_size 和 component_mask配错 MIPI 的典型症状症状 可能原因───────────────────────────────────────────────────────黑屏/无数据 lane 映射错误或位深不匹配画面花屏/条纹 MIPI 速率不匹配图像偏移/裁切不对 img_rect 设置错误偶发性丢帧 data_rate 模式错误MIPI LP/HP 错误日志 sensor 端的 MIPI 连续时钟配置与 SOC 端不一致MIPI 配置是最容易踩坑的地方——sensor 端和 SOC 端必须完全对称任何一点不匹配都会导致黑屏。而且黑屏时几乎没有任何调试信息只能通过 SOC 的 MIPI 错误计数器日志来排查。