MIPI 协议层(CSI-2 / DSI)概述
做 MIPI 调试这么多年,我有个很深的体会——很多人搞不懂虚拟通道,其实根子出在没吃透协议层。你想想看,PHY 层只管高速串行的电平翻转,Lane 管理层只管把 bit 流拆成字节,真正理解"数据从哪里来、要往哪里去"的,是协议层。虚拟通道的切换逻辑,说白了就是在协议层里做的文章。
这一章我们就把 CSI-2 和 DSI 的协议层拆开来看。不扯太深的理论,重点落在"虚拟通道在协议层里怎么编码、怎么识别、怎么切换",为后面的实战调试铺好路。
1. MIPI 协议栈的分层模型
先看整体架构。MIPI 协议栈从上到下分为四层,每一层各管一摊事:
| 层次 | CSI-2(相机) | DSI(显示) | 核心职责 |
|---|---|---|---|
| 应用层 | Sensor 输出 RAW/YUV/JPEG | Display 刷新帧数据 | 原始图像数据格式 |
| 协议层 | VC 打包 + 数据类型标识 | VC 打包 + 命令/数据标识 | 虚拟通道分配、包封装 |
| Lane 管理层 | 字节分发到各 Lane | 字节分发到各 Lane | Lane 拆分与合并 |
| PHY 层 | D-PHY / C-PHY | D-PHY / C-PHY | 串行化、电气特性 |
这里有个关键点——虚拟通道是在协议层实现的。Lane 管理层和 PHY 层根本不关心你是哪个 VC 的数据,它们只负责把字节流无损地搬运过去。所以,切换虚拟通道的本质,就是在协议层修改包头里的 VC 标识符。
核心理解:虚拟通道是协议层的逻辑概念,不是物理概念。4 位 VC_ID 最多支持 16 个虚拟通道,所有 VC 的数据都在同一条物理链路上传输。
下面这张图能帮你直观看到 CSI-2 和 DSI 在分层结构上的异同,以及虚拟通道所处的位置:
从图上可以清楚看到:CSI-2 和 DSI 在分层结构上是对称的,区别主要在于应用层处理的数据类型不同,但协议层的 VC 机制完全一致。这也是为什么后面讲虚拟通道切换时,CSI-2 和 DSI 的调试方法是通用的。
2. CSI-2 协议层:数据类型与虚拟通道
CSI-2 是 Camera Serial Interface,它的协议层主要做两件事:给图像数据打上 VC 标签 和 标注数据类型。
每个数据包进入协议层时,会被封装成统一格式。包头里有一个字节叫"数据标识符"(Data Identifier, DI),它的结构如下:
Bit 7:4 — 虚拟通道编号 (VC_ID),范围 0~15
Bit 5:0 — 数据类型编号 (DT),范围 0~63
举个例子。我在项目中调试过一颗 RAW10 的 sensor,它输出图像数据时 DT 值是 0x2B。如果这个 sensor 被配置为 VC 0,那么每个数据包的 DI 字节就是 0x0B(VC=0, DT=0x2B)。如果 sensor 同时输出 RAW10 和 YUV422 两路数据,通常会分配到不同的 VC 上,比如 VC0 传 RAW10,VC1 传 YUV422。协议层就是靠 DI 里的这 4 位 VC_ID 来区分两路流的。
| 常见 CSI-2 数据类型 | DT 值(Hex) | 说明 |
|---|---|---|
| RAW8 | 0x2A | 8-bit Bayer RAW |
| RAW10 | 0x2B | 10-bit Bayer RAW |
| RAW12 | 0x2C | 12-bit Bayer RAW |
| YUV422 8-bit | 0x1E | YUV 4:2:2 格式 |
| JPEG | 0x40 | JPEG 压缩流 |
经验之谈:我建议你在配置 sensor 寄存器时,先确认 DT 值是否与接收端(ISP 或 SoC)匹配。如果 DT 对不上,接收端可能会直接丢弃数据包,现象就是"黑屏"或"无数据"。我记得有个项目就是因为 DT 配错,折腾了两天才找到原因。
3. DSI 协议层:命令模式与虚拟通道
DSI 是 Display Serial Interface,它的协议层比 CSI-2 多了一个"命令模式"。DSI 不仅传像素数据,还传命令——比如初始化序列、亮度调节、刷新率设置。这些命令和数据的区分,也是通过 DT 来做的。
DSI 协议层定义了两种包:
- 短包:用于传命令,没有数据负载,比如 0x05(写命令)、0x15(读命令请求)。
- 长包:用于传大块数据,像图像帧、初始化参数表。
不管是短包还是长包,包头里都包含 VC_ID。我在调试一块 MIPI 屏时遇到过一个问题:屏幕只有左边一半显示正常,右边一半花屏。最后用逻辑分析仪抓包发现,显示驱动 IC 内部把 VC 和 DT 的映射搞混了,导致一部分像素数据被当成命令处理了。
注意:DSI 的命令模式和视频模式共用 VC 编号。当你同时使用命令模式和视频模式时,务必确保两种模式的 VC 没有冲突。我曾经见过有人把命令通道放在 VC0,视频通道也放在 VC0,结果屏幕初始化正常,但一刷图就卡死。
4. 虚拟通道的编码与包格式
理解了 VC 在协议层中的位置,我们再看看具体的数据包怎么编解码。MIPI 协议层的长包格式如下:
+---------+---------+-----------+-----------------+---------+
| SoT | DI | WC | Data Payload | CS |
| (包起始) | (VC+DT) | (字计数) | (WC 字节) | (校验和) |
+---------+---------+-----------+-----------------+---------+
1 Byte 1 Byte 2 Bytes 0~65535 Bytes 2 Bytes
其中 DI 字节的 Bit[7:4] 就是 VC_ID。接收端的协议层会检查每个包的 VC_ID,把它分发到对应的处理通道。如果你在调试时发现某一路 VC 的数据丢了,先用示波器或协议分析仪抓一下包,看看 DI 字节里的 VC_ID 是否和配置一致。
为什么会丢数据?我遇到过两类情况:
- 发送端 VC 配置错误——sensor 或主控的 VC 映射寄存器写错了,导致包头里的 VC_ID 和预期不符。
- 接收端 VC 过滤错误——接收端(ISP 或显示驱动)的 VC 使能寄存器没配好,直接把对应 VC 的数据丢弃了。
调试要点:虚拟通道切换的本质就是修改 DI 字节中的 VC_ID 域。发送端和接收端的 VC_ID 必须严格一致,否则数据传不过去。
5. 实战经验:协议层调试的几点建议
最后聊聊我这些年积累的一些"土办法",不一定多高深,但管用。
第一,优先用协议分析仪抓包。 别光靠读寄存器。我习惯在调试初期就用分析仪抓一段 MIPI 数据流,直接看 DI 字节里的 VC_ID 和 DT,确认发送端出来的包是对的,再查接收端。
第二,逐层排查。 如果虚拟通道切不过去,先确认 PHY 层有没有 Lock(锁定),再确认 Lane 管理层有没有同步(Sync),最后才查协议层的 VC 匹配。很多新手一上来就调 VC 寄存器,结果发现 PHY 根本没连上,白白浪费时间。
第三,用好"回环测试"。 如果条件允许,把发送端和接收端用一根短跳线连起来,配置成回环模式。这样数据发出去又收回来,可以直接验证 VC 编码和解码是否正确。我在几个 FPGA 项目里都用过这招,效率很高。
一个小技巧:写一个简单的脚本,遍历所有 VC(0~15)发送测试图案,然后在接收端检查每个 VC 是否能收到正确的数据。这样能快速定位是哪个 VC 的链路出了问题。我至今还在用这个办法。