第一章:相机驱动概述

做手机相机驱动这些年,我最大的感受就是——这活儿真不是光看芯片手册就能搞定的。你得懂硬件怎么连,软件怎么搭,还得会调工具链。今天咱们就从最基础的开始聊。

1.1 手机相机硬件架构

一台手机的相机模组,说白了就是三个核心部件:镜头、传感器、ISP。它们各司其职,缺一不可。

镜头(Lens)

镜头负责把光线汇聚到传感器上。别小看这玩意儿,它决定了进光量和成像质量。我遇到过不少项目,驱动调了半天,结果发现是镜头脏了或者装歪了——嗯,这种坑踩过一次就记住了。

  • 定焦镜头:结构简单,成本低,常见于副摄
  • 自动对焦镜头:带VCM马达,驱动需要控制电流
  • 可变光圈镜头:高端机才有,驱动逻辑更复杂

传感器(Sensor)

传感器是相机的「视网膜」。它把光信号转成电信号。常见的接口有MIPI CSI-2,数据量很大,布线要小心。我个人习惯是先看传感器数据手册里的时序图,再写驱动——顺序搞反了容易出问题。

关键参数:分辨率、帧率、像素大小、动态范围、信噪比。这些在驱动初始化时都要配好。

ISP(图像信号处理器)

ISP负责把传感器输出的RAW数据变成我们能看的图片。它可能是独立的芯片,也可能是集成在SoC里。驱动里要做的,就是给ISP喂配置参数,比如白平衡、降噪、锐化等等。

你想想看,如果没有ISP,传感器出来的数据就是一堆马赛克。所以驱动里ISP的初始化顺序很重要——先配时钟,再配电源,最后配寄存器。我曾经因为电源时序搞反了,ISP死活不工作,查了两天才发现。

1.2 驱动软件栈

软件栈分三层:HAL、Kernel Driver、V4L2。咱们一层层说。

HAL(硬件抽象层)

HAL是Android系统里连接Framework和Kernel的桥梁。它把底层的硬件操作封装成标准接口。比如打开相机、设置参数、获取帧数据。我建议初学者先别碰HAL,容易绕晕。先搞懂Kernel层再说。

Kernel Driver

这是驱动开发的核心。你要写的是Sensor驱动、ISP驱动、闪光灯驱动等等。它们直接操作硬件寄存器。举个例子,Sensor驱动里要配PLL、配曝光时间、配增益。代码大概长这样:

static int sensor_init(struct i2c_client *client)
{
    // 配置PLL
    sensor_write(client, 0x0100, 0x00); // 软件复位
    msleep(10);
    sensor_write(client, 0x0300, 0x04); // PLL分频
    sensor_write(client, 0x0301, 0x10); // PLL倍频
    // 配置曝光
    sensor_write(client, 0x0200, 0x01); // 曝光时间高字节
    sensor_write(client, 0x0201, 0x00); // 曝光时间低字节
    return 0;
}

小技巧:写Sensor驱动时,先读ID寄存器确认通信正常,再往下配。我习惯在probe函数里加一句dev_info打印ID值,方便调试。

V4L2(Video for Linux 2)

V4L2是Linux内核里视频设备的统一框架。它定义了一套标准接口,比如open、close、ioctl、mmap。你的Sensor驱动要注册成V4L2设备,上层才能用标准方式访问。

说白了,V4L2就是个「中间人」。它让应用层不用关心底层是哪个Sensor。我刚开始学的时候,觉得V4L2的ioctl命令太多了,记不住。后来发现,常用的就那几个:VIDIOC_QUERYCAP、VIDIOC_S_FMT、VIDIOC_STREAMON。其他的用到再查。

1.3 驱动开发环境搭建

环境搭不好,后面全是坑。我见过太多人卡在编译这一步。咱们一步步来。

交叉编译工具链

手机上的CPU是ARM架构,你的电脑是x86架构。所以得用交叉编译工具链,在电脑上编译出能在手机上跑的程序。常用的有gcc-arm-linux-gnueabihf或者aarch64-linux-gnu。

安装很简单:

sudo apt-get install gcc-arm-linux-gnueabihf

然后编译内核模块时,指定CROSS_COMPILE变量:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

注意:工具链版本要和内核版本匹配。我曾经用新工具链编译旧内核,结果模块加载时报错“invalid module format”。折腾了半天,换成对应版本就好了。

adb调试

adb是Android调试桥。你编译好的驱动模块,得通过adb推到手机上。常用命令:

  • adb push:把文件推到手机
  • adb shell:进入手机终端
  • adb logcat:看日志

我个人的调试流程是这样的:

  1. 编译驱动模块(.ko文件)
  2. adb push到手机的/data/local/tmp/
  3. adb shell进去,insmod加载模块
  4. dmesg看内核日志
  5. 有问题就改代码,重新编译,重复以上步骤

嗯,这里要注意:每次insmod之前,记得先rmmod卸载旧模块,不然会报错“File exists”。

小结

这一章咱们聊了相机硬件架构、驱动软件栈、还有环境搭建。说白了,就是让你对整个相机驱动有个宏观认识。下一章咱们会深入Sensor驱动的具体实现。到时候我会拿一个实际项目里的代码来拆解,保证你看了就能用。

对了,如果你在搭环境时遇到问题,别硬扛。先查查内核版本和工具链是否匹配,八成能解决。