前阵子把一套基于MT6737平台的Camera方案重新做了一遍AF(自动对焦)链路优化,最耗时间的不是3A算法本身,反而在Lens HAL层。如果你也负责过这类入门级SoC平台的相机调试,应该深有体会:MT6737的Lens HAL层代码量不算大,但它夹在Sensor驱动、VCM马达和上层3A算法之间,任何一处参数没对齐,表现出来的症状都是一样的——“对焦没反应”或者“来回扫但是合不上焦”。这篇文章我打算把这层的职责边界、数据流、关键代码模块和调试手段全部摊开来讲,希望能给正在搞MT6737 Camera调试的同行省点时间。
文章里所有代码路径、模块名按方案商常用布局来描述,跟你自己手上的项目可能有些差异,但对应的逻辑关系是一致的。文末我会写几段移植第三方马达驱动时踩过的典型坑,这些属于纯经验范畴,常规文档里基本找不到。
1. 为什么单独拉出MT6737的Lens HAL来做分析
MT6737这颗平台在手机和IoT设备里都大量存在,主摄常见搭配是1300万像素,副摄500万像素,主打低成本。很多人拿到项目第一反应是去调ISP的降噪和色彩,对AF链路的重视程度往往不够,直到客户反馈“拍照模糊”“点击对焦没反应”才回头查。我这边负责过一个扫码设备项目,主摄用800万像素模组,前期预览和拍照都正常,但扫码偶尔拉风箱,最后定位出来的问题就在Lens HAL对马达运动状态的判断上。
所谓Lens HAL,全称是Lens Hardware Abstraction Layer。在MTK平台的标准HAL3架构里,Lens HAL不直接暴露给应用层,而是作为Camera HAL内部的一个子模块,专门负责和镜头执行机构相关的操作。它包括VCM(音圈马达)驱动IC的控制、DAC码与镜头位移的映射、EEPROM/OTP中AF校准数据的解析,以及马达运动状态机的维护。
为什么值得单独分析?因为MT6737上的AF方案以对比度AF(CAF,Contrast AF)为主,部分项目做了PDAF,但默认配置一般不开。对比度AF对Lens HAL的依赖程度很高:算法层需要边移动镜头边读取ISP统计值,统计值由图像内容决定,但“镜头当前位置在哪”“下一步往哪个方向扫”“扫到目标位置之后怎么稳定下来”这些动作全部由Lens HAL执行。如果这个层没吃透,3A算法再好也体现不出来。
另一个原因是,MT6737的Lens HAL典型目录里同时包含了对EEPROM的读写逻辑。很多项目用的是CameraCube或者一体式模组,EEPROM中的AF校准参数如果解析偏移了一个字节,轻则近焦/远焦端点不准,重则马达直接不动作。这类问题排查起来往往要从HAL层一路追到驱动IC的寄存器配置,涉及的知识点横跨I2C时序、VCM驱动IC选型和OTP数据格式。
所以这篇文章的重点不是把某个版本的源码抄一遍,而是帮你建立一套分析框架:知道Lens HAL在整个Camera架构里的位置,知道一条对焦请求从上层下发之后经过哪些环节,知道出问题时从哪个日志、哪个接口入手查。框架建立了,任何版本的代码你都能快速上手。
2. 架构边界:Lens HAL在Camera HAL3里管什么、不管什么
先把边界划清楚。HAL3模式下,应用层通过CameraService与Camera HAL交互,Camera HAL内部会拆分成多个独立模块。MTK平台的HAL3大体由CamHal、ISP子系统、Sensor子系统、3A子系统和Lens子系统组成。Lens HAL属于Camera HAL内部的一个服务模块,它主要做四件事。
第一件事是VCM马达控制。上层3A通过Lens HAL下发一个目标DAC码,Lens HAL负责把DAC码转成具体的I2C写入序列,驱动VCM运动到指定位置。同一颗马达在不同平台上的I2C配置可能不一样,有的需要16位寄存器寻址,有的8位寻址,有的写入后还要等待一定的时间让镜头稳定。Lens HAL封装了这些差异,对外只暴露位置设置接口。
第二件事是EEPROM/OTP数据处理。模组出厂时会写入AF校准参数,常见内容包括近焦端点DAC、远焦端点DAC、行程斜率、温漂补偿系数等。Lens HAL在启动时从EEPROM里读出这些数据,转换成内部的映射表,供AF算法使用。这里需要注意,EEPROM的读写通常是挂在I2C总线上的,Lens HAL做的是数据解析和缓存,真正底层的I2C读写由驱动IC完成。
第三件事是马达运动状态机维护。VCM不是你说动就瞬间到的,从当前DAC到目标DAC的运动过程需要经过加速、匀速、减速、稳定几个阶段。Lens HAL内部维护着一个状态机,常见状态包括IDLE、MOVING、SETTLED、ERROR。AF算法查询状态来决定是否读取当前帧的清晰度统计,如果状态不对就直接跳过,避免在镜头还在晃动时取到无效数据。
第四件事是镜头信息上报。包括当前马达位置、镜头是否支持PDAF、近焦/远焦对应的DAC范围、马达稳定时间等。这些信息会通过HAL的元数据通道上报给框架层,应用侧可以通过CameraCharacteristics查询到。
再说它不管什么。AE和AWB归属于3A的另外两个模块,Lens HAL只做AF相关的透镜控制。ISP侧的黑电平、镜头阴影校正、色彩校正等环节虽然也有“lens”字样,但那属于ISP调校范畴,不归Lens HAL管。同样,Sensor本身的曝光、增益、帧率配置由Sensor HAL处理,Lens HAL不会碰。
有个容易混淆的点:很多调试人员把“Lens HAL”和“镜头驱动IC驱动”当成一回事。实际上Lens HAL是用户态逻辑层,负责算法对接和数据解析;驱动IC驱动在内核态,负责具体I2C寄存器读写。排查问题时两者都要看,但代码定位思路完全不同。我建议你一开始就把这个分层记牢,后面所有分析都基于这个框架展开。
3. AF数据流拆解:从用户点选画面到马达执行的方向定位
一条AF请求的完整链路是这样的:应用层点击画面上的某个点,框架层把这个点坐标封装进CaptureRequest下发到Camera HAL。HAL内部的3A模块收到后,先算当前场景的亮度、对比度分布,再结合对焦区域决定下一次镜头运动目标。这时Lens HAL登场了。
对比度AF的核心逻辑是边扫描边评估。AF算法拿到ISP当前帧的清晰度统计值后,会和上一次位置的值做比较,判断清晰度是变好还是变坏,然后决定继续往同一个方向扫还是掉头回来。每次决策的结果是一个DAC码,Lens HAL把这个DAC码翻译成马达指令并执行。MT6737上这个流程一般是逐帧驱动的,也就是每一帧都会做一次决策和一次动作。
PDAF模式略有不同。相位对焦点会提供失焦方向和偏移量,相当于给了一个“路标”,无需慢慢扫就能跳到大致合焦位置。这个模式下Lens HAL只需要执行一次较大幅度的移动,然后再用对比度方式微调。MT6737有部分项目开了PDAF,但大部分低端模组的PDAF精度有限,最终还是混合AF兜底。
你可能会问:Lens HAL在这里面只负责“动”,算法怎么判断“值不值得动”?答案是,两侧是紧耦合的。Lens HAL在运动过程中会周期性地把当前位置、运动状态、DAC码反馈给AF算法。算法拿到这些反馈后,才能把清晰度统计值和镜头位置对应起来。如果Lens HAL反馈不及时,AF就会停在一帧画面里反复判定,出现“只听到对焦声、画面始终模糊”的典型故障。
从代码实现来看,Lens HAL对上层的接口通常是一组move/setPosition操作,对下层的接口则是一组I2C读写和寄存器配置。中间还有一条延迟链路:镜头从命令发出到真正停下来并稳定,需要几百毫秒甚至更久。这里有一个容易被忽视的参数——VCM稳定时间(settle time)。不同马达的稳定时间差异很大,便宜的VCM马达动辄50毫秒以上,高端一点的能做到10毫秒以内。Lens HAL在初始化时要从EEPROM或配置文件中读取这个参数,如果设得偏小,镜头还在回落阶段就被算法采了统计值,合焦精度会明显变差。
还有一个常被忽略的点是温度补偿。VCM马达的磁体在温度变化时磁场强度会漂移,同样一个DAC码在不同温度下的实际位移不一样。Lens HAL读到EEPROM里的温度系数后,会在每次计算目标DAC码时加上一个温度偏移量。这一步做不做,直接决定了设备在冬天和夏天对焦一致性。很多项目只做了常温验证,到了高低温测试就暴露问题,大概率是温度补偿路径没走通。
数据流这块总结下来就是:请求到达HAL后,3A算法决策,Lens HAL执行并反馈状态。重点盯住“决策—执行—反馈”这三个环节的同步关系,AF链路的大多数问题都出在这三步的衔接上。
4. 代码层面:Lens HAL的关键文件、核心接口与初始化时序
方案商的Lens HAL代码通常位于vendor目录下,从我的项目经验来看,涉及的代码路径包括Lens HAL主体、AF算法库、EEPROM读取模块和马达控制抽象层四部分。这四部分的分工很明确:Lens HAL主体提供对外接口;AF算法库根据清晰的统计值决定目标位置;EEPROM读取模块负责校准数据的获取和解析;马达控制抽象层把DAC码落到I2C命令上。
核心接口按照HAL3的编码习惯来命名,一般包括这几个职责。
initialize():完成Lens HAL初始化,打开I2C句柄,读取EEPROM里的AF校准数据,构造内部映射表。这个接口执行失败的话,后面所有AF操作都会空转。configure():根据当前的分辨率、帧率模式配置马达参数,例如扫描范围、移动步长、稳定时间窗口。setPosition(dac, mode):把目标DAC码下发到马达。mode可以区分绝对定位和相对移动,调试时相对移动方式更常用。getLensStatus():返回当前马达位置、运动状态、DAC码对应的物理位移。AF算法每一帧都会调用它来获取状态。processResult():在PDAF模式下,把Sensor输出的相位数据解析成失焦方向,下发到AF算法。getLensInfo():返回镜头信息,包括光圈值、焦距、对焦距离范围,以及是否支持PDAF。
初始化时序是很值得你重视的地方。启动时,Lens HAL先初始化I2C并读取EEPROM,然后构造马达映射表,最后进入IDLE状态等待AF算法请求。这个顺序不能乱。如果你的代码里读到EEPROM失败仍然继续初始化,往往会出现马达能转但位置错误的情况,表现就是对焦总是偏一个固定的方向,怎么调算法阈值都没用。
EEPROM数据的解析有几个关键点。首先确认全区映射还是分区映射,很多模组的EEPROM前几个字节是版本号和标志位,偏移量没对齐就会读到全零。其次是位宽和精度,AF校准数据一般以0.1微米或1/12微米为单位,不同模组厂的习惯可能不同,解析时要乘一个缩放系数。最后是端点值的含义,近焦端点DAC和远焦端点DAC分别对应镜头最近对焦距离和最远对焦距离时的DAC码,这两组值直接决定了扫描边界。
马达控制抽象层里,常见的VCM驱动IC有若干种,区别主要集中在I2C寻址方式、寄存器位宽和默认上电状态。有些驱动IC支持16位DAC,有些是10位封装在16位寄存器里。Lens HAL封装了一层统一的写码接口,把DAC码按各IC的位宽规则拆解后写入。调试时如果发现马达到不了目标位置,优先检查这一层的数据位拼装逻辑。
我建议你在阅读代码时,先按初始化时序走一遍,确认EEPROM读取成功;然后手动调用一次setPosition接口,观察马达是否运动到期望的DAC位置;最后再看AF算法与Lens HAL的同步逻辑。按照这个顺序排查,能过滤掉至少八成的低级问题。
5. 调试Lens HAL的常用手段、日志开关和问题定位表
调试这层最常用的手段是看日志。方案商的Lens HAL在打开调试宏的情况下会输出丰富的运行信息,包括马达当前位置、目标位置、运动状态、I2C读写返回值。日志开关一般是设置一个属性值,例如把persist.vendor.debug.lens.enable设为1,或者开启对应logcat tag的调试级别。不同版本的代码tag命名有差别,常见的关键字有LensHal、VCM、AF、Eeprom、Focus等。
抓日志的时候别只盯着logcat。I2C底层报错往往不会上抛到用户态,而是出现在内核日志里。同时开logcat和dmesg,用时间戳对齐,才能把完整链路串起来。看“VCM I2C error”这类关键字出现的频率和位置,如果错误发生在每次FOCUS请求期间,基本可以断定是驱动IC配置问题。
日志之外,最有效的调试手段是手动控制马达。我通常的做法是在Lens HAL代码里加一个调试命令接口,接收一个DAC码,强制马达移动到该位置。这样能快速验证三点:I2C通路是否正常、马达是否能响应指令、EEPROM校准数据是否被正确加载。三项都验证通过后,再回到AF算法去检查搜峰逻辑。
手动控制正常而自动对焦失败的情况,优先检查AF算法与Lens HAL之间的状态同步。典型症状是:手动设置目标位置没问题,但自动对焦时马达只动一步就停。这种通常意味着Lens HAL上报的运动状态一直是“运动中”或“错误”,AF算法不敢下发下一步动作,卡在了等状态变更上。
下面这张表是我整理的问题定位清单,基本覆盖了MT6737 Lens HAL层最常见的故障模式。
| 症状 | 可能原因 | 排查顺序 |
|---|---|---|
| 点击对焦完全没反应 | I2C通道异常、EEPROM读取失败导致马达初始化不完整 | 先查I2C地址和上电时序,再查EEPROM映射 |
| 马达动一下就不动 | 稳定时间参数过大、状态机卡在MOVING | 查settle time配置和状态上报逻辑 |
| 来回扫但是合不上焦 | 扫描边界设置不当、EEPROM端点值错误 | 查近焦/远焦端点的DAC映射 |
| 近距离对焦正常,远距离对焦失败 | 马达行程不够、视场角位置转换错误 | 查镜头位移对应关系 |
| 对焦后画面来回漂移 | 温度补偿路径异常、VCM迟滞未处理 | 查温度补偿系数和迟滞校正 |
| 手机休眠后重新打开相机,AF失效 | 休眠时I2C总线电源被切断,恢复后未重新初始化 | 查恢复流程是否重新读取EEPROM |
排查时有一个习惯值得养成:先把问题现象转化成日志里的具体行为。比如“来回扫但合不上焦”,对应的日志就应该是Lens HAL里搜索边界反复反转、AF算法每次把位置调回原点。日志里看到这样的循环,再回头去查边界配置,比盲目调算法参数可靠得多。
还有一类疑难问题是偶发性的,比如开机前几次拍照正常,之后对焦越来越慢。这种多半不是Lens HAL逻辑错误,而是VCM马达在连续运动后温度升高,线圈电阻变大导致的机械响应变慢。评论区很多同行反馈过这个问题,处理思路是适当放宽稳定时间窗口,或者限制连续FOCUS的最小间隔时间。这类问题不写日志看不出来,需要结合实际环境判断。
6. 移植第三方AF马达时最容易踩的坑记录
MT6737项目上最容易耗时间的阶段,是换用不同品牌的VCM马达之后重新调试AF。表面上看,不同马达都是通过I2C接收DAC码,换成另一个品牌的IC应该只是地址改一下。实际操作下来,差异点远比想象中多,我挑几个印象最深的记录一下。
第一个坑是I2C字节序和寄存器位宽不一致。某款常见的VCM IC用16位寄存器存12位DAC,高4位是标志位;另一款却把DAC拆成两个8位寄存器分两次写。代码里如果沿用上一颗IC的拼位逻辑,轻则写入值错误,重则直接导致马达锁死不动。移植时第一件事就是拿到新IC的数据手册,把每一条I2C命令的字节序、位宽、写时序全部核对一遍,不要依赖旧的初始化代码。
第二个坑是EEPROM校准数据格式不一致。同样是“近焦端点DAC”,有的模组厂给的是10位二进制原始值,有的给的是带符号的偏移量,还有的加了一个缩放比例。如果你在Lens HAL里写死了数据解析格式,换模组厂之后读到的数值会完全不可用。更隐蔽的是,某些模组厂的校准数据里还包含马达的驱动方向标志位——正接反接影响DAC码递增方向和镜头运动方向的一致性,没处理对就会出现“越调越模糊”的情况。
第三个坑是马达行程和DAC码的线性关系假设。很多便宜的马达在行程两端有明显的非线性区,中间段DAC码和位移基本成正比,靠近两端就会出现“码变了很多,镜头没动多少”的现象。AF算法直接拿DAC码当位移用,在高精度场景下就会失焦。解决办法是分段映射,或者在算法扫描时限制边界,不要轻易扫到物理行程的极限位置。
第四个坑是VCM稳定时间的估算。同一颗VCM,在空载和带镜头两种情况下稳定时间差别很大,而且和环境温度相关。原型机上用一个值能跑通整个对焦流程,量产机换成另一批次的模组后就开始出现过冲振荡。Lens HAL里所有硬编码的稳定时间都要改成从EEPROM或系统配置读取,并且留一定余量。宁可多等几十毫秒,也不要让AF算法读到镜头还在晃动的统计值。
第五个坑是休眠唤醒后的I2C恢复。VCM驱动IC的I2C总线在休眠时可能被断电,唤醒后Lens HAL如果不重新初始化就下发命令,大概率会收到I2C错误。我遇到过一种情况:唤醒后第一次对焦失败,再次点击就正常了,原因就是恢复流程没有对马达做状态复位。处理方法是把“重新读取马达状态 + 重新加载EEPROM映射”做成唤醒路径的固定动作。
移植调试的通用建议是:先拿一个已知正常的模组做基准,记录一次完整对焦的日志;更换马达后再走一遍同样的流程,对比日志中DAC码序列的变化。差异点就是新马达需要适配的地方。不要一次性把所有参数都改完,那样发生问题根本定位不到是哪个参数引起的。
另外,方案商的Lens HAL通常会提供一个自定义的马达参数配置文件,里面列出了VCM IC型号、I2C地址、DAC位宽、稳定时间、行程范围等字段。拿到一颗新马达后,把配置文件的参数更新到位,再配合日志验证,整个移植流程基本就能顺畅走完。这一步做到位,后面的AF算法调参就轻松很多。