2. RK AF算法框架解析:初始化流程、核心数据结构与状态机

好,我们直接进入正题。RK的AF算法库,说白了就是一套硬件驱动 + 算法策略 + 状态管理的集合体。很多刚接触RK平台的工程师,上来就对着代码里的结构体和枚举发懵——我当年也一样。今天我就把AF模块的骨架拆开给你看。

2.1 AF模块的初始化流程

初始化这事儿,看着简单,坑其实不少。RK的AF初始化大致分三步走:

  1. 硬件资源申请:包括I2C总线、PWM定时器、中断线。我记得第一次调一个带VCM的模组,I2C地址死活对不上,查了半天发现是模组厂商把地址写错了。
  2. 算法参数加载:从XML或DTS中读取镜头参数、搜索步长、边界阈值等。
  3. 状态机复位:将AF状态机强制拉回AF_STATE_IDLE。

核心要点:初始化时一定要检查VCM的物理行程范围。我曾经遇到过模组标称行程是0~1023,实际只能走到950,结果算法一直往边界撞,画面反复震荡。

下面这张图是我整理的初始化流程,你看一眼就明白了:

RK AF模块初始化流程 AF模块入口 申请I2C/PWM/中断资源 从DTS/XML加载镜头参数 校验VCM行程范围 & 边界 状态机复位 → IDLE ⚠ 常见失败点 I2C通信异常 / 参数越界

个人习惯:我一般在初始化完成后,会主动发一次AF_CMD_GET_STATUS,确认硬件真的在线。这一步能过滤掉至少一半的硬件问题。

2.2 核心数据结构

数据结构是AF算法的骨架。RK的AF库里有几个关键结构体,我挑最重要的说:

结构体名称 作用 关键字段
rk_af_context AF全局上下文,贯穿整个生命周期 状态机变量、统计缓存、回调函数指针
rk_af_params 算法参数集合,从DTS解析而来 搜索步长、边界阈值、稳定判定窗口
rk_af_stats 3A统计结果,ISP反馈的清晰度数据 Sharpness值、对比度、亮度权重
rk_af_vcm VCM硬件抽象层,屏蔽驱动差异 当前位置、目标位置、行程范围

这里我特别想提一下rk_af_context。这个结构体里藏着一个环形统计缓存,用来保存最近N帧的清晰度值。为什么要这么做?因为单帧的清晰度波动很大,尤其是低照度场景。我踩过这个坑——有一次在室内弱光下,AF一直在来回震荡,后来发现是缓存长度设得太短,算法被噪声带偏了。

避坑指南:我曾经把rk_af_params里的search_step设成固定值,结果在微距和远景切换时,对焦速度慢得离谱。后来改成动态步长——根据当前清晰度梯度自动调整,效果立竿见影。

2.3 状态机转换逻辑

状态机是AF模块的大脑。RK的AF状态机不算复杂,但每个状态的转换条件必须搞清楚。我画了一张状态转换图:

RK AF状态机转换逻辑 IDLE SCANNING FOCUSED LOST 触发AF 找到峰值 清晰度下降超阈值 重新触发AF 💡 状态转换的关键判断依据 SCANNING → FOCUSED:连续3帧清晰度下降,且当前值 > 峰值*0.95

状态转换的触发条件,我整理成了一张表,方便你对照代码看:

当前状态 触发事件 下一状态 备注
IDLE 用户触发AF / 场景变化 SCANNING 开始爬山搜索
SCANNING 找到清晰度峰值 FOCUSED 锁定对焦位置
FOCUSED 清晰度下降超过阈值 LOST 进入失焦状态
LOST 重新触发AF SCANNING 再次搜索

关键点:从FOCUSED到LOST的阈值非常敏感。设得太小,画面稍微抖动就重新搜索;设得太大,画面已经糊了还不触发。我一般取峰值清晰度的15%~20%作为下降阈值,具体要看镜头模组的MTF曲线。

2.4 代码层面的关键实现

光讲理论不够,我贴一段实际初始化代码的核心片段。这是我从RK AF库中提取的简化版本:

// AF上下文初始化
int rk_af_init(rk_af_context *ctx, rk_af_params *params)
{
    int ret;

    // 1. 申请硬件资源
    ret = rk_af_vcm_init(&ctx->vcm, params->vcm_bus);
    if (ret) {
        // 我曾经在这里卡了三天,最后发现是I2C时钟频率不匹配
        return -EIO;
    }

    // 2. 加载算法参数
    ctx->params = *params;
    ctx->stats_buf = kzalloc(sizeof(uint32_t) * params->stats_window, GFP_KERNEL);
    if (!ctx->stats_buf)
        return -ENOMEM;

    // 3. 复位状态机
    ctx->state = AF_STATE_IDLE;
    ctx->peak_value = 0;
    ctx->peak_pos = 0;

    // 4. 注册3A回调(重要!)
    ctx->stats_cb = rk_af_stats_collect;

    return 0;
}

我的建议:初始化时最好加一个AF_STATE_INIT中间状态,用来做硬件自检。虽然RK官方库没这个状态,但我自己加了一行——自检失败直接返回错误,比等到运行时崩溃好得多。

2.5 状态机实现的几个坑

最后聊几个实际项目中容易踩的坑:

  • 状态转换死锁:SCANNING状态下如果一直找不到峰值,会卡死。我一般加一个timeout机制,超过500ms强制回到IDLE。
  • 中断上下文问题:AF状态机不要在中断里直接修改状态。我曾经这么干过,结果导致状态机跳转丢失。正确的做法是用workqueue延后处理。
  • 多摄同步:双摄场景下,两个AF模块的状态机要独立运行,但触发时机需要同步。我习惯用atomic_t做同步计数,等两个模块都完成SCANNING再一起切到FOCUSED。

嗯,AF算法框架这部分就先讲到这里。你把这些结构体和状态机逻辑吃透了,后面讲性能优化和场景适配时,才能跟得上节奏。