2. RK AF算法框架解析:初始化流程、核心数据结构与状态机
好,我们直接进入正题。RK的AF算法库,说白了就是一套硬件驱动 + 算法策略 + 状态管理的集合体。很多刚接触RK平台的工程师,上来就对着代码里的结构体和枚举发懵——我当年也一样。今天我就把AF模块的骨架拆开给你看。
2.1 AF模块的初始化流程
初始化这事儿,看着简单,坑其实不少。RK的AF初始化大致分三步走:
- 硬件资源申请:包括I2C总线、PWM定时器、中断线。我记得第一次调一个带VCM的模组,I2C地址死活对不上,查了半天发现是模组厂商把地址写错了。
- 算法参数加载:从XML或DTS中读取镜头参数、搜索步长、边界阈值等。
- 状态机复位:将AF状态机强制拉回
AF_STATE_IDLE。
核心要点:初始化时一定要检查VCM的物理行程范围。我曾经遇到过模组标称行程是0~1023,实际只能走到950,结果算法一直往边界撞,画面反复震荡。
下面这张图是我整理的初始化流程,你看一眼就明白了:
个人习惯:我一般在初始化完成后,会主动发一次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状态机不算复杂,但每个状态的转换条件必须搞清楚。我画了一张状态转换图:
状态转换的触发条件,我整理成了一张表,方便你对照代码看:
| 当前状态 | 触发事件 | 下一状态 | 备注 |
|---|---|---|---|
| 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算法框架这部分就先讲到这里。你把这些结构体和状态机逻辑吃透了,后面讲性能优化和场景适配时,才能跟得上节奏。