数据帧格式
一条 CAN 报文在总线上究竟长什么样?逐字段拆解标准帧与扩展帧,理解 SOF、仲裁场、控制场、数据场、CRC、ACK 与 EOF,以及巧妙的位填充规则。
一条 CAN 报文在总线上究竟长什么样?逐字段拆解标准帧与扩展帧,理解 SOF、仲裁场、控制场、数据场、CRC、ACK 与 EOF,以及巧妙的位填充规则。
下图逐字段展示一个 11 位 ID 的标准数据帧,从 SOF 到 EOF 的全部组成。
| 字段 | 标准帧位数 | 扩展帧位数 | 取值/含义 |
|---|---|---|---|
| SOF 帧起始 | 1 | 1 | 固定显性(0) |
| 标识符 ID | 11 | 11(基础)+18(扩展)=29 | 报文标签 + 优先级 |
| RTR 远程请求 | 1 | 1(SRR 代替+末尾 RTR) | 0=数据帧 1=远程帧 |
| SRR 替代远程请求 | — | 1 | 扩展帧占位,隐性 |
| IDE 扩展标志 | 1 | 1 | 0=标准 1=扩展 |
| r0 / r1 保留位 | 1 | 2 | 发送显性 |
| DLC 长度码 | 4 | 4 | 0~8 字节 |
| 数据场 | 0~64 | 0~64 | 0~8 字节负载 |
| CRC 序列 | 15 | 15 | 循环冗余校验 |
| CRC 界定符 | 1 | 1 | 隐性 |
| ACK 槽 + 界定符 | 2 | 2 | 接收确认 |
| EOF 帧结束 | 7 | 7 | 7 个隐性位 |
标准帧 11 位 ID 提供 2048 个标识符,够绝大多数整车网络使用,帧更短、效率更高,是乘用车 CAN 的主流。扩展帧 29 位 ID 提供约 5 亿个标识符,主要用于需要结构化编码的商用车协议 J1939(把 29 位划分成优先级/PGN/源地址等字段,本教程第 13 章详解)。两者可在同一总线共存,靠 IDE 位区分。
CAN 是异步位流,接收方靠数据边沿再同步时钟。若数据恰好是一长串相同位(如 0x00 或 0xFF),就没有边沿可用。位填充强制插入跳变解决这一问题。
位填充使一帧的实际位数不固定——最坏情况下每 5 位插 1 位,帧会比理论值长约 20%。计算最坏传输时间和总线负载时必须考虑填充位。此外,填充位也参与错误检测:若接收方在应填充处看到第 6 个相同位,就触发"填充错误"(详见第 5 章)。这把位同步与错误检测巧妙地统一在一个机制里。
CAN 总线上流动的不只是数据帧,还有另外三种特殊帧,各司其职。
| 帧类型 | 作用 | 关键特征 |
|---|---|---|
| 数据帧 (Data Frame) | 携带实际数据从发送方广播到全网 | RTR=0,含 0~8 字节数据场 |
| 远程帧 (Remote Frame) | 请求某 ID 报文的持有者发送数据 | RTR=1,无数据场,DLC 指期望长度 |
| 错误帧 (Error Frame) | 检测到错误的节点主动破坏总线,通告出错 | 6 个显性位(主动错误)违反位填充,第 5 章详解 |
| 过载帧 (Overload Frame) | 接收方尚未处理完、请求延迟下一帧 | 格式类似错误帧,插在帧间隔期 |
远程帧(RTR=1)本意是"我想要某数据,请持有者发出来",但它存在语义歧义(多节点可能都持有同 ID)和实时性问题,现代车辆设计普遍改用周期性主动广播或 UDS 诊断请求来获取数据,远程帧已很少使用。许多 AUTOSAR 通信矩阵甚至明确禁用远程帧。理解它主要是为了看懂协议和历史代码。
在 STM32 HAL 中,一帧的头部与数据分别用结构体描述,字段与上面的物理帧一一对应:
/* STM32 HAL 中的 CAN 发送头结构(对应帧的仲裁场+控制场)*/ CAN_TxHeaderTypeDef txHeader; txHeader.StdId = 0x123; /* 11 位标准 ID(仲裁场)*/ txHeader.ExtId = 0; /* 扩展帧才用 29 位 ID */ txHeader.IDE = CAN_ID_STD; /* IDE=0 标准帧 */ txHeader.RTR = CAN_RTR_DATA; /* RTR=0 数据帧 */ txHeader.DLC = 8; /* 数据长度码:8 字节 */ txHeader.TransmitGlobalTime = DISABLE; uint8_t data[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; /* 数据场 */ /* SOF / CRC / ACK / EOF / 位填充 全部由 CAN 控制器硬件 自动生成和校验,软件只需填 ID、RTR、IDE、DLC 和数据 */ if (HAL_CAN_AddTxMessage(&hcan, &txHeader, data, &txMailbox) != HAL_OK) { /* 发送失败处理 */ }
一个 CAN 数据帧按时间顺序由这些字段组成:SOF(1 位显性,硬同步)→ 仲裁场(标准帧 11 位 ID+RTR,扩展帧 29 位 ID)→ 控制场(IDE 区分标准/扩展、r0 保留、4 位 DLC 数据长度)→ 数据场(0~8 字节负载)→ CRC 场(15 位校验+界定符)→ ACK 场(发送方发隐性、接收方覆写显性表示"有人收到")→ EOF(7 个隐性位)。关键控制位:RTR=0 数据帧/1 远程帧,IDE=0 标准/1 扩展,显性优先决定同 ID 竞争胜负。位填充规定连续 5 个相同位后插入一个相反位(作用于 SOF~CRC 序列),既保证位时钟再同步,又参与错误检测。总线上有数据帧、远程帧、错误帧、过载帧四种帧,其中远程帧因语义问题已渐被弃用。在 STM32 HAL 中,软件只需填 ID/RTR/IDE/DLC 和数据字节,SOF/CRC/ACK/EOF 和位填充全部由控制器硬件自动完成。