2、Camera硬件基础:摄像头模组构成与接口协议

做Camera驱动开发,第一件事是什么?

不是看代码,是看硬件。我见过太多工程师上来就调寄存器,结果折腾半天发现是模组供电不对。所以今天咱们先把摄像头模组的底裤扒干净——从物理构成到通信协议,把那些你迟早要踩的坑提前说清楚。

2.1 摄像头模组三大件:Lens、Sensor、ISP

一个完整的摄像头模组,说白了就三样东西:镜头、传感器、图像处理器。我习惯把它们比作「眼睛、视网膜、视觉皮层」——缺一个都看不见东西。

2.1.1 Lens(镜头)

镜头负责把光线汇聚到传感器上。参数很多,但驱动工程师最该关注的是这几点:

  • 焦距:决定了视场角。我调过一款广角镜头,边缘畸变特别严重,后来发现是镜头模组装配公差太大。
  • 光圈:F值越小进光量越大。但要注意,大光圈镜头在强光下容易过曝,需要配合Sensor的曝光控制。
  • VCM(音圈马达):自动对焦的核心。我踩过最大的坑——VCM驱动电流没配好,镜头咔咔响就是不对焦。
⚠️ 注意: 镜头模组有方向性!我曾经把一片IR-Cut滤光片装反了,白天画面偏紫,晚上直接全黑。后来养成了习惯:拿到模组先看丝印,确认安装方向。

2.1.2 Sensor(图像传感器)

Sensor是模组的心脏。它把光信号转成电信号。目前主流是CMOS,少数高端用CCD。

我个人习惯把Sensor参数分成三类:

类别 关键参数 驱动关注点
分辨率 像素阵列(如1920x1080) MIPI lane数够不够?带宽算过没?
帧率 30fps / 60fps / 120fps 曝光时间窗口够不够?
输出格式 RAW10 / RAW12 / YUV ISP能不能处理?DMA配置对了吗?

嗯,这里要注意:Sensor的寄存器配置顺序很重要。我遇到过一例——先配了输出格式再配分辨率,结果Sensor直接死锁。后来查手册才发现,某些型号必须按特定顺序初始化。

2.1.3 ISP(图像信号处理器)

ISP负责把Sensor的原始数据变成人眼能看的画面。它可能集成在Sensor内部,也可能是独立的芯片。

你想想看,Sensor输出的RAW数据有多「原始」?每个像素只有一种颜色(拜耳阵列),而且有噪声、有暗角、有坏点。ISP要干的事包括:

  • 去马赛克:把拜耳阵列插值成RGB
  • 自动白平衡:让白色在不同光源下看起来都是白的
  • 降噪:2D降噪、3D降噪,我调过最头疼的是运动场景下的拖影
  • Gamma校正:人眼对暗部更敏感,Gamma曲线就是干这个的
💡 个人经验: 如果ISP是独立的,驱动里一定要留好调试接口。我习惯在驱动里加一个debugfs节点,可以实时dump ISP的中间结果——比如去马赛克前的RAW数据、降噪后的图像。这招帮我定位过至少5个疑难杂症。

2.2 MIPI CSI/DSI接口协议

MIPI是移动设备上最主流的摄像头接口。CSI是摄像头接口,DSI是显示接口。咱们做Camera驱动,主要跟CSI打交道。

2.2.1 MIPI CSI物理层

MIPI用差分信号传输,一组差分对叫一个Lane。常见的配置有1-Lane、2-Lane、4-Lane。带宽怎么算?

带宽 = Lane数 × 每Lane速率 × 8/10编码效率

举个例子:4-Lane、每Lane 1Gbps,实际可用带宽是 4 × 1G × 0.8 = 3.2Gbps。够不够传1080p 60fps?算一下:1080p 60fps RAW10的数据量是 1920×1080×10×60 ≈ 1.24Gbps。嗯,绰绰有余。

但我遇到过一个问题:某款Sensor标称支持4-Lane,但实际布线时有一对差分线等长没做好,导致信号歪斜,画面出现条纹。后来我学乖了——硬件设计阶段就要求Layout工程师把MIPI差分对等长控制在±5mil以内。

2.2.2 CSI协议层

CSI协议分三层:物理层、协议层、应用层。驱动工程师最关心的是协议层——怎么把数据包解析出来。

一个典型的CSI数据包长这样:

| 包起始码 | 数据标识符 | 字计数 | 有效数据 | 校验和 |
  • 包起始码:0x00000001 或 0x00000002(短包/长包)
  • 数据标识符:告诉你是Y数据还是UV数据,或者是嵌入式数据
  • 字计数:有效数据的字节数
  • 校验和:CRC校验,我习惯在驱动里打开CRC检查,一旦出错就打印警告
⚠️ 踩坑记录: 我曾经遇到一个Bug——画面偶尔出现绿色条纹。查了三天,最后发现是CSI接收端的FIFO深度不够,导致数据溢出。解决办法:在驱动里把接收FIFO阈值调高,同时降低DMA中断频率。

2.2.3 DSI接口(顺带一提)

虽然咱们主要做Camera,但DSI也得了解。因为有些平台用DSI做预览回显——Camera数据经过ISP处理后,直接通过DSI送到显示屏。

DSI和CSI在物理层几乎一样,区别在协议层。DSI有命令模式和视频模式两种传输方式。我建议:做预览回显时用视频模式,延迟更低;做拍照预览时用命令模式,控制更灵活。

2.3 I2C/SPI控制总线

Sensor的寄存器配置、ISP的参数调整,全靠这两条总线。它们就像Camera模组的「神经」,控制着每一个像素的行为。

2.3.1 I2C总线

I2C是Camera模组最常用的控制总线。两根线:SCL(时钟)、SDA(数据)。

驱动里配置I2C时,我习惯关注三个参数:

  • 速率:标准模式100kHz,快速模式400kHz。我建议用400kHz,但要注意上拉电阻——我遇到过因为上拉电阻太大,信号上升沿太慢,导致通信失败的案例。
  • 地址:7位地址还是10位地址?大部分Sensor用7位。但要注意,有些Sensor的地址可以配置——通过拉高/拉低某个引脚来改变地址。我习惯在驱动里把地址做成可配置的,方便调试。
  • 时序:起始条件、停止条件、应答信号。嗯,这里有个坑——有些Sensor的应答信号延迟特别大,需要把I2C控制器的时钟延展功能打开。
// 我常用的I2C读写函数模板
static int sensor_i2c_write(struct i2c_client *client, u16 reg, u8 val)
{
    struct i2c_msg msg;
    u8 buf[3];
    int ret;

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

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

    ret = i2c_transfer(client->adapter, &msg, 1);
    if (ret != 1) {
        dev_err(&client->dev, "I2C write failed\n");
        return -EIO;
    }
    return 0;
}

2.3.2 SPI总线

SPI比I2C快,但线多。四根线:SCLK、MOSI、MISO、CS。有些高速Sensor会用SPI来传输配置数据,甚至有些ISP的固件升级也走SPI。

我建议:如果Sensor支持SPI和I2C两种接口,优先选SPI。为什么?因为SPI是全双工,而且没有I2C那种应答延迟。我在一个项目中把I2C换成SPI后,Sensor初始化时间从200ms降到了30ms。

但SPI也有坑:

  • 时钟极性/相位:CPOL和CPHA必须和Sensor匹配。我习惯在驱动里先读Sensor的ID寄存器——如果读出来是0xFF或0x00,大概率是SPI模式配错了。
  • 片选信号:有些Sensor要求CS在传输期间保持低电平,有些则要求每个字节之间拉高一次。这个必须看数据手册,不能猜。
💡 调试技巧: 我习惯在驱动里加一个「总线测试」函数——写一个已知值到Sensor的测试寄存器,再读回来比对。如果不对,直接打印错误并停止初始化。这招帮我快速定位过至少3次硬件焊接问题。

2.4 小结

Camera硬件基础就这些。说白了,Lens负责收光,Sensor负责转电,ISP负责美化。MIPI是高速公路,I2C/SPI是控制通道。

我个人觉得,做Camera驱动最难的不是写代码,而是理解硬件怎么工作。你想想看,如果你连Sensor的曝光时间怎么配置、MIPI的带宽怎么算都不清楚,出了问题只能瞎猜。

下一章咱们开始实战——从零搭建一个Camera驱动的框架。到时候我会把今天讲的硬件知识都用上。

📌 本章核心要点:
  • 摄像头模组 = Lens + Sensor + ISP,缺一不可
  • MIPI CSI带宽 = Lane数 × 每Lane速率 × 0.8
  • I2C配置注意速率和上拉电阻,SPI注意时钟极性和片选
  • 驱动里一定要加调试接口和总线测试函数