4、摄像头硬件接口:MIPI CSI与DVP接口区别、Sensor初始化序列、时钟与电源管理

好,咱们进入第四讲。这一讲的内容,说白了就是摄像头和主控芯片之间怎么“说话”的硬件基础。你光会调驱动不行,得知道底层的物理接口长什么样,Sensor上电那一下到底发生了什么,时钟又是怎么个玩法。

我个人习惯,做摄像头驱动之前,先把硬件接口文档和Sensor的Datasheet翻烂。不然出了问题,你都不知道是软件没配好,还是硬件线没连对。

4.1 MIPI CSI 与 DVP 接口:两条不同的路

现在主流的摄像头接口就两种:MIPI CSI 和 DVP。DVP是老祖宗,MIPI是当红炸子鸡。我刚开始做嵌入式Linux的时候,用的还是DVP接口的OV7670,那会儿觉得并口传输挺快的。直到后来接手一个MIPI CSI的500万像素项目,才发现DVP在高分辨率下根本扛不住。

4.1.1 DVP 接口:并行的老将

DVP,全称Digital Video Port。它用一组并行数据线(通常是8位、10位或12位)加上同步信号来传输图像数据。

  • 数据线:D[0:7] 或 D[0:9],并行传输像素数据。
  • 同步信号:VSYNC(帧同步)、HSYNC(行同步)、PCLK(像素时钟)。
  • 特点:简单、便宜、开发容易。但缺点也很明显——线多、抗干扰差、速率上不去。

我在一个低端安防项目里用过DVP接口的Sensor,分辨率只有640x480。当时布线空间紧张,DVP那十几根线挤在一起,稍微有点干扰,图像就开始花屏。嗯,这里要注意,DVP的PCLK频率一旦超过48MHz,信号完整性就很难保证了。

4.1.2 MIPI CSI 接口:高速串行的新贵

MIPI CSI,全称Mobile Industry Processor Interface Camera Serial Interface。它用差分信号对(D-PHY)来传输数据,速率极高。

  • 数据通道:1 Lane、2 Lane 或 4 Lane。每个Lane是一对差分线(Dp/Dn)。
  • 时钟通道:一对差分时钟线(CLKp/CLKn)。
  • 特点:线少(4 Lane也就10根线)、抗干扰强、速率高(每Lane可达1Gbps以上)。

你想想看,同样是传输1080p@30fps的数据,DVP可能需要12根线加一个高频PCLK,而MIPI CSI用2 Lane就能搞定。这就是技术的进步。

核心区别总结

特性 DVP MIPI CSI
传输方式 并行 串行差分
线数 10~20根 4~10根
最高速率 ~48MHz PCLK 每Lane >1Gbps
抗干扰
适用场景 低分辨率、低成本 高分辨率、高速

4.2 Sensor 初始化序列:上电那一下的玄机

很多新手觉得Sensor初始化就是写几个寄存器。其实不然。Sensor的初始化序列,尤其是上电时序,搞错了直接烧芯片。我曾经在一个项目里,因为电源上电顺序没调好,连续烧了三块Sensor板子,那叫一个心疼。

4.2.1 标准上电时序

一般来说,Sensor的上电时序分三步走:

  1. 先供模拟电源(AVDD):通常是2.8V或3.3V。
  2. 再供数字电源(DVDD):通常是1.2V或1.8V。
  3. 最后供IO电源(IOVDD):通常是1.8V或2.8V。

为什么是这个顺序?因为Sensor内部的模拟电路先稳定,数字电路后启动,IO接口最后才需要工作。你反过来搞,IO口先有电了,但内部逻辑还没准备好,很容易产生闩锁效应(Latch-up)。

警告: 不同型号的Sensor,上电时序要求可能不同。一定要看Datasheet里的“Power Up Sequence”时序图。有些Sensor要求AVDD和DVDD同时上电,有些要求间隔至少1ms。别想当然。

4.2.2 复位与初始化

上电完成后,接下来是复位和初始化。我一般这么操作:

  1. 拉低复位引脚(XCLR):保持至少1ms。
  2. 拉高复位引脚:释放复位。
  3. 等待稳定:等待至少5ms,让Sensor内部PLL锁相环稳定。
  4. 通过I2C/SPI写寄存器:按照Datasheet推荐的初始化序列,逐项配置。

这里有个坑:有些Sensor的初始化序列里,第一步不是写寄存器,而是先读一个ID寄存器,确认通信正常。我建议你也这么做,能省很多调试时间。

// 伪代码示例:Sensor初始化流程
int sensor_init(void) {
    // 1. 上电(由PMIC或GPIO控制)
    gpio_set_value(AVDD_EN, 1);  // 开启模拟电源
    mdelay(5);
    gpio_set_value(DVDD_EN, 1);  // 开启数字电源
    mdelay(5);
    gpio_set_value(IOVDD_EN, 1); // 开启IO电源
    mdelay(5);

    // 2. 复位
    gpio_set_value(XCLR_PIN, 0); // 拉低复位
    mdelay(2);
    gpio_set_value(XCLR_PIN, 1); // 释放复位
    mdelay(10);

    // 3. 读取Sensor ID,确认通信
    uint16_t id = i2c_read_reg(SENSOR_ADDR, 0x0000);
    if (id != EXPECTED_ID) {
        printk("Sensor ID mismatch: 0x%04x\n", id);
        return -ENODEV;
    }

    // 4. 写入初始化寄存器序列
    for (int i = 0; i < init_seq_len; i++) {
        i2c_write_reg(SENSOR_ADDR, init_seq[i].reg, init_seq[i].val);
        mdelay(1); // 有些寄存器写入后需要等待
    }

    return 0;
}

4.3 时钟与电源管理:省电与稳定的平衡

摄像头是个耗电大户。尤其是高帧率、高分辨率的时候,功耗能到几百毫瓦。嵌入式设备对功耗敏感,所以时钟和电源管理是必修课。

4.3.1 时钟源的选择

Sensor需要两个时钟:一个是主时钟(MCLK/XCLK),由主控提供;另一个是内部PLL产生的像素时钟(PCLK),用于数据输出。

  • MCLK:通常为24MHz、27MHz或48MHz。由主控的GPIO或专用时钟输出引脚提供。
  • PCLK:由Sensor内部PLL根据MCLK倍频得到。比如MCLK=24MHz,PLL倍频后得到PCLK=96MHz。

我个人习惯,MCLK尽量用27MHz。为什么?因为很多Sensor的PLL设计对27MHz支持最好,能产生整数倍的像素时钟,减少抖动。

小技巧: 如果你发现图像有横纹或闪烁,很可能是MCLK的精度不够。用示波器量一下MCLK的波形,看看频率是否准确,抖动是否过大。我曾经遇到过因为晶振负载电容没配好,导致MCLK频率偏了0.5%,图像直接没法看。

4.3.2 电源管理策略

嵌入式Linux下,电源管理通常通过Regulator框架来实现。Sensor的AVDD、DVDD、IOVDD都可以挂在不同的Regulator上。

我一般这么设计:

  • 正常工作时:所有电源都开启。
  • 待机模式(Standby):关闭AVDD和DVDD,只保留IOVDD,保持I2C通信能力。
  • 休眠模式(Sleep):所有电源关闭,Sensor完全断电。

在驱动里,通过struct regulator来控制电源的开关。代码大概长这样:

// 电源控制示例
static int sensor_power_on(struct device *dev) {
    struct regulator *avdd = regulator_get(dev, "avdd");
    struct regulator *dvdd = regulator_get(dev, "dvdd");
    struct regulator *iovdd = regulator_get(dev, "iovdd");

    regulator_enable(avdd);
    mdelay(5);
    regulator_enable(dvdd);
    mdelay(5);
    regulator_enable(iovdd);
    mdelay(5);

    return 0;
}

static int sensor_power_off(struct device *dev) {
    struct regulator *avdd = regulator_get(dev, "avdd");
    struct regulator *dvdd = regulator_get(dev, "dvdd");
    struct regulator *iovdd = regulator_get(dev, "iovdd");

    regulator_disable(avdd);
    regulator_disable(dvdd);
    regulator_disable(iovdd);

    return 0;
}

嗯,这里要注意,regulator_getregulator_put要成对出现,不然会有内存泄漏。我刚开始写驱动时,就因为这个被同事review出来好几次。

4.3.3 时钟门控与动态调频

有些主控支持时钟门控(Clock Gating),可以在Sensor不工作时关闭MCLK,进一步省电。但要注意,关闭MCLK后,Sensor内部PLL会失锁,重新开启后需要重新初始化。

动态调频(DVFS)在摄像头场景下用得不多,因为Sensor对时钟频率要求固定。但如果你做的是低帧率应用(比如10fps),可以尝试降低MCLK频率,减少功耗。不过要小心,MCLK降低后,PCLK也会降低,帧率会受影响。

好了,这一讲就到这里。MIPI CSI和DVP的区别、Sensor上电初始化的细节、时钟电源的管理,这些都是硬件底层的硬功夫。下一讲,我们会进入V4L2框架,看看软件层面怎么操作摄像头。