第4章:AF驱动与马达控制——VCM马达类型与I2C控制时序

各位好,我是你们的老朋友。今天咱们聊聊AF系统里最“接地气”的部分——VCM马达。说白了,镜头能不能动、动得快不快、停得准不准,全看它。

我刚开始接触高通平台时,总觉得AF算法是核心,马达不过是执行指令的“小兵”。后来踩过几次坑才明白——马达控制不好,算法再牛也白搭。你想想看,算法算出来要走到DAC值500,结果马达抖到了520,画面就糊了。

4.1 VCM马达的三种主流类型

目前手机摄像头里,VCM马达主要分三类:开环、闭环、中置。我按个人习惯,把它们比作三种性格的人。

类型 控制方式 位置反馈 典型精度 成本
开环(Open Loop) 开环电压驱动 无 ±10~15μm 低
闭环(Closed Loop) 闭环PID控制 霍尔传感器 ±1~3μm 中
中置(Middle / Ball-type) 开环+机械中置 无(机械限位) ±5~8μm 中低

4.1.1 开环马达

开环马达,说白了就是“给电压就动,不给就停”。没有反馈,你给多少DAC值,它跑到哪算哪。我在项目里遇到过一个问题:同一批马达,个体差异导致同样的DAC值,镜头位置能差出20μm。嗯,这就是开环的代价。

避坑指南: 开环马达的迟滞效应(Hysteresis)非常明显。从近端往远端走和从远端往近端走,同一个DAC值对应的位置可能差15μm以上。我曾经因为没做迟滞补偿,导致连续对焦时画面来回“呼吸”,被测试团队追着改了一个星期。

4.1.2 闭环马达

闭环马达就聪明多了。它内部有个霍尔传感器,实时检测镜头位置,然后通过PID控制让镜头精准停在目标位置。我个人习惯在闭环马达上做“位置环+速度环”双环控制,收敛速度比单环快30%以上。

为什么会这样?因为霍尔传感器能告诉你“现在在哪”,控制器就能算出“还差多少”。不像开环,只能靠猜。

4.1.3 中置马达

中置马达是个“混血儿”。它本质上还是开环控制,但机械结构上做了优化——镜头在中间位置时有一个机械“休息区”。平时镜头停在中置位,需要对焦时往两边走。好处是响应快,坏处是行程短。

我记得有一次做超薄机型,镜头模组空间受限,只能用中置马达。结果发现近摄端(Macro)行程不够,最后靠算法做了“数字补偿”才勉强过关。

4.2 DAC值与镜头位置的映射关系

DAC值,就是驱动马达的数字量。但DAC值和镜头位置之间,不是简单的线性关系。我画了一张图,帮你理清这个映射逻辑。

DAC值与镜头位置映射关系图 DAC值与镜头位置映射关系 DAC值输入 0 ~ 1023(10bit) 映射函数 Pos = f(DAC) 含迟滞、温度补偿 镜头位置输出 0 ~ 镜头行程(μm) 开环映射 近似线性 迟滞环明显 个体差异大 闭环映射 线性度好 迟滞可忽略 需校准霍尔增益 中置映射 中置区平坦 两端非线性 行程受限 关键经验 开环马达必须做“双向校准”:分别记录近→远和远→近的DAC-位置表 闭环马达重点关注霍尔传感器的线性区间,避开饱和区

实际项目中,我们通常会在产线做“DAC校准”。具体做法是:给马达一组DAC值,用激光测距仪记录实际镜头位置,然后拟合出一条曲线。我习惯用分段线性拟合,精度够用,计算量也小。

核心公式: 对于开环马达,位置与DAC的关系可以近似为:
Position = K * DAC + Offset + Hysteresis(DAC, direction)
其中K是增益,Offset是零位偏移,Hysteresis是迟滞项(与当前DAC和运动方向相关)。

4.3 I2C控制时序与实战要点

VCM马达的控制接口,99%都是I2C。高通平台通过CCI(Camera Control Interface)总线来读写马达的寄存器。说白了,就是往马达芯片里写DAC值,让它动起来。

4.3.1 标准I2C写时序

一个典型的I2C写操作包含:起始位、设备地址、寄存器地址、数据、停止位。我直接给你看代码,这是高通平台常用的驱动片段。

// 高通平台VCM马达I2C写操作示例
static int vcm_i2c_write(struct i2c_client *client, uint16_t reg, uint16_t val)
{
    int rc;
    struct i2c_msg msg;
    uint8_t buf[3];

    buf[0] = (reg >> 8) & 0xFF;  // 寄存器高字节
    buf[1] = reg & 0xFF;         // 寄存器低字节
    buf[2] = val & 0xFF;         // DAC值

    msg.addr = client->addr;
    msg.flags = 0;               // 写操作
    msg.len = 3;
    msg.buf = buf;

    rc = i2c_transfer(client->adapter, &msg, 1);
    if (rc < 0) {
        pr_err("VCM I2C write failed: %d\n", rc);
        return rc;
    }
    return 0;
}

这段代码看起来简单,但坑不少。我曾经遇到过一个问题:连续快速写DAC值时,I2C总线会卡死。后来发现是马达芯片的“写周期时间”(tWR)没满足——两次写操作之间至少要等500μs。

个人技巧: 在高通平台上,我习惯在每次I2C写之后加一个usleep_range(500, 600)。别小看这500微秒,它能避免90%以上的I2C通信异常。

4.3.2 高通平台CCI时序配置

高通平台的CCI控制器,可以配置I2C的时钟频率、上升时间、下降时间。我一般这样配:

参数 典型值 说明
时钟频率 400kHz 标准模式,兼容性好
上升时间 300ns 根据总线电容调整
下降时间 100ns 一般保持默认
写周期时间 500μs 两次写操作的最小间隔

嗯,这里要注意:如果总线电容太大(比如接了多个从设备),上升时间不够会导致波形畸变。我遇到过一例,就是因为马达和EEPROM共用一条I2C总线,上升时间从300ns改到500ns才稳定。

4.3.3 多马达同步控制

现在很多旗舰机有双摄、三摄,每个摄像头都有自己的VCM马达。高通平台支持多路CCI总线,可以同时控制多个马达。但有个细节:不同马达的I2C地址不能冲突。

我建议的做法是:在驱动初始化时,先扫描所有马达的I2C地址,确认唯一性。如果发现地址冲突,通过马达的“地址配置引脚”来区分。

曾经踩过的坑: 有一次做双摄项目,两个马达的I2C地址都是0x18。我以为软件上做“分时访问”就行,结果发现马达A写数据时,马达B的寄存器也被改了。最后硬件上改了马达B的地址引脚,才解决问题。所以,硬件地址唯一性是前提,软件分时只是辅助。

4.4 实战:从DAC值到镜头移动的完整流程

我把整个流程串起来,给你一个完整的闭环视角。

  1. AF算法计算目标位置:比如算法算出镜头需要走到DAC值500。
  2. 查映射表:根据马达类型,查表得到对应的DAC值(开环)或目标位置(闭环)。
  3. I2C写操作:通过CCI总线,把DAC值写入马达寄存器。
  4. 马达响应:VCM驱动线圈,推动镜头移动。
  5. 位置确认:闭环马达通过霍尔传感器读取实际位置;开环马达则等待固定时间(Settle Time)。
  6. AF算法确认:检查对焦评价函数是否达到峰值,如果没达到,继续调整。

这个流程看起来简单,但每一步都有优化空间。我个人习惯在“I2C写操作”和“位置确认”之间加一个状态机,用来处理异常情况——比如I2C写失败、马达卡住、霍尔传感器读数异常等。

一句话总结: VCM马达控制,本质上是“算法算得准 + 驱动写得稳 + 时序控得严”三者的结合。缺一个,对焦就飘。