DSI数据包结构:包类型、包头、包尾、CRC等

DSI的数据包结构,说白了就是两套规则:短包和长包。我刚接触DSI协议时,总觉得这俩东西差不多,后来在调一块屏幕的初始化序列时栽了跟头——短包传命令,长包传数据,混用就会导致接收端解析错误。今天就把这两套结构掰开揉碎讲清楚。

包类型:短包 vs 长包

DSI链路上只有两种包:短包和长包。短包固定4字节,长包则根据负载长度可变。想一下,为啥要这么设计?

  • 短包:用于命令、同步、应答等控制场景。固定大小,解析快,开销低。
  • 长包:用于传输像素数据、大块配置参数等。带CRC校验,保证数据完整性。

我曾经在调试一块4K屏时,误把长包格式用在写寄存器命令上,结果屏幕死活没反应。后来一看协议手册才反应过来——短包才是命令的正确打开方式。

包头(Packet Header)结构

短包和长包的头结构不一样,但都有一个共同的核心字段:Data ID(1字节)、Word Count / Data0/1(2字节)、ECC(1字节)。下面这张表对比一下两者的差异。

字段 短包 长包
字节0Data ID(VC + DT)Data ID(VC + DT)
字节1-2Data0 / Data1(16位数据参数)Word Count(16位负载字节数)
字节3ECC(错误纠正码)ECC(错误纠正码)

Data ID里包含了Virtual Channel(VC,2位)和Data Type(DT,6位)。VC用来区分同一链路上不同设备的数据流,DT则告诉接收端这个包是干什么的——比如0x29是长包写命令,0x05是短包同步脉冲。我在一个多摄像头项目里,就是靠VC区分主摄和广角的数据流,避免数据打架。

重点:长包的Word Count是负载字节数,不包括包头和CRC。注意这个值不能超过DCS和显示模块协商的最大包长,否则接收端会丢弃包。

包尾:EoT(End of Transmission)

长包传完后,并不是直接进入下一个包。DSI要求发送端在长包结束后插入一个EoT等待序列——其实就是一段LPS(Low Power State,低功耗状态),用来做包边界标记。短包不需要EoT,因为短包本身就是原子操作。

你想想看,如果没有这个EoT,接收端怎么知道当前包结束了?靠CRC校验吗?CRC只能检错,不能做帧定界。所以EoT是物理层层面的包尾标记。

个人习惯:我调试时喜欢把示波器探针挂在Lane0上,看LPS的长度就能快速判断长包有没有正确结尾。有一次发现EoT等待时间短了200ns,结果接收端状态机直接跑飞。

CRC校验:长包的“护身符”

长包的负载(Payload)后面紧跟着2字节CRC(Cyclic Redundancy Check),算法是CRC-16-CCITT(多项式0x1021)。CRC覆盖的范围是整个Payload,不包括包头。

短包没有CRC——只有ECC。为啥?短包总共才4字节,ECC用汉明码就能搞定单比特纠错和双比特检错,没必要上更重的CRC。说白了,ECC是轻型保安,CRC是重型装甲,各司其职。

我记得有一次在实验室验证一块新屏,发现偶尔闪屏。抓包发现个别长包的CRC校验失败,但ECC又没报错——后来排查是信号质量下降导致Payload比特翻转。加上CRC就原形毕露了。

完整包结构一览

下面这张图把短包和长包的完整结构放在一起对比,方便理解整体流程。

DSI 数据包结构概览 短包(4 字节固定) Data ID Data0/Data1 ECC ← 无 CRC,无负载 → 长包(可变长度) Data ID Word Count ECC Payload (可变) CRC EoT(LPS 等待) ECC:单比特纠错,双比特检错 CRC:多项式 0x1021,覆盖整个 Payload

注意:长包传输过程中,如果CRC校验失败,接收端通常不会重传——DSI没有链路层重传机制。错误包直接被丢弃,后续由应用层决定是否补偿(比如屏幕刷新时利用消隐区重发)。所以我建议在发送关键初始化数据时,最好连续发两遍,或者用短包封装校验反馈。

实际抓包中的一个避坑点

我曾经调试一个MIPI DSI发送器,发现长包发完后发送端自己先睡着了,导致EoT之后没有回到高速状态。接收端一直等,结果超时报错。后来在协议文档里看到一条说明:EoT的LPS长度有最低要求(通常≥50ns),但实际设计时最好给800ns以上的余量,省得碰上奇葩接收端。

短包虽然结构简单,但Data ID里的VC和DT组合一旦配错,接收端会直接忽略。我习惯在代码里用宏定义把常用的DT列成枚举,比如:

// 常用 Data Type 定义
#define DT_GENERIC_SHORT_WRITE_0  0x03
#define DT_GENERIC_SHORT_WRITE_1  0x13
#define DT_GENERIC_SHORT_WRITE_2  0x23
#define DT_DCS_LONG_WRITE         0x39

这样做的好处是,读代码时一眼就能看出包的用途,不用去翻协议手册查十六进制。嗯,算是个小习惯吧。

小结

DSI数据包的核心就三件事:包类型选对,包头字段填准,长包别忘了CRC和EoT。短包靠ECC保护,长包靠CRC保障。搞清楚了这些,调DSI链路时心里就有底了,至少能看懂示波器上抓的波形里哪个字节是干什么的。