4. XCP传输层协议
XCP 协议的核心,说白了就是主从设备之间来回倒腾数据包。你想想看,标定工程师通过上位机发一条指令,ECU 得能听懂,还得能回话。这个“听懂”和“回话”靠的就是三种数据包:CMD/CRM(命令/响应)、DAQ(数据采集)和 STIM(数据刺激)。
我刚开始接触 XCP 时,总觉得这些包格式很抽象。后来在台架上调一个发动机扭矩模型,上位机死活收不到标定值,查了两天才发现是 CMD 包的 PID 字段写错了。嗯,从那以后,我对每个字节都不敢马虎。
4.1 数据包通用头部
不管哪种包,XCP 都有一套统一的头部结构。就像快递单一样,收件人、寄件人、包裹类型都得写清楚。
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | PID | 1 | 包标识符,区分 CMD/CRM/DAQ/STIM |
| 1 | 填充/保留 | 0-1 | 对齐用,某些传输层需要 |
| 2+ | 载荷 | 可变 | 具体命令或数据内容 |
这个 PID 是关键。它告诉接收方:“我这是个啥包”。比如 0xFF 是 CONNECT 命令,0xFE 是 DISCONNECT。我习惯把 PID 表打印出来贴在工位上,查起来快。
4.2 CMD/CRM 包:一问一答
CMD 包是主设备(上位机)发给从设备(ECU)的。CRM 包是从设备回给主设备的。它们俩成对出现,就像你问“今天天气咋样?”,对方回“还行,25度”。
4.2.1 CMD 包结构
+--------+--------+------------------+
| PID | 保留 | 参数/数据 |
+--------+--------+------------------+
1 byte 0-1 byte 0-8 bytes
- PID:命令标识符,比如
0xF0表示 SHORT_UPLOAD(短上传)。 - 保留:有些传输层要求对齐到 4 字节,这里就填 0。
- 参数/数据:比如上传的地址、长度等。
举个例子,我想从 ECU 的 0x8000 地址读 4 个字节,CMD 包就是:
PID = 0xF0 (SHORT_UPLOAD)
保留 = 0x00
参数 = 0x00 0x80 0x00 0x04 // 地址高字节在前,长度在后
4.2.2 CRM 包结构
+--------+--------+------------------+
| PID | 保留 | 响应数据 |
+--------+--------+------------------+
1 byte 0-1 byte 0-8 bytes
CRM 包的 PID 和 CMD 的 PID 有对应关系。比如 CMD 是 0xF0,CRM 就是 0xFF(成功)或 0xFE(错误)。
响应数据里放的就是你要读的值。上面那个例子,ECU 会回:
PID = 0xFF (成功)
保留 = 0x00
数据 = 0x12 0x34 0x56 0x78 // 从 0x8000 读到的 4 个字节
0xFE,后面会跟一个错误码。我曾经遇到过 ECU 返回 0xFE 0x10,查手册才知道是“命令不支持”。别慌,对照协议文档逐条排查。
4.3 DAQ 包:数据主动上报
DAQ 包和 CMD/CRM 不一样。它不是一问一答,而是 ECU 按照设定好的周期,主动往上位机“推”数据。这玩意儿在标定和测量时特别有用——你不需要一直发命令去读,ECU 自己会定时发。
4.3.1 DAQ 包结构
+--------+--------+------------------+------------------+
| PID | 保留 | DAQ列表号 | 数据记录 |
+--------+--------+------------------+------------------+
1 byte 0-1 byte 1 byte N bytes
- PID:固定为
0x00(DAQ 包标识)。 - DAQ 列表号:标识这是哪个 DAQ 列表的数据。每个列表可以配置不同的采集周期和信号。
- 数据记录:按照预先配置好的格式,把多个信号的值打包在一起。
我举个例子。假设你配置了 DAQ 列表 0,里面包含发动机转速和冷却液温度。ECU 每 10ms 发一次 DAQ 包:
PID = 0x00
保留 = 0x00
DAQ列表号 = 0x00
数据 = 0x0A 0x00 0x1E // 转速 0x0A00(2560 rpm),温度 0x1E(30°C)
4.2.2 DAQ 的两种模式
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 周期模式 | ECU 按固定时间间隔发送 DAQ 包 | 连续测量,比如监控发动机运行状态 |
| 事件模式 | ECU 在特定事件触发时发送 DAQ 包 | 故障诊断,比如检测到爆震时上报数据 |
我个人更喜欢周期模式,简单可靠。事件模式虽然灵活,但调试时容易漏包,你想想看,如果事件触发条件没满足,数据就永远发不出来。
4.4 STIM 包:数据注入
STIM 包和 DAQ 包正好相反。DAQ 是 ECU 往外发数据,STIM 是上位机往 ECU 里写数据。这玩意儿在标定参数调整时特别有用——你可以实时修改 ECU 内部的标定量,比如喷油脉宽、点火提前角。
4.4.1 STIM 包结构
+--------+--------+------------------+------------------+
| PID | 保留 | STIM列表号 | 数据记录 |
+--------+--------+------------------+------------------+
1 byte 0-1 byte 1 byte N bytes
- PID:固定为
0x01(STIM 包标识)。 - STIM 列表号:标识这是哪个 STIM 列表的数据。
- 数据记录:要写入 ECU 的信号值。
比如你想把喷油脉宽从 2.5ms 改成 3.0ms,STIM 包就是:
PID = 0x01
保留 = 0x00
STIM列表号 = 0x00
数据 = 0x0A 0x00 // 假设喷油脉宽用 0.1ms 为单位,3.0ms = 30 = 0x001E
4.5 三种数据包的关系
为了让你更直观地理解这三种包怎么配合工作,我画了一张图:
4.6 数据包长度与对齐
XCP 协议对数据包长度有要求。不同的传输层(CAN、以太网、SPI)支持的包长度不一样。比如 CAN 传输层,一帧最多 8 字节数据;以太网传输层可以到 64KB 甚至更大。
| 传输层 | 最大包长(字节) | 对齐要求 |
|---|---|---|
| CAN | 8 | 无 |
| CAN FD | 64 | 4 字节对齐 |
| 以太网(TCP/UDP) | 65535 | 4 字节对齐 |
| SPI | 可变 | 2 字节对齐 |
我建议你在设计 DAQ 列表时,尽量让数据长度对齐到 4 字节。这样 ECU 处理起来快,上位机解析也方便。如果信号长度不对齐,比如一个 3 字节的信号,后面就得补 1 字节的填充。
4.7 常见问题与排查思路
在实际项目中,数据包解析出问题是最常见的。我总结了几条排查思路:
- PID 对不上:检查上位机和 ECU 的协议版本是否一致。XCP 1.0 和 XCP 1.1 的 PID 定义有差异。
- 长度不匹配:用逻辑分析仪抓包,看实际收到的字节数和预期是否一致。我遇到过 ECU 多发了 2 字节填充,导致解析错位。
- DAQ 包没收到:先确认 DAQ 列表是否已经启动(用 GET_DAQ_CLOCK 命令检查)。再确认 ECU 的采集周期是否配置正确。
- STIM 包写不进去:检查 STIM 列表的访问权限。有些标定量是只读的,需要先解锁安全模式。
嗯,数据包格式这块,说白了就是“协议规定怎么写,你就怎么写”。但实际调试时,多留个心眼,多抓包看看,比什么都管用。