4、图像传感器接口与数据采集:MIPI CSI-2协议解析、Sensor驱动配置、RAW数据格式

图像传感器是ISP流水线的起点。说白了,Sensor没配好,后面算法再牛也白搭。这一章我们聚焦三个核心:MIPI CSI-2物理层和协议怎么跑、Sensor驱动怎么配、RAW数据到底长什么样。

本章核心脉络: MIPI物理层(D-PHY)→ CSI-2协议层(LLP)→ Sensor寄存器配置 → RAW数据格式解析 → 嵌入式系统采集实战

4.1 MIPI CSI-2协议解析

MIPI CSI-2是目前移动端和嵌入式视觉系统最主流的Sensor接口。我最早接触它是在做手机摄像头项目时,那时候对D-PHY的时序要求理解不深,结果第一版板子跑不到目标速率,折腾了两周才找到问题——原来是PCB走线等长没做好。

4.1.1 物理层:D-PHY

CSI-2的物理层通常是D-PHY,采用差分信号传输。一条lane由一对差分线(Dp/Dn)组成。常见的配置是1-lane、2-lane或4-lane。

参数 典型值 说明
每lane速率 80 Mbps ~ 2.5 Gbps 取决于Sensor和SoC能力
工作模式 HS(高速)/ LP(低功耗) HS传数据,LP传控制
电压摆幅 HS: 200mV / LP: 1.2V 差分摆幅很小,抗干扰强
时钟lane DDR双边沿采样 每个时钟沿传1bit

D-PHY有两种工作状态:高速模式(HS) 和 低功耗模式(LP)。HS模式下数据以差分信号高速传输,LP模式下用单端信号传输控制指令。嗯,这里要注意:从LP切换到HS需要经过一个特定的时序序列(LP-11 → LP-01 → LP-00 → HS-0),这个时序必须严格满足,否则接收端会丢包。

4.1.2 协议层:CSI-2 LLP

CSI-2的协议层叫LLP(Low Level Protocol)。它定义了数据怎么打包成包(Packet)传输。每个包分为短包(Short Packet) 和 长包(Long Packet)。

  • 短包: 只有包头,没有数据负载。用于帧同步、行同步、事件触发等控制信息。
  • 长包: 包含包头、数据负载和包尾。数据负载就是实际的像素数据。

包的结构如下:

长包格式:
[SoT] [PH: 2字节数据ID + 2字节字计数 + 1字节ECC] [Payload: 字计数指定长度] [PF: 2字节CRC16] [EoT]

短包格式:
[SoT] [PH: 2字节数据ID + 2字节数据值 + 1字节ECC] [EoT]

数据ID(Data Identifier)包含虚拟通道(VC) 和数据类型(DT)。VC支持最多4个虚拟通道,可以复用同一条物理链路传输多路视频流。DT则标识了数据类型,比如RAW8、RAW10、YUV422等。

避坑指南: 我曾经在一个项目中,Sensor输出的RAW10数据,但接收端配置成了RAW8,结果图像全是错位的。检查了半天才发现是DT字段不匹配。所以调试时第一件事就是确认DT值对不对。

4.1.3 帧结构

一帧图像在CSI-2链路上是这样组织的:

FS(帧起始短包) →  Line1(行长包) →  Line2(行长包) →  ... →  LineN(行长包) →  FE(帧结束短包)

每一行数据作为一个长包传输。行与行之间可以有消隐区(HBlank),帧与帧之间有帧消隐(VBlank)。这些消隐时间可以用来做其他处理,比如DMA传输或CPU干预。

4.2 Sensor驱动配置

Sensor驱动配置是嵌入式系统工程师的必修课。我习惯把配置流程分成三步:硬件初始化 → 寄存器配置 → 流控管理。

4.2.1 硬件初始化

上电时序是第一步,也是最容易出问题的地方。不同Sensor对电源上电顺序有严格要求,比如:

  1. 先上模拟电源(AVDD)
  2. 再上数字电源(DVDD)
  3. 最后上IO电源(IOVDD)
  4. 等待稳定后,释放复位信号
  5. 等待MCLK稳定
  6. 通过I2C/SPI读取Sensor ID确认通信正常
注意: 上电时序不满足可能导致Sensor无法正常工作,甚至永久损坏。我曾经遇到过因为电源管理芯片配置错误,导致AVDD和DVDD同时上电,结果Sensor内部LDO烧毁。所以一定要仔细看Datasheet的Power Sequence图。

4.2.2 寄存器配置

Sensor的寄存器配置通常通过I2C或SPI接口进行。典型的配置项包括:

寄存器组 典型地址 配置内容
模式选择 0x0100 软件复位、待机/流控模式
分辨率 0x0340-0x0347 输出宽度、高度、起始位置
帧率控制 0x0348-0x034B 行时间、帧消隐、PLL分频
输出格式 0x0112 RAW8/RAW10/RAW12/YUV等
增益/曝光 0x0202-0x0205 模拟增益、数字增益、曝光时间
MIPI配置 0x0300-0x0307 Lane数、速率、时钟分频

下面是一个典型的Sensor初始化代码片段(伪代码):

// 1. 上电后等待10ms
delay_ms(10);

// 2. 读取Sensor ID确认通信
uint16_t id = i2c_read_16bit(0x0000);
if (id != 0x1234) {
    printf("Sensor ID mismatch!\n");
    return -1;
}

// 3. 软件复位
i2c_write_8bit(0x0103, 0x01);
delay_ms(5);

// 4. 配置输出分辨率 1920x1080
i2c_write_16bit(0x0340, 1080);  // 输出高度
i2c_write_16bit(0x0342, 2200);  // 行长度(含消隐)

// 5. 配置输出格式 RAW10
i2c_write_8bit(0x0112, 0x2B);   // 0x2B = RAW10

// 6. 配置MIPI 4-lane, 速率 800Mbps/lane
i2c_write_8bit(0x0305, 0x04);   // Lane数
i2c_write_16bit(0x0307, 0x0064); // PLL配置

// 7. 启动流控
i2c_write_8bit(0x0100, 0x01);   // Streaming ON

4.2.3 流控管理

流控(Streaming)管理控制Sensor是否持续输出图像数据。启动流控后,Sensor会按照配置的帧率和分辨率持续通过MIPI接口发送数据。停止流控时,Sensor进入待机模式,功耗大幅降低。

我建议在系统初始化时先配置好所有寄存器,最后再启动流控。这样能避免在配置过程中产生不完整帧。

4.3 RAW数据格式

RAW数据是Sensor直接输出的原始像素值,未经任何ISP处理。它保留了最完整的图像信息,也是ISP算法的输入起点。

4.3.1 Bayer模式

大多数彩色Sensor使用Bayer滤色阵列。每个像素只采集一种颜色分量(R、G或B),通过插值算法恢复全彩色。常见的Bayer模式有:

  • BGGR: 第一行 B G B G ...,第二行 G R G R ...
  • RGGB: 第一行 R G R G ...,第二行 G B G B ...
  • GBRG: 第一行 G B G B ...,第二行 R G R G ...
  • GRBG: 第一行 G R G R ...,第二行 B G B G ...

嗯,这里要注意:不同Sensor的Bayer模式可能不同,配置错了会导致颜色完全错乱。我习惯在驱动初始化时读取Sensor的寄存器确认Bayer模式,而不是硬编码。

4.3.2 RAW位深与存储

RAW数据常见的位深有8bit、10bit、12bit、14bit、16bit。位深越高,动态范围越大,但数据量也越大。

位深 每像素bit数 存储方式 典型应用
RAW8 8 1字节/像素 低端摄像头、预览
RAW10 10 5字节/4像素(packed)或2字节/像素(unpacked) 主流手机、安防
RAW12 12 3字节/2像素(packed)或2字节/像素(unpacked) 工业相机、医疗
RAW16 16 2字节/像素 高动态、科学级

RAW10的packed存储方式比较特殊:每4个像素占用5个字节。比如像素值分别是A[9:0]、B[9:0]、C[9:0]、D[9:0],存储为:

Byte0: A[7:0]
Byte1: B[7:0]
Byte2: C[7:0]
Byte3: D[7:0]
Byte4: {A[9:8], B[9:8], C[9:8], D[9:8]}

这种打包方式节省带宽,但处理时需要拆包。我在项目中通常用unpacked方式,虽然多占内存,但CPU处理起来更简单。

4.3.3 RAW数据在内存中的布局

Sensor通过MIPI接口传输的数据,经过接收端(如SoC的CSI控制器)后,会被DMA写入内存。内存中的布局通常是:

地址偏移: 0x0000 ~ 0x0FFF  →  Line0 (1920像素, RAW10 packed = 2400字节)
地址偏移: 0x1000 ~ 0x1FFF  →  Line1
...
地址偏移: 0x0438000 ~ 0x0438FFF →  Line1079

每行数据可能有对齐要求(比如32字节对齐)。如果不对齐,DMA传输可能会出错。我建议在分配DMA缓冲区时,使用cacheline对齐的内存,避免cache一致性问题。

经验之谈: 调试RAW数据采集时,我习惯先抓一帧数据保存成文件,然后用Python解析验证。这样可以快速确认MIPI传输和DMA配置是否正确,不用每次都跑完整的ISP流程。

4.4 本章知识体系

下面这张图总结了本章的核心知识点和它们之间的关系:

图像传感器接口与数据采集知识体系 图像传感器接口与数据采集知识体系 MIPI CSI-2协议 Sensor驱动配置 RAW数据格式 D-PHY物理层 CSI-2 LLP协议 硬件初始化 寄存器配置 Bayer模式 位深与存储 HS/LP模式切换 短包/长包结构 上电时序 I2C/SPI配置 BGGR/RGGB等 Packed/Unpacked 核心流程:Sensor上电 → I2C配置寄存器 → 启动MIPI流控 → CSI-2接收 → DMA写入内存 → RAW数据 调试要点:检查上电时序、确认DT值、验证Bayer模式、核对RAW位深 嵌入式系统采集实战 DMA配置 → 缓冲区管理 → 帧同步 → 数据校验 ⚠️ 常见坑点 1. 上电时序不满足 → Sensor不工作或损坏 2. MIPI速率不匹配 → 丢包/花屏 3. RAW位深配置错误 → 图像错位/颜色异常

4.5 嵌入式系统采集实战要点

最后,我把嵌入式系统中采集Sensor数据的几个关键点列出来,这些都是我在实际项目中踩过的坑:

  1. DMA缓冲区管理: 使用双缓冲(ping-pong buffer)避免丢帧。一个缓冲区在DMA传输,另一个供CPU处理。
  2. 帧同步: 通过CSI-2的FS/FE短包或Sensor的VSYNC信号判断帧边界。我习惯用硬件VSYNC中断,比轮询更可靠。
  3. 数据校验: 抓取第一帧后,检查帧大小是否等于 宽度×高度×每像素字节数。如果不一致,说明配置有问题。
  4. 时钟域同步: MIPI时钟和系统总线时钟是异步的,需要FIFO或桥接逻辑做跨时钟域处理。
重要提醒: 调试MIPI接口时,示波器探头要尽量短,差分探头要匹配。我曾经因为探头接地线太长,测出来的眼图全是噪声,误以为是PCB设计问题,折腾了三天才发现是测量方法不对。