
开篇先交代一个背景去年我接手了一个基于展锐平台的双摄项目主摄GC8034副摄GC02M1主打AIoT场景下的深度计算与人脸识别客户要求不高常规规格里也就一句“双摄需要帧同步”。但就是这句不起眼的话让我在产线、实验室和驱动代码之间来回折腾了两周。这篇文章就是把我踩过的坑、排查过的寄存器、抓过的波形原原本本整理出来希望对正在做展锐双摄帧同步的工程师有点帮助。帧同步这个事说大不大说小不小。做得好了你的深度图和运动物体边缘干干净净做得不好轻则虚化穿帮重则深度信息直接算错。而且帧同步的问题往往不是单点的它横跨sensor驱动、展锐的ISP pipeline、甚至PCB走线。所以这篇文章从配置参数一路讲到调试手段顺便把几个最容易翻车的隐藏陷阱也一起挑明。1. 从“拍糊了”说起双摄帧同步到底解决什么问题1.1 一个典型的运动场景事故现场客户最初反馈的问题非常朴素用我们样机对着跑步的人拍一张照片预览状态下看不出什么问题但一拍完图人身体边缘就出现明显的“鬼影”尤其是手和脚的位置拖出一条淡淡的残影。接着测试深度图发现运动物体所在区域的深度值一团糟虚化效果里人像边缘像被狗啃过。这个问题如果只看照片第一反应往往是长曝光手抖、OIS没校好、或者EIS抽风。但我们把主摄和副摄的图像分别从sensor直接拉出来检查发现两帧画面中人物的手部位置差异非常明显——也就是说两张图根本不是同一时刻拍的。主摄已经曝光完了副摄可能才刚开始曝光时间差有时能到几毫秒。这就是典型的帧同步失效。1.2 帧同步不达标会引发哪些怪现象帧同步的本质是让两个sensor在同一时刻完成曝光。因为双摄系统拿到两路图像后要通过视差计算深度或者做融合如果两路图像在时间上不对齐物体一动就会在图像上产生位置偏差。偏差大到一定程度深度算法就崩了。实际项目中帧同步不达标的表现可以归纳成几类运动物体边缘出现“色边”或“残影”这是最好观察的拍个挥动的手就行。深度图出现大量稀疏噪点算法误匹配尤其在物体的运动边界上。3D人脸解锁/支付时偶尔失败在弱光下曝光时间变长两路曝光窗偏移会更大。双摄虚化时背景与主体的边界发虚尤其是头发丝、边缘复杂的区域。连拍或录像时动态画面有明显跳跃感帧率不稳导致的。这几类问题很多厂商在项目初期往往不会当回事因为静态场景下用标版测试帧同步的bug根本看不出。但一到用户手上拍个孩子、拍个宠物问题全暴露了。1.3 展锐方案里帧同步的实现路线概览展锐平台的摄像头架构从老的SprdCamera到新的HALpipeline系统里其实已经预留了双摄同步的处理逻辑但默认情况下很多参数并不会给你调好。框架层面会提供“同步使能”的控制位但真正的同步是靠sensor本身的硬件同步模式加上驱动和HAL层配置配合完成的。我在这个项目里把整个链路拆成了三层来看物理层MCLK是否同源XSHUTDOWN或者GPIO触发信号是否到位VSYNC/HSYNC信号是否正常。驱动层sensor的master/slave模式配置、曝光寄存器与VTS寄存器是否被正确写入设备树节点是否让两个sensor都进入了同步工作模式。系统层展锐Camera HAL/Pipeline是否对两路stream做了启动同步和帧号对齐ISP侧有没有做frame id的匹配。三层只要有一层掉链子最后的效果就是“看起来什么都对但一拍动态就出事”。所以做展锐双摄帧同步不能只看某一个点而是要把整条链路都盯住。下面我按这条链路逐层往下拆。2. 展锐双摄同步机制拆解从Sensor寄存器到ISP时序2.1 三类同步手段主从模式、外部触发与FreeRun对齐先说结论展锐平台上真正稳定可用的双摄同步靠的是sensor层级的主从Master/Slave同步模式和外部触发ExternalTrigger模式。FreeRun软件对齐这种方案精度完全不够用。主从模式是CMOS sensor本身就支持的能力。以格科微的GC8034这类常见sensor为例主摄工作在Master模式自己产生帧同步信号同时把帧同步信息通过硬件引脚比如XVS、XSHUTDOWN、或者MIPI的同步包输出给副摄。副摄工作在Slave模式接收这个同步信号把自己的曝光窗口硬性对齐到主摄。这是最稳的方案也是展锐平台官方推荐的用法。外部触发模式则是用SoC的GPIO或者其他外部信号源主动给两个sensor同时发触发脉冲两个sensor都工作在trigger模式。这种方式在展锐平台上也能用但对GPIO的精度要求高触发信号的频率也需要软件精确维护调试成本会高一些。而FreeRun软件对齐就是让两个sensor各自独立自由运行靠软件读回两个sensor的帧起始时间戳然后通过调整VTS或者曝光延时把两路流的帧号尽量对齐。这个方案不是说不能用但前提是两路帧率必须完全一致而且对sensor的寄存器写入时序非常敏感一遇到AE自动曝光调节就会重新漂移。我初版图省事用过这个方案后面直接被实测数据打脸。2.2 MCLK、XSHUTDOWN、VSYNC信号链路到底发生了什么物理层看起来简单实际上坑最多。展锐双摄项目中主摄和副摄的MCLK一般有两种接法共用一路时钟或者各自接一个MCLK引脚。共用一路时钟在硬件上更常见因为展锐SoC的Camera MCLK引脚数量有限双摄项目通常会复用。共用MCLK带来的第一个问题就是相位偏斜。如果PCB走线长度差得比较大两个sensor收到的时钟沿会有ns级别的偏差。对于普通拍摄来说这个偏差无所谓但当你去做曝光窗口对齐时ns级别的时钟偏差经过PLL倍频后可能被放大到足以让曝光起始时刻出现几个line的偏差。这个后面调试章节我会再展开。XSHUTDOWN是很多sensor用来控制曝光和读出的引脚。在主从同步模式下这个引脚往往承担两个任务一是作为sensor的复位或shutdown控制二是作为同步触发的参考信号。有的硬件设计图里XSHUTDOWN被同时接到了两个sensor上看起来没什么问题但如果这两个sensor的时序要求不一样一个上电复位需要时间另一个不需要就可能导致同步窗口错开。我们项目里就曾经因为复位时序上的差异导致副摄一直收不到同步信号看了半天寄存器都正常最后是拿逻辑分析仪抓XSHUTDOWN波形才发现的。VSYNC信号更直接它代表了每一帧的开始。在主从模式下主摄的VSYNC输出要能正确传到副摄。有的平台会通过GPIO中断去捕获VSYNC的上升沿或者下降沿有的平台则直接把主摄的VSYNC路由到副摄的同步输入引脚。展锐平台多数会通过内部信号路由去处理不占用外部GPIO但前提是sensor的驱动配置里要把同步输入功能打开。2.3 展锐Camera HAL中帧同步相关标志位与数据流进入系统层展锐的Camera HAL在启动双摄流时会有一连串的初始化动作。我在代码里追踪过一条完整的双摄stream on流程大致是HAL层判断当前是dual camera场景查找sensor列表里是否有同步组合配置。底层库会对两个sensor做“同步上电”power on sequence主摄先上电副摄紧随其后。驱动层配置sensor的工作模式如果是主从同步会先让主摄按指定的分辨率和帧率start streaming然后下发副摄的trigger/slave配置。两路stream都起来后ISP或Symphony/Deep Learning具体模块名跟平台版本有关的pipeline会根据帧号和时间戳对图像做匹配。展锐的HAL层给上层暴露了“camera sync mode”的配置入口常见的模式值有模式含义适用场景SYNC_MODE_NONE不做同步单摄或无关紧要的辅助摄像头SYNC_MODE_MASTER主摄作为同步源输出信号主从双摄SYNC_MODE_SLAVE副摄接收同步信号主从双摄SYNC_MODE_EXTERNAL外部触发控制双摄特殊工业/视觉场景如果你在代码里看到两路流已经起来了但副摄的帧率和主摄对不上先别急着调sensor寄存器回头看一眼HAL里这两个模式标志位到底有没有配对设置。我们踩过一个坑就是两条stream的启动是通过不同代码分支走的主摄正确配置成了MASTER副摄却走了默认的SYNC_MODE_NONE结果副摄一直自由运行帧号自然对不齐。3. 工程配置实战DTS、驱动与Sensor寄存器配置要点3.1 设备树节点里的同步参数展锐平台的双摄配置很大一部分是从dts设备树开始的。以我调的这个项目为例两个sensor在dts里各有一个I2C节点节点里除了常见的reg、reset-gpios、pwdn-gpios、mclk频率外还有几个跟同步强相关的属性需要格外留意每个sensor节点的“sync mode”或类似字段标记该sensor是master还是slave。主摄节点的“sync-out”信号路由配置决定主摄的sync信号会通过哪个内部信号通道送出去。副摄节点的“sync-in”信号路由配置决定副摄从哪儿接收同步输入。VTS/VMAX和曝光行时间相关的字段这组参数直接决定了两路sensor的帧长是否一致。很多工程师拿到一个参考设计喜欢直接把整个dts段拷贝过来只改I2C地址和reset脚。这在单摄项目里基本没问题但在双摄同步项目里就会出事。副摄的同步输入如果没接到对的信号通道那寄存器配得再漂亮也白搭。我的建议是拿到板子先用adb读一下/sys/firmware/devicetree/或者cat /proc/device-tree/下的对应节点确认实际生效的配置跟dts源码一致。有几次我们改完dts编译烧录后发现没生效最后排查是bootloader里的dtb覆盖了这种问题最容易让人怀疑人生。3.2 Sensor驱动侧必须确认的几个寄存器配置驱动侧的寄存器配置才是真正决定同步精度的环节。无论你用的是格科微、思特威、OV还是三星的sensor寄存器手册里基本都会有一组和同步相关的寄存器。命名各有不同但作用大同小异我列几个常见的必须确认的点主从模式选择位通常在sensor的0x0100stream on附近的全局控制寄存器组里或者单独的模式配置寄存器里。主摄要设成Master副摄要设成Slave。如果设反了两个sensor会互相等信号直接不出图。帧同步延时Frame Sync Delay/Offset这个寄存器用来微调副摄相对于主摄的曝光起始偏移。驱动里一般会设一个默认值但这个值在不同分辨率、不同帧率下需要重新计算。VTSVertical Total Size寄存器两路sensor的VTS必须一致。VTS决定了每一帧的总行数如果两个sensor的VTS不一致帧率自然就不一致同步就无从谈起。展锐的AE算法在某些情况下会自动调整VTS这就要在ISP/3A的配置里把“固定帧长”或者“帧率锁定”打开。曝光时间寄存器副摄的曝光寄存器理论上应该由AE自己控制但如果想验证同步可以在test模式下给两路sensor写一个固定的曝光时间比如10ms然后看两路图像亮度是否一致。Grouped Parameter Hold相关寄存器这类寄存器用于保证同一帧内多个参数曝光、增益、VTS的原子性更新。在双摄同步场景下如果这个位没有正确设置可能出现主摄已经更新了新一帧参数副摄还在用上一帧参数导致两帧参数不同步。sensor手册里每家的叫法差很多没有统一的标准。比如OV喜欢叫“Grouped Parameter Hold”有的厂商叫“Auto Frame”有的叫“Skew Control”。拿到一款新sensor先花十分钟翻到寄存器表的“Frame Timing”章节把上面这几个点全部画出来比瞎试寄存器靠谱得多。3.3 双摄同时出流的最优上电时序配置上电时序这个东西文档里通常只有一句话“主摄先上电副摄后上电两者上电间隔建议在xxx ms以内。”但实际项目里上电时序直接影响同步的建立过程。我建议的做法是把上电流程拆分到这样的步骤先拉主摄的reset和pwdn让主摄进入正常供电模式。延时一个trick time一般5-10ms足够确保主摄内部PLL稳定。再拉副摄的reset和pwdn并确保副摄上电后也有一小段稳定时间。先start主摄的stream通过I2C确认主摄已经在出帧。然后再start副摄的stream并立即设置sync相关寄存器。这个顺序的意义在于如果副摄先于主摄进入stream状态它可能因为收不到同步信号而一直等有些sensor会进入挂死状态必须重新上电才能恢复。如果两个sensor同时上电主摄的PLL还没稳定副摄收到的第一个同步沿可能是毛刺可能导致第一帧曝光时间异常。这里还有一个容易被忽略的细节上电时序要在驱动代码里通过state machine控制而不是简单地在userspace调ioctl。展锐平台的sensor驱动一般都会有一个v4l2_subdev的power_on/power_off回调你要确认两路sensor的power_on回调执行顺序是可控的。不能依赖枚举顺序因为每次系统启动设备探测的顺序可能不同。4. 调试避坑实录帧不同步的定位全过程4.1 开局log里看到两个sensor的帧号对不上时间回到我们项目出问题的那个阶段。当时静态场景测试一切正常标版拍出来清晰锐利。但只要场景里加入运动物体深度图就开始出现大片噪声。我们第一步是抓log。展锐的Camera相关日志通过adb logcat抓HAL和Provider层的打印用串口调试助手抓内核驱动层的打印。这里提醒一下串口调试助手的缓存要开大一点否则高帧率下sensor的I2C读写日志很容易被冲掉。我们当时就吃过亏前期抓的日志里大量丢失关键打印后来把串口波特率调到921600、缓存扩大才勉强看到全貌。日志里最直接的线索是这句话大意是“frame id mismatch”主摄已经出到第120帧副摄还在第118帧。帧号差了两帧如果换算到时间轴上就差了大约66ms。这个误差大到一眼就知道问题不在AE而在同步链路。接下来我们做了几个快速验证用mdubus调试助手直接读副摄的帧计数寄存器确认副摄确实在持续出帧只是帧率和主摄对不上。用示波器抓主摄XVS引脚和副摄XVS引脚的波形看两个VSYNC脉冲的相位关系。用adb shell抓current state确认两条stream的status都是running。示波器结果出来的时候答案已经很明显了主摄的VSYNC间隔是固定的33.36ms副摄的VSYNC间隔是34.01ms两个sensor根本不在一个节奏上。主摄作为master已经输出信号但副摄明显没有以master的节奏运行。4.2 关键工具串口、adb logcat、mdubus、逻辑分析仪的组合用法我先把这套工具链怎么用捋一遍免得大家到时候手忙脚乱。串口调试助手抓内核的printk日志尤其适合排查sensor驱动里的I2C传输、上电时序、GPIO申请是否成功。串口日志的优势是实时性和底层性HAL层日志看不到的sensor寄存器写操作在串口日志里都能看到。adb logcat抓HAL、Provider、CameraService这些用户态模块的日志。适用于排查stream on的顺序、同步模式标志位、3A状态切换这类问题。mdubus调试助手展锐平台上的一个调试利器可以读写SoC内部和外部sensor的寄存器、内存甚至能直接给sensor发命令。我在调同步的时候经常用它去读两个sensor的曝光寄存器确认当前帧的曝光时间是否一致。逻辑分析仪抓GPIO/VSYNC/XSHUTDOWN这类数字信号的时序。逻辑分析仪比示波器更适合看多路信号的相对关系因为通道多可以同时抓主摄XVS、副摄XVS、XSHUTDOWN、MCLK四路信号直接看到相位差。示波器看MCLK的频率和信号质量以及模拟域的干扰。MCLK如果振铃严重sensor内部时钟受影响也会导致同步抖动。这套组合拳打下来基本上能把问题定位到物理层还是驱动层。4.3 三个真实case曝光跳变导致错帧、GPIO复用被吃掉、MCLK时钟相位抖动Case 1曝光跳变导致错帧这个case是我们在修复完帧号不一致之后遇到的。帧率对齐了但运动物体仍然有轻微鬼影。进一步排查发现AE在收敛过程中主摄和副摄的曝光时间差异会瞬间拉大。比如场景从暗处转到亮处AE要做一次大步长的曝光调整。主摄先收到新的曝光参数副摄由于3A统计的时序慢了一拍还沿用旧曝光。这就造成同一帧里主摄曝光10ms副摄曝光8ms虽然帧起始时间差很小但曝光中心Exposure Center已经错开了。这个问题的本质是帧起始对齐只保证了“vertical sync”对齐没有保证“曝光中心”对齐。如果两路sensor的曝光时间不同那么曝光窗口的中间位置就会出现偏移。解决方法是两路sensor通过I2C同时写入曝光寄存器或者利用grouped parameter hold机制让两路sensor在同一个帧边界上更新参数。展锐HAL层有对应的“ae sync”配置打开之后会尽量让两路的AE收敛节奏一致。Case 2GPIO复用被吃掉这个case最隐蔽。现象是副摄偶发丢帧而且丢帧频率不稳定有时几分钟一次有时半小时一次。用逻辑分析仪抓XSHUTDOWN信号发现信号每隔一段时间就会有一个异常毛刺像是被什么东西拉低了一下。追查了很久最后通过debugfs的gpio信息发现XSHUTDOWN所对应的GPIO被另一个外设驱动在初始化时申请走了。那两个驱动在硬件上明明用的是不同引脚但dts里copy paste时没注意io-reuse数组配置导致相同功能的GPIO被重复分配了。排查这个case的经验就是发现偶发丢帧且波形不干净的时候先查GPIO占用情况不要一上来就怀疑sensor硬件坏了。Case 3MCLK时钟相位抖动还有一个case是上量产前的老化测试里冒出来的前几个小时一切正常升温之后开始出现帧同步误差增大。示波器量MCLK波形发现在板温升高后副摄的MCLK幅值下降边沿斜率变缓对应的sensor内部时钟相位就出现了几十ns的抖动。这个问题的根源是PCB上MCLK走线过长且没有加匹配电阻温度升高后信号质量劣化。解决方式是硬件上调整匹配网络在靠近副摄sensor端加一个22欧姆的串联电阻并把上拉电阻的值微调。软件上作为临时规避我们把MCLK的驱动强度调大了一档问题也有明显改善。这个case给我的教训是帧同步不只是软件配置问题MCLK的信号完整性会直接影响sensor内部PLL的抖动进而影响曝光窗口的对齐精度。如果你的帧同步误差呈温度相关性优先怀疑时钟链路。5. 如何量化验证帧同步精度不靠“看起来还行”5.1 双LED相位测试法做双摄帧同步最怕的就是“看起来还行”。人的肉眼在预览小图上是看不出两帧时间差10ms的。所以必须有一套定量的验证方法我一贯推荐用双LED相位测试法。具体做法很简单准备两个高亮LED用一个小单片机STM32或普通的555电路都行驱动让两个LED以一定频率交替点亮。比如LED1亮5ms灭5msLED2亮5ms灭5ms两者相位相差180度。把两个LED放在双摄前方同一视野中同时拍照。取主摄和副摄同一帧的图像比较两个LED的亮度关系。如果帧同步精度高那么主摄和副摄在同一帧里看到的LED状态应该一致——要么都是LED1亮要么都是LED2亮。如果帧不同步就会出现同一帧里主摄看到LED1亮、副摄却看到LED2亮的情况。通过改变LED的闪烁频率你甚至能估算出帧间的时间差。这个方法做起来便宜、直观而且不需要复杂的图像分析软件产线上也可以快速验证。5.2 运动被摄物残影检测与VTS对齐读值双LED法之外更贴近真实场景的是运动被摄物残影检测。我习惯用一个电机驱动的旋转黑白扇叶扇叶旋转时可以产生稳定的运动速度然后用双摄拍一段视频逐帧分析扇叶边缘的模糊程度和位置偏移。这个测试方法需要有图像分析工具支持因为靠肉眼判断不够精确。我在项目里用Python脚本读取两路图像计算扇叶边缘的绝对位置差值。如果同一帧里两路图像中扇叶边缘的x坐标差值稳定在1个像素以内说明同步精度在这个运动速度下是OK的。还有一种更软件化的验证手段直接读sensor内部的寄存器。通过mdubus或者I2C工具分别读主摄和副摄的如下参数当前帧的VTS值当前帧的曝光时间寄存器值当前帧的frame start time如果sensor暴露这个寄存器的话计算两路sensor的曝光起始时间差公式大概是曝光起始时间差 (主摄VTS - 副摄VTS) × line_time (主摄曝光寄存器值 - 副摄曝光寄存器值) × line_time如果这个值在1个line time以内具体line time取决于sensor的分辨率一般4K以下在10us-20us左右那说明同步精度是达标的。如果超过几行甚至几十行就要回去检查同步配置或者信号质量了。5.3 量产一致性风险与老化考虑最后说一个很多人都会忽略的点帧同步精度会跟着温度、电压、批量元器件差异漂移。你在实验室调出来一个完美的配置不代表每台机器都能复现。量产上的建议有几点产测环节加入同步验证项。不一定要多复杂用双LED法跑一次快速判断几秒钟就能筛出明显不同步的设备。关注sensor的批次差异。同一型号的sensor不同批次之间的同步响应时间可能略有差异。如果你的设计方案把同步余量卡得很死换一个批次可能就翻车。所以调试时尽量留出20%以上的余量。做高温和低温老化测试时把帧同步误差作为一个监控指标记录下来。如果误差随温度漂移超过一个阈值说明硬件设计存在隐患不建议直接量产。6. 复盘与建议如果再做一个双摄项目我会怎么做写完这份避坑记录我自己也在反思如果重新开始做一个展锐双摄项目有哪些事是可以在立项阶段就提前规避掉的第一个改变硬件方案评审阶段就要把帧同步作为专项来评审而不是软件调试阶段再倒逼硬件。MCLK走线长度、XSHUTDOWN/VSYNC信号的走线保护、GPIO复用表这些都要在画板之前确认清楚。软件能解决的同步问题有限硬件信号质量不好后面调试成本高到难以估量。第二个改变放弃在开发板上自由发挥的幻想尽早使用展锐推荐的参考设计。参考设计的PCB走线、电阻电容匹配都是经过验证的。我们这次就是因为改了MCLK匹配电路的位置才引起温度相关的抖振问题。如果你不是专业的RF/模拟工程师参考设计的硬件部分尽量原封不动。第三个改变调试计划里给帧同步预留专项时间不要用“双摄能出图”来替代“双摄同步达标”。真正启动动态场景测试的时间最好在第一次能出图像之后就立刻安排。等静态标版测完再想起来测动态往往已经接近交付节点手忙脚乱。第四个改变工具链提前准备好。逻辑分析仪和示波器这类硬件工具不要等出了问题才去借。mdubus这类展锐调试工具也要提前确认你手上版本的命令集和连接方式。这个工具我用得很顺手但要花一天时间熟悉它。前期熟悉得越早后面定位问题越快。这个项目的最终结果是帧同步误差稳定控制在一个line time左右运动场景下的鬼影几乎不可见深度图干净了很多。回顾两周的折腾最值钱的经验就是一句话——帧同步是系统级问题不是单点配置问题。宁可多花时间在前期设计与验证方法上也不要迷信“先跑通再优化”因为跑通的只是表象同步精度这个东西不量化测试你永远不知道它到底行不行。