IMX378 PDAF 寄存器配置与驱动调试指南:屏蔽像素、窗口、校准全解析
简介面向相机系统软件工程师的IMX378相位检测自动对焦PDAF软件参考手册v1.0.0由Sony Semiconductor Solutions发布专门解释相位检测功能的输入/输出接口与控制方法。该传感器在有效像素区域内以99%块占用率嵌入右半屏蔽与左半屏蔽像素每16×32像素块含8个右屏蔽像素和8个左屏蔽像素从而实现高精度对焦。包体为单个PDF文档约1.67MB共1份文件体积小巧便于查阅。内容覆盖屏蔽像素区域对齐、相位检测操作概述、控制寄存器与AF窗口配置、相位差数据输出MIPI/I2C以及模块校准设置等核心章节并保留自2015年12月至2017年2月的修订历史帮助读者追踪寄存器地址与灵活窗口模式等更新。手册适合开发IMX378相机模组或相关嵌入式视觉产品的软硬件人员使用已有1586人学习下载可作为实现快速、精确自动对焦方案的直接参考资料。1. 为什么 IMX378 的 PDAF 手册值得逐行读IMX378 这颗 CMOS 图像传感器在手机后摄和车载相机里出镜率都不低但真正把相位检测自动对焦PDAF调明白的人往往都卡在同一处它不是拨一个开关就能搞定的功能屏蔽像素布局、AF 窗口坐标换算、MIPI/I2C 两条数据通路和模块校准这四件事必须同时理顺。这份原厂发布的 v1.0.0 版 Software Reference Manual for PDAF就是围绕 IMX378 讲清楚相位差数据怎么控、怎么读、怎么校准的完整说明。适合正在写 PDAF 驱动的工程师、做 ISP 算法的同学以及负责模组产线校准的同事。别嫌它 2017 年就定稿同代传感器的 PDAF 寄存器框架到现在还在沿用把这份 PDF 下载下来对照着调参比四处搜碎片化的帖子靠谱得多。2. 屏蔽像素与 PDAF 原理: 16x32 块里的 88 结构怎么决定对焦快慢2.1 左右屏蔽像素布局: 半金属遮挡为什么能测相位先理解硬件基础。IMX378 在有效像素区域内部嵌入了一批屏蔽像素每个像素大约有一半面积被金属遮挡。按遮挡方向分成两类右半被遮挡的叫右屏蔽像素左半被遮挡的叫左屏蔽像素。手册给出的排列规则是每个 16 像素 × 32 像素的块内离散嵌入 8 个右屏蔽像素和 8 个左屏蔽像素屏蔽区在块内的占用率约 99%。这个 99% 的块占用率直观理解是 16×32 的块结构几乎铺满整个有效成像区而不是只在画面中央埋几个点。这样做的意义在于无论对焦框落在画面哪个位置都能就近找到可用的屏蔽像素对参与相位统计如果只在中心布局边缘对焦就会变成盲区。这种半边遮光的设计原理不复杂左屏蔽像素只让左侧光线进入右屏蔽像素只让右侧光线进入左右两组像素对同一个物点的响应存在一个固定的视角差。当镜头离焦时这个视角差会换算成像素域上的错位量也就是相位差。单位是像素方向由符号表达。工程上的第一个隐藏工作是屏蔽像素分布在有效像素区内部普通像素做颜色插值和 shading 校正时必须知道它们的确切位置并把它们排除在插值邻域之外否则图像上会留下一条条固定的亮线或暗点。有些平台把这步合到 PDAF 像素校正里做但前提是驱动能准确上报屏蔽像素坐标表。2.2 相位差与置信度: 算法侧必须成对消费的数据传感器内部会针对每个指定的 AF 区域做统计处理输出两个值Phase Difference相位差单位像素和 Confidence Level置信度无量纲。相位差代表当前焦点偏离合焦位置的方向和程度。数值的方向性体现在符号上正负号决定了镜头该往近推还是往远拉。置信度则是这组相位差数据可信度的参考值手册里的定义很直白它与 AF 窗口内易于检测相位差的竖直边缘数量成正比。这个设计对算法非常有价值。如果你把 AF 窗口对准一面白墙或者一片天空相位差数值大概率会来回跳但置信度会很低。算法侧的常见做法是只把置信度超过阈值的窗口结果喂给对焦控制否则回退到 CDAF反差对焦或者保持当前位置不动作。我在驱动里会把两个值一起读出来打日志排查对焦抖动时只盯相位差曲线往往看不出名堂把置信度叠上去立刻就能区分数据不可信和马达过冲两种完全不同的故障。注意手册里所有相位差和置信度都是每个 AF 窗口一组。窗口设得越细数据量越大读取策略要在驱动设计阶段就想清楚不能拿到数据再规划。2.3 支持的操作模式: Normal Full / HDR / V2 Binning 全开手册 Table 2-1 明确列出了相位检测在 IMX378 所有主要工作模式下都受支持这张表对方案选型影响很大。传感器工作模式相位检测支持Normal Full支持Normal V2 Binning支持HDR Full支持HDR V2 Binning支持这意味着做产品定义时不需要为了保留 PDAF 而牺牲 HDR 或者 binning三者的开关是独立的。但支持不等于性能完全一致。我实际调试的经验是binning 模式下的相位差分辨率会受像素合并影响对焦精度和置信度分布与 Full 模式有明显差异。建议先用 Normal Full 完成 SPC 和 DCC 标定再去验证 binning 和 HDR 下的行为不要直接拿 binning 模式做全部标定否则换模式后对焦手感会变。3. 控制寄存器与序列: 三个关键地址和一段可抄的启动代码3.1 0x38a3 AREA_MODE 与 0x3e37 PDAF_CTRL1相位检测的寄存器数量不多核心入口就三个。手册 Table 3-1 把它们列得很清楚这里按驱动视角整理成一张速查表。I2C 地址位名称功能描述0x38a3[1:0]AREA_MODE0: 固定区域 16x121: 固定区域 8x62: 自由区域切换灵活窗口3: 保留0x3e37[0]PDAF_CTRL10: 相位差数据不从 MIPI 输出1: 从 MIPI 输出0x7c08 ~ 0x7f07[7:0]Phase Difference DATAI2C 方式读取的相位差数据区只读一个容易被忽略的细节是 PDAF_CTRL1 只控制 MIPI 输出通路。调试阶段如果你不想解析 MIPI 侧的复杂打包格式完全可以不置这个位直接走 I2C 数据区读相位差和置信度。反过来如果产品要用 MIPI 数据喂实时对焦算法就必须在出流之前把这一位置 1否则帧结构正常、图像正常唯独 PDAF 数据通道是空的。3.2 相位差数据区: 0x7c08 起 768 字节的只读区域0x7c08 到 0x7f07 是一整块只读数据区按 AF 窗口的划分顺序排列相位差和置信度。16x12 固定窗口模式正好是 192 个窗口这块区域的总长度 768 字节按每个窗口 4 字节对齐时刚好铺满8x6 模式只有 48 个窗口每个窗口对应的字节数就要按手册 Table 6-1 重新确认不能照搬。读这块区域我有两个固定习惯。第一用 burst 方式整块读不要在窗口边界处拆成单字节散读否则可能读到跨帧拼接的数据。第二调试阶段先把原始字节完整打印出来确认数据区数值确实在随镜头位置变化而变化再谈解析和算法规避。寄存器配置错了数据区往往给出的是固定值或者明显乱跳的值这一步能帮你把问题边界划清楚。配合 i2c-tools 可以快速做一次存活检查# 快速回读 PDAF 关键寄存器, -y 跳过交互确认 i2cget -y 1 0x20 0x3e37 # 确认 PDAF_CTRL1 是否真的写进去了 i2cget -y 1 0x20 0x38a3 # 确认 AREA_MODE 当前值 i2cget -y 1 0x20 0x7c08 # 读第一个窗口的相位差低字节这三个命令在模组刚点亮时跑一遍能排除掉一大类寄存器写失败的底层问题。0x20 是示例地址实际以模组规格书为准。3.3 控制序列: SW standby 里写Streaming 后只读数据手册第 4 章的相位检测控制序列非常简洁SW standby → PDAF_CTRL11 → Streaming → PD OUT ON。翻译成驱动代码就是下面这段。// IMX378 PDAF 开启序列, I2C 地址按实际模组总线调整 void pdaf_power_on(uint8_t i2c_addr) { // 1. 进入软 standby, 常见挂在 0x0100 的 bit0 sensor_write(i2c_addr, 0x0100, 0x00); // 2. 选择 AF 区域模式, 0x02 表示自由区域(灵活窗口) sensor_write(i2c_addr, 0x38a3, 0x02); // 3. 打开 MIPI 相位差输出 sensor_write(i2c_addr, 0x3e37, 0x01); // 4. 退出 standby, 开始出流 sensor_write(i2c_addr, 0x0100, 0x01); // 出流后 MIPI 帧内携带相位差数据, 同时 0x7c08 起也可读 }这个顺序不能乱。如果先出流再写 0x3e37部分批次的 sensor 不会认这次写入导致 MIPI 侧一直拿不到相位差数据而 I2C 侧却正常。排查时先回读 0x3e37 确认 bit0 是否真的为 1再往上层找问题。0x0100 软 standby 是传感器通用惯例如果某个模组行为不一致优先查模组的上电时序说明不要一上来就怀疑这段逻辑。4. AF 窗口配置: 固定窗口与灵活窗口的寄存器计算和落地写法4.1 固定窗口: 16x12 / 8x6 的偏移、尺寸与 13 位拆写固定窗口模式下你只需要设置第一个格子左上角的 X/Y 偏移以及单个格子的宽高。格子总数由 AREA_MODE 决定0 对应 16x121 对应 8x6。偏移和尺寸都是 13 位无符号数物理上拆成两个寄存器高 5 位存在高位地址低 8 位存在低位地址。含义高 5 位地址低 8 位地址起始 X 坐标0x38a40x38a5起始 Y 坐标0x38a60x38a7单格宽度0x38a80x38a9单格高度0x38aa0x38ab// 固定窗口: 第一个格子在 (220,240), 单格 1000x600 pdaf_write13(0x38a4, 0x38a5, 220); // PD_AREA_X_OFFSET pdaf_write13(0x38a6, 0x38a7, 240); // PD_AREA_Y_OFFSET pdaf_write13(0x38a8, 0x38a9, 1000); // PD_AREA_WIDTH pdaf_write13(0x38aa, 0x38ab, 600); // PD_AREA_HEIGHT // 13 位寄存器拆分写入, 先高后低 void pdaf_write13(uint8_t addr_hi, uint8_t addr_lo, uint16_t val) { sensor_write(addr_hi, (val 8) 0x1F); // 高 5 位 sensor_write(addr_lo, val 0xFF); // 低 8 位 }这里有三件事必须注意。第一坐标和尺寸的单位都是输出图像坐标也就是经过 crop、binning、scaling、mirror、flip 之后的坐标不是 sensor raw 坐标。手册里专门强调了这一点实际移植时最容易错的就是拿 raw 坐标直接填。第二如果窗口整体超出最终图像边界越界的格子会输出 invalid code并且不参与相位检测这不是 bug是手册规定的行为。第三我习惯先写高 5 位再写低 8 位虽然手册没有强约束写顺序但能避免中间态落在错误窗口里成本极低。4.2 灵活窗口: 最多 8 个可重叠区域与 0x1FFF 模式标志灵活窗口模式的价值在于可以同时关注多个区域比如人脸框加画面中心区最多 8 个允许互相重叠。进入这个模式有一个非常容易漏的条件必须把 0x38a8 到 0x38ab 这四个宽高寄存器的 13 位值全部写成 0x1FFF作为自由区域的标志。只配置 AREA_EN 是不够的我第一次移植就栽在这。以窗口 0 和窗口 1 为例坐标寄存器的映射关系是每组连续 4 个地址起始 X、起始 Y、结束 X、结束 Y每个坐标同样是 13 位拆两个寄存器。含义窗口 0 高 5 位地址窗口 0 低 8 位地址起始 X0x38b40x38b5起始 Y0x38b60x38b7结束 X0x38b80x38b9结束 Y0x38ba0x38bb// 进入灵活窗口模式: 宽高寄存器写固定值 0x1FFF pdaf_write13(0x38a8, 0x38a9, 0x1FFF); // 模式标志 pdaf_write13(0x38aa, 0x38ab, 0x1FFF); // 模式标志 // 窗口 0: (400,300) 到 (1000,700) pdaf_write13(0x38b4, 0x38b5, 400); pdaf_write13(0x38b6, 0x38b7, 300); pdaf_write13(0x38b8, 0x38b9, 1000); pdaf_write13(0x38ba, 0x38bb, 700); sensor_write(0x38ac, 0x01); // AREA_EN_0 1 // 窗口 1: (600,400) 到 (800,600), 与窗口 0 刻意重叠 pdaf_write13(0x38bc, 0x38bd, 600); pdaf_write13(0x38be, 0x38bf, 400); pdaf_write13(0x38c0, 0x38c1, 800); pdaf_write13(0x38c2, 0x38c3, 600); sensor_write(0x38ad, 0x01); // AREA_EN_1 1窗口 2 到窗口 7 的寄存器地址按同样的四对一组往后排手册里列到了 0x38d3再往后是窗口 4 的起始地址 0x38d4规律非常明显。重叠窗口的像素会被多个窗口重复统计这是允许的但会把置信度分布拉平做阈值判断时心里要有数。手册没有明确结束点是否包含在窗口内这种边界语义我建议拿到 sensor 后用两个已知尺寸的窗口对着 MIPI 输出做一次校准确认开闭区间之后再写进代码注释。4.3 窗口生效时序和 mirror / flip 的组合影响手册 5.3 节专门讨论了 AF 窗口的反射时序。窗口配置不是写完立刻生效而是按帧节奏被 sensor 拾取。常见做法是出流前一次性配置完运行中不频繁改写。如果产品需要动态追踪对焦框那就要算好帧边界避免在窗口更新瞬间去读相位差数据否则读到的可能是新旧窗口混合的结果。另外模组为了结构排布经常做水平翻转。手册 6.1 节明确提示水平反转后相位差数据的符号会反转原因不复杂左右屏蔽像素的角色互换了。处理方法是保持寄存器配置不变在算法侧根据 mirror 状态对读回来的相位差统一取反。我在项目里踩过一次症状是对焦马达永远朝远离合焦点的方向走每次开机都是先拉远再拉近最后定位到就是符号问题。5. 相位差数据输出、校准与避坑: 五个值得写进文档的坑5.1 MIPI 与 I2C 两条通路怎么分工相位差数据可以从 MIPI 随帧输出也可以从 I2C 直接读两条通路互不冲突但使用场景完全不同。MIPI 通路在 0x3e37 置 1 后生效数据随图像帧一起带出来适合实时对焦控制数据量对后端 ISP 和算法透明。I2C 通路则是 0x7c08 起的只读区适合调试、产测和低功耗场景。产线校准工具通常纯 I2C 读取因为不起 MIPI 就能快速验证 SPC 参数是否烧录成功省掉一套 mipi 解析逻辑。我建议开发阶段两条通路都开着MIPI 数据喂算法I2C 数据打日志两边数值对得上说明整条链路健康对不上就去查帧时序和寄存器配置而不是先怀疑算法。5.2 SPC 与 DCC: 校准系数不是玄学是必须做的手册第 7 章用一整章讲模块校准核心是两个系数。SPC屏蔽像素校正用来校正屏蔽像素本身的响应差异不校正的话画面上会出现固定的颗粒或条纹。DCC散焦转换系数单位是 DAC/pixel作用是把像素域的相位差换算成镜头驱动器的 DAC 步数对焦控制靠它算出马达要推多远。校准系数要固化在模组的 EEPROM 或 OTP 里系统上电后加载到 sensor 或 ISP。手册 7.4 节给了 SPC 参数的设定时机实际操作中我遇到的翻车场景是产线换了镜头批次但没有重新标定 DCC结果 PDAF 每次都对焦偏近或偏远画面永远差一口气。标定件的更换记录和系数版本号建议在驱动日志里打出来方便回查。5.3 常见问题与排查: 现象、原因、解决问题 1边缘窗口的相位差全是无效值现象固定窗口模式下画面边界附近的格子读回来的数据是 invalid code置信度也是 0。原因AF 窗口超出了最终图像边界手册规定越界格子输出无效码并且不参与检测。解决写寄存器前先把窗口坐标裁剪到图像尺寸内最好在驱动里统一做一次 clamp不要留给上层去猜。问题 2开 mirror 后马达一直往反方向推现象图像正常对焦框位置也正常但镜头总是朝远离合焦的方向运动。原因水平翻转让左右屏蔽像素互换相位差符号反了手册 6.1 节专门讲过这一点。解决根据 mirror 状态对相位差做符号翻转或者在校准阶段把符号方向写进系数表二选一并在移植文档里标注清楚避免后面接手的人重复踩。问题 3配置了灵活窗口出来的还是固定窗口数据现象AREA_EN_0 到 AREA_EN_7 都置 1 了MIPI/I2C 读回来的格子数还是 16x12 或 8x6。原因灵活模式有硬门槛0x38a8~0x38ab 的宽高寄存器必须整体写 0x1FFF很多人漏了这一步。解决回读这四个寄存器确认高 5 位都是 0x1F、低 8 位都是 0xFF再检查 AREA_EN。问题 4窗口位置偏了一半或者整体错位现象对焦框实际位置和预期位置差一格或者差几倍看起来像坐标缩放错误。原因13 位坐标拆两个寄存器时高 5 位和低 8 位写反或者只写了低 8 位高位地址保持默认值。解决统一用拆分函数写入写完立刻回读拼回 13 位原始值与预期值比对这一步在产线测试脚本里也应该保留。问题 5出流中改窗口相位差数据偶发错乱现象运行中动态更新 AF 窗口后相位差数据时好时坏偶尔整组窗口的数据顺序错位。原因窗口反射时序和帧同步问题Streaming 状态下写入可能落在帧中间或者不被本次帧拾取。解决把窗口配置全部放在 SW standby 阶段完成运行时只读数据。确需动态切换就在帧消隐期写入并等两帧之后再读取写入窗口用手册 5.3 的时序参数设计。这五条是我在 IMX378 系列模组调试中反复遇到的高频问题前三条几乎是每套新模组都会碰到的。把这五条写进你的移植文档至少能让后面接手的人省下一周的黑盒调试时间。这份手册的 PDF 里带有完整的窗口坐标示例图调参时对照着看比自己推导快很多。6. 验证 PDAF 数据的最后一公里: I2C 回读与 MIPI 交叉验证新模组点亮的当天我最先做的事不是调对焦而是验证相位差数据链路。做法是同时打开 MIPI 输出和 I2C 数据区借助帧同步信号抓一帧 MIPI 里的相位差再通过 I2C 读同一帧的 0x7c08 数据区两边对一下数值。这个交叉验证能一次性暴露寄存器配置错误、MIPI 打包偏差、解析字节序三类问题比单独看一路数据可靠得多。# i2c_verify_pdaf.py: 16x12 固定窗口模式, 读 0x7c08 起 768 字节 import smbus2 bus smbus2.SMBus(1) SENSOR_ADDR 0x20 # 按模组实际 I2C 地址修改 # 768 字节 192 窗口 × 4 字节(相位差 2B 置信度 2B) data bus.read_i2c_block_data(SENSOR_ADDR, 0x7c08, 192 * 4) for idx in range(192): off idx * 4 phase_raw (data[off 1] 8) | data[off] conf_raw (data[off 3] 8) | data[off 2] # 相位差按有符号数处理, 高位为 1 表示负方向 phase phase_raw - 0x10000 if phase_raw 0x8000 else phase_raw if idx 8: # 先打印前 8 个窗口, 完整数据落文件 print(fwin {idx:02d}: phase{phase:5d} conf{conf_raw})解析格式上有两点要交代清楚。第一4 字节一组相位差 2 字节加置信度 2 字节的假设只对 16x12 固定窗口模式成立因为 768 字节正好等于 192 乘以 4换到 8x6 模式每窗口的实际字节数要以手册 Table 6-1 为准。第二相位差是否有符号、字节序是大端还是小端不同固件版本可能有差异脚本跑出来的值如果和 MIPI 解析值系统性差一个符号优先怀疑这里。脚本跑通之后我会把 I2C 回读值和 MIPI 解析值放在同一张表里逐窗口对比偏差超过 1 个像素就回去查帧时序而不是先改算法。从那以后我每点亮一颗新模组都会强制走一遍回读拼接 → MIPI 对比 → mirror 符号检查这套流程。它帮我抓出过三次产线换料后坐标寄存器地址漂移的问题也让我养成了先把数据链路验证干净再动对焦算法的习惯。希望帮到你。本文还有配套的精品资源点击获取