本文系统介绍了CAN总线的基础知识、报文设计、信号定义、多网络架构以及开发实践。主要内容包括:
文章提供了8周系统学习路线,涵盖从基础到高级的CAN知识体系,并附有快速参考表与常见问题解答,适合汽车电子工程师系统学习CAN总线技术。
CAN总线完整学习框架
第一部分:CAN基础知识
第一章:CAN总线概述
1.1 什么是CAN?
CAN(Controller Area Network,控制器局域网) 是由德国博世(Bosch)公司在1986年开发的一种串行通信协议,最初用于汽车内部电子控制单元(ECU)之间的通信。
核心特点:
- 🚗 多主机:任何节点都可以发起通信
- 🔄 广播式:一个节点发送,所有节点接收
- ⚡ 实时性强:基于优先级的仲裁机制
- 🛡️ 可靠性高:强大的错误检测和处理机制
- 💰 成本低:仅需双绞线,布线简单
1.2 CAN总线的应用领域
| 汽车 | 发动机控制、ABS、车身控制 | 最广泛应用 |
| 工业 | 工厂自动化、机器人控制 | 抗干扰强 |
| 医疗 | 医疗设备互联 | 高可靠性 |
| 航空 | 飞行控制系统 | 安全关键 |
| 新能源 | 电池管理系统(BMS) | 实时性要求高 |
1.3 CAN标准演进
CAN 2.0A (1991) → 标准帧 (11位ID)
CAN 2.0B (1995) → 扩展帧 (29位ID)
CAN FD (2012) → 灵活数据速率
CAN XL (2023) → 超长数据帧
第二章:物理层基础
2.1 总线拓扑
线性总线结构:
120Ω 120Ω
终端电阻 │ │ 终端电阻
│ │
─────┴──────┬──────┬──────┬──────┬─────────┴─────
CAN_H │ │ │ │ CAN_H
│ │ │ │
CAN_L ─┘ ECU1 ECU2 ECU3 CAN_L
最大节点数:理论无限,实际30-100个
最大线长:取决于波特率
2.2 电平与信号
差分信号:CAN_H 和 CAN_L
| 0 | 显性(Dominant) | 3.5V | 1.5V | 2.0V |
| 1 | 隐性(Recessive) | 2.5V | 2.5V | 0V |
仲裁规则:显性覆盖隐性
节点A发送:隐性(1)
节点B发送:显性(0) ← 胜出
总线实际:显性(0)
2.3 波特率与距离
| 1 Mbps | 40m | 高速CAN(动力) |
| 500 Kbps | 100m | 高速CAN(底盘) |
| 250 Kbps | 200m | 中速CAN |
| 125 Kbps | 500m | 低速CAN(车身) |
速率与距离关系:
- 速率越高 → 距离越短(信号衰减)
- 距离越长 → 需要降低速率
第三章:CAN报文帧格式
3.1 CAN标准数据帧结构(重点)⭐⭐⭐⭐⭐
完整帧结构图:
┌─────────────────────────────────────────────────────────────────┐
│ CAN标准数据帧 (最多130位) │
├──────────┬─────────┬────────┬──────┬─────┬─────┬─────┬──────────┤
│ 帧起始 SOF │ 仲裁场 │ 控制场 │ 数据场 │ CRC场│ ACK场│ EOF │ 帧间隔IFS │
│ 1位 │ 12位 │ 6位 │ 0-64位│ 16位│ 2位│ 7位 │ 3位 │
└──────────┴─────────┴────────┴──────┴─────┴─────┴─────┴──────────┘
3.2 各字段详细解析
字段1:帧起始(SOF – Start of Frame)
- 长度:1位
- 值:显性电平(0)
- 作用:
- 标志一帧的开始
- 所有节点同步总线时钟
- 唤醒处于空闲状态的节点
字段2:仲裁场(Arbitration Field)
标准格式共12位 = 11位ID + 1位RTR
2.1 标识符(Identifier,11位)
- 作用:决定报文优先级(数值越小,优先级越高)
- 范围:0x000(最高优先级)~ 0x7FF(最低优先级)
- 实例:
ID = 0x123 (二进制: 00100100011)
优先级高于 ID = 0x456
常见汽车CAN ID分配:
| 0x000-0x0FF | 高优先级控制信号 | 发动机扭矩请求 |
| 0x100-0x1FF | 安全相关信号 | ABS状态 |
| 0x200-0x3FF | 动力系统 | 发动机转速、车速 |
| 0x400-0x5FF | 车身控制 | 车门状态、灯光 |
| 0x600-0x7FF | 信息娱乐 | 多媒体控制 |
2.2 RTR位(Remote Transmission Request)
- 长度:1位
- 值:
- 0(显性):数据帧
- 1(隐性):远程帧(请求数据)
字段3:控制场(Control Field)
共6位 = IDE(1) + r0(1) + DLC(4)
3.1 IDE位(Identifier Extension)
- 0:标准帧(11位ID)
- 1:扩展帧(29位ID)
3.2 r0位(保留位)
- 值:必须为0(显性)
- 作用:预留给未来协议扩展
3.3 DLC(Data Length Code,数据长度码)
- 长度:4位
- 值:0-8
- 作用:指示数据场字节数
DLC二进制 十进制 数据字节数
0000 0 0字节
0001 1 1字节
…
1000 8 8字节
1001-1111 保留/非法
注意:CAN FD可以支持更多字节(12/16/20/24/32/48/64字节)
字段4:数据场(Data Field)
- 长度:0-8字节(0-64位)
- 作用:承载实际传输的数据
- 字节顺序:通常采用大端序(Big-Endian)
示例:发动机转速报文
ID: 0x201
DLC: 8
数据: 0C 80 1F 40 00 00 00 00
解析:
字节0-1: 0C80 (大端) = 3200 RPM
字节2-3: 1F40 (大端) = 8000 (节气门开度×100)
字节4-7: 保留
字段5:CRC场(Cyclic Redundancy Check)
共16位 = CRC序列(15) + CRC界定符(1)
5.1 CRC序列(15位)
- 作用:错误检测
- 计算:对SOF到数据场末尾的所有位进行CRC-15计算
- 多项式:x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
5.2 CRC界定符(1位)
- 值:1(隐性)
- 作用:分隔CRC序列和ACK场
错误检测能力:
- ✅ 检测所有单比特错误
- ✅ 检测所有双比特错误
- ✅ 检测所有奇数个比特错误
- ✅ 检测所有突发长度≤15的错误
字段6:ACK场(Acknowledgment Field)
共2位 = ACK槽(1) + ACK界定符(1)
6.1 ACK槽(1位)
- 发送节点:发送隐性位(1)
- 接收节点:收到正确后,覆盖为显性位(0)
- 作用:确认至少有一个节点正确接收
工作原理:
发送节点:发送隐性(1)
↓
接收节点:覆盖显性(0) ← 表示收到
↓
发送节点:检测到显性(0) → 发送成功
6.2 ACK界定符(1位)
- 值:1(隐性)
- 作用:标志ACK场结束
字段7:帧结束(EOF – End of Frame)
- 长度:7位
- 值:全为隐性(1111111)
- 作用:标志一帧的结束
字段8:帧间隔(IFS – Interframe Space)
- 长度:3位(最少)
- 值:全为隐性(111)
- 作用:
- 分隔连续的帧
- 给节点处理时间
- 错误节点在此期间不能发送
3.3 扩展帧格式
扩展帧ID(29位):
- 结构:基本ID(11位) + SRR(1位) + IDE(1位) + 扩展ID(18位) + r1(1位)
- 总ID范围:0x00000000 – 0x1FFFFFFF
- 可用ID数量:536,870,912个
- 应用:J1939协议、CANopen、现代汽车网络
29位扩展ID结构(J1939为例):
29位ID = 优先级(3) + 保留(1) + 数据页(1) + PDU格式(8) + PDU特定(8) + 源地址(8)
示例:0x0CF00400
二进制:00001100 11110000 00000100 00000000
───┬─── ──┬──┬── ──┬───── ──┬─────
│ │ │ │ │
优先级 R DP PF PS SA
3 0 0 0xF0 0x04 0x00
第四章:仲裁机制
4.1 什么是仲裁?
当多个节点同时发送时,CAN通过 非破坏性位仲裁 决定谁优先。
4.2 仲裁过程
规则:ID值越小,优先级越高
示例:3个节点同时发送
位序号: 10 9 8 7 6 5 4 3 2 1 0 RTR
节点A: 0 0 1 0 0 1 0 0 0 1 1 0 (ID=0x123)
节点B: 0 0 1 0 0 1 0 1 1 0 0 0 (ID=0x12C)
节点C: 0 0 1 0 0 0 1 0 0 0 1 0 (ID=0x111)
↓ ↓ ↓ ↓ ↓ ↓
总线实际: 0 0 1 0 0 0 …
↑
节点A、B检测到总线为0,但自己发送1
退出竞争,停止发送
结果:节点C胜出(ID=0x111最小)
关键点:
4.3 优先级设计原则
汽车CAN网络示例:
0x001 – 发动机紧急停机请求 (最高优先级)
0x100 – ABS激活信号
0x200 – 发动机转速
0x300 – 车速
0x400 – 空调状态
0x7FF – 诊断响应 (最低优先级)
设计建议:
- ✅ 安全关键信号 → 低ID
- ✅ 实时性要求高 → 低ID
- ✅ 信息类数据 → 高ID
- ✅ 诊断数据 → 最高ID
第五章:错误检测机制
5.1 五种错误类型
| 位错误 | Bit Error | 发送位≠读取位 | 发送节点 |
| 填充错误 | Stuff Error | 违反位填充规则 | 所有节点 |
| CRC错误 | CRC Error | CRC校验失败 | 接收节点 |
| 格式错误 | Form Error | 固定位格式错误 | 所有节点 |
| 应答错误 | ACK Error | 无节点应答 | 发送节点 |
5.2 位填充(Bit Stuffing)
规则:连续5个相同位后,自动插入1个相反位
示例:
原始数据: 1 1 1 1 1 0 0 0 0 0 0
↓插入 ↓插入
填充后: 1 1 1 1 1 0 0 0 0 0 0 1
解释:
– 5个1后插入0
– 6个0后插入1
作用:
- 保证足够的电平跳变
- 便于时钟同步
- 防止总线长时间保持同一电平
应用范围:
- ✅ SOF到CRC序列
- ❌ CRC界定符到EOF(不填充)
5.3 错误帧格式
当节点检测到错误时,立即发送错误帧:
┌─────────────────────────────────┐
│ 错误帧 │
├──────────────┬──────────────────┤
│ 错误标志 │ 错误界定符 │
│ 6-12位 │ 8位隐性 │
└──────────────┴──────────────────┘
错误标志:
- 主动错误:6个连续显性位(违反位填充规则)
- 被动错误:6个连续隐性位
后果:
- 当前帧被破坏
- 所有节点丢弃该帧
- 发送节点稍后自动重传
5.4 错误计数与节点状态
每个CAN节点维护两个计数器:
发送错误计数器(TEC)
- 发送失败 → +8
- 发送成功 → -1
接收错误计数器(REC)
- 接收失败 → +1
- 接收成功 → -1
节点状态机:
TEC≤127 && REC≤127
┌─────────────────────────┐
│ 主动错误状态 │
│ (Error Active) │
└─────────────────────────┘
│
TEC或REC >127
↓
┌─────────────────────────┐
│ 被动错误状态 │
│ (Error Passive) │
└─────────────────────────┘
│
TEC >255
↓
┌─────────────────────────┐
│ 总线关闭状态 │
│ (Bus Off) │
└─────────────────────────┘
状态说明:
| 主动错误 | TEC≤127 && REC≤127 | 正常工作,发送主动错误标志 |
| 被动错误 | TEC或REC >127 | 降级工作,发送被动错误标志 |
| 总线关闭 | TEC >255 | 断开总线,停止一切通信 |
5.5 位定时
位时间划分:
┌────┬────┬─────┬──────┐
│同步段│传播段│相位段1│相位段2│
│1TQ │1-8TQ│1-8TQ │1-8TQ │
└────┴────┴─────┴──────┘
↑ 采样点
TQ = Time Quantum(时间量子)
典型配置(500Kbps):
位时间 = 2μs
TQ = 0.125μs
1位 = 16TQ
SS = 1TQ
PTS = 2TQ
PH1 = 5TQ
PH2 = 8TQ
采样点 = 50%
第二部分:报文与信号设计
第六章:报文ID设计
6.1 ID的完整含义
完整理解:
ID = 网络标识 + 功能域标识 + 内容标识 + 优先级
6.2 多CAN网络架构
实际车辆中的多CAN网络:
text
整车CAN网络拓扑
│
├── 动力CAN (Powertrain CAN)
│ ├── 波特率: 500 Kbps
│ ├── 节点: ECM, TCM, BMS, MCU
│ └── ID范围: 0x000-0x3FF
│
├── 底盘CAN (Chassis CAN)
│ ├── 波特率: 500 Kbps
│ ├── 节点: ABS, ESP, EPS
│ └── ID范围: 0x000-0x3FF
│
├── 车身CAN (Body CAN)
│ ├── 波特率: 125 Kbps
│ ├── 节点: BCM, PEPS, 车门模块
│ └── ID范围: 0x000-0x5FF
│
├── 座舱CAN (Cockpit CAN)
│ ├── 波特率: 500 Kbps
│ ├── 节点: IVI, IC, HUD, ADAS
│ └── ID范围: 0x000-0x4FF
│
└── 诊断CAN (Diagnostic CAN)
├── 波特率: 500 Kbps
├── 节点: 所有ECU
└── ID范围: 0x700-0x7FF
6.3 ID分配策略
优先级导向分配:
| 0x000-0x0FF | 最高 | 紧急制动、安全气囊 | 事件触发 |
| 0x100-0x1FF | 高 | 实时控制(ABS、ESP) | 5-20ms |
| 0x200-0x3FF | 中高 | 状态监控(转速、车速) | 20-100ms |
| 0x400-0x6FF | 中低 | 舒适功能(空调、车窗) | 100-1000ms |
| 0x700-0x7FF | 最低 | 诊断(OBD-II) | 事件触发 |
功能域分组示例:
text
动力CAN:
0x100-0x1FF: BMS(电池管理)
0x200-0x2FF: MCU(电机控制)
0x300-0x3FF: VCU(整车控制)
0x400-0x4FF: OBC/DCDC(充电/电源)
车身CAN:
0x100-0x1FF: BCM(车身控制)
0x200-0x2FF: 车门/车窗
0x300-0x3FF: 座椅
0x400-0x4FF: 空调
0x500-0x5FF: 灯光
6.4 CAN网络标识方法
方法一:通过报文属性标识(推荐)⭐⭐⭐⭐⭐
在Excel Matrix中增加"CAN网络"列:
| 0x100 | Powertrain_CAN | PT | PT_BMS_1_Voltage | 8 | 10 | BMS | VCU,MCU | →Cockpit | D |
| 0x101 | Powertrain_CAN | PT | PT_BMS_2_SOC | 8 | 100 | BMS | VCU | →Cockpit | B |
| 0x100 | Body_CAN | BODY | BODY_BCM_1_Door | 4 | 100 | BCM | IC | →Cockpit(0x500) | QM |
优点:
- ✅ 直观清晰
- ✅ 便于人工查看
- ✅ 易于管理维护
方法二:通过报文命名前缀(辅助方法)⭐⭐⭐⭐
命名规范:
text
格式: [网络缩写]_[发送者]_[序号]_[功能描述]
示例:
PT_BMS_1_Voltage_Current (Powertrain CAN)
PT_MCU_1_Motor_Status (Powertrain CAN)
BODY_BCM_1_Door_Status (Body CAN)
CHAS_ABS_1_Wheel_Speed (Chassis CAN)
COCK_IC_1_Display_Request (Cockpit CAN)
网络缩写标准:
| Powertrain_CAN | PT | 动力总成 |
| Chassis_CAN | CHAS | 底盘 |
| Body_CAN | BODY | 车身 |
| Cockpit_CAN | COCK | 座舱 |
| Diagnostic_CAN | DIAG | 诊断 |
第七章:报文属性定义
7.1 报文核心属性列表
完整报文定义表结构:
| 报文ID(Hex) | Message ID (Hex) | 文本 | 0x100 | 十六进制表示 |
| 报文ID(Dec) | Message ID (Dec) | 整数 | 256 | 十进制表示 |
| 报文名称 | Message Name | 文本 | BMS_1_Voltage_Current | 唯一标识符 |
| CAN网络 | CAN Network | 枚举 | Powertrain_CAN | 所属物理CAN总线 ⭐ |
| 网络缩写 | Network Abbr | 文本 | PT_CAN | 便于标识 |
| DLC | Data Length Code | 整数(0-8) | 8 | 数据字节数 |
| 周期(ms) | Cycle Time (ms) | 整数 | 10 | 发送周期,0表示事件触发 |
| 发送类型 | Send Type | 枚举 | Cyclic | Cyclic/Event/Cyclic+Event |
| 发送者 | Sender | 文本 | BMS | 发送节点名称 |
| 接收者 | Receiver | 文本列表 | VCU,MCU,IC | 逗号分隔 |
| 跨网关转发 | Gateway Routing | 文本 | →Cockpit_CAN | 是否需要网关转发 ⭐ |
| ASIL等级 | ASIL Level | 枚举 | ASIL-D | QM/A/B/C/D |
| 安全机制 | Safety Mechanism | 文本 | CRC+Counter | CRC/Rolling Counter/E2E |
| 启动延迟(ms) | Start Delay | 整数 | 0 | 上电后首次发送延迟 |
| 超时时间(ms) | Timeout | 整数 | 50 | 接收超时判定时间 |
| 信号数量 | Signal Count | 整数 | 4 | 包含的信号个数 |
| 描述 | Description | 文本 | 电池组核心数据 | 功能说明 |
7.2 DLC(数据长度码)
定义: 报文数据场的字节数
取值范围:
text
CAN 2.0: 0-8 字节
CAN FD: 0-8, 12, 16, 20, 24, 32, 48, 64 字节
DLC编码(CAN FD):
0-8: 实际字节数
9: 12字节
10: 16字节
11: 20字节
12: 24字节
13: 32字节
14: 48字节
15: 64字节
设计原则:
text
✅ 够用即可,避免浪费
✅ 考虑未来扩展,预留1-2字节
❌ 不要为了"好看"统一用8字节
7.3 周期(Cycle Time)
定义: 报文发送的时间间隔,单位:毫秒(ms)
典型值:
text
实时控制回路: 5-20ms (如电机控制、制动)
状态监控: 50-100ms (如温度、压力)
慢速状态: 500-1000ms (如门窗状态)
事件触发: 0 (按需发送)
设计原则:
text
安全相关: ≤20ms
实时性要求高: ≤50ms
一般状态信息: 100-500ms
慢变量: ≥1000ms
公式:
周期 ≤ 1/(2×最快变化频率) [奈奎斯特采样定理]
7.4 发送类型(Send Type)
| 周期发送 | Cyclic | 固定周期持续发送 | 状态监控、实时控制 |
| 事件触发 | Event | 事件发生时发送 | 故障报警、用户操作 |
| 周期+事件 | Cyclic + Event | 周期发送+变化时立即发送 | 重要状态(如档位) |
示例:
text
周期发送 (Cyclic):
– 电池电压:每10ms发送
– 车速:每20ms发送
事件触发 (Event):
– 故障码:故障发生时立即发送
– 诊断响应:收到请求后发送
周期+事件 (Cyclic + Event):
– 档位信号:
* 正常:100ms周期发送
* 档位切换时:立即发送
7.5 ASIL等级
定义: Automotive Safety Integrity Level (汽车安全完整性等级),来自ISO 26262标准
等级划分:
| QM | Quality Management | 非安全相关 | 信息娱乐、舒适功能 |
| ASIL-A | 最低 | 辅助功能 | 后视镜、雨刷 |
| ASIL-B | 低 | 次要安全 | SOC显示、灯光控制 |
| ASIL-C | 中 | 重要安全 | 车速、档位 |
| ASIL-D | 最高 | 关键安全 | 制动、转向、电池保护 |
ASIL等级对Matrix的影响:
text
ASIL-D 报文要求:
✅ 必须有安全机制(CRC+AliveCounter或E2E)
✅ 周期≤20ms
✅ 超时监控≤3×周期
✅ 发送确认机制
✅ 完整的故障检测覆盖
7.6 安全机制
常用机制:
| CRC | 循环冗余校验 | 4-8位 | B/C/D |
| AliveCounter | 滚动计数器 | 4位 | B/C/D |
| CRC+AliveCounter | 组合 | 12位(1.5字节) | C/D |
| E2E Profile | AUTOSAR端到端保护 | 根据配置 | D |
E2E (End-to-End) 保护配置文件:
text
E2E Profile 01 (最常用):
– CRC: 8位
– Counter: 4位
– DataID: 16位(隐含)
开销:12位
适用:ASIL-D,数据长度≤8字节
字节布局示例:
Byte0: [Counter(4bit)] [CRC(4bit高位)]
Byte1: [CRC(4bit低位)] [Data…]
第八章:信号定义
8.1 信号属性全集
完整信号定义表结构:
| 位置 | 信号名称 | 唯一标识 | Pack_Voltage |
| 所属报文ID | 对应报文 | 0x100 | |
| 起始位(Start Bit) | 0-63 | 0 | |
| 位长度(Bit Length) | 1-64 | 16 | |
| 字节序(Byte Order) | Intel/Motorola | Intel | |
| 数值 | 值类型(Value Type) | Unsigned/Signed/Float | Unsigned |
| 分辨率(Resolution) | 原始值→物理值的乘数 | 0.1 | |
| 偏移量(Offset) | 原始值→物理值的加数 | 0 | |
| 最小值/最大值 | 物理值范围 | 0 / 800 | |
| 单位(Unit) | V/A/rpm/°C等 | V | |
| 初始值 | 上电默认值 | 0 | |
| 无效值 | 表示信号失效的值 | 0xFFFF | |
| 安全 | ASIL等级 | 继承或独立定义 | ASIL-D |
| 其他 | 数据类型 | C语言类型 | uint16_t |
| 精度要求 | 测量精度 | ±0.5% | |
| 描述/备注 | 功能说明 | 电池组总电压 |
物理值计算公式:
text
物理值 = 原始值 × 分辨率 + 偏移量
Physical_Value = Raw_Value × Factor + Offset
8.2 起始位(Start Bit / Offset)
定义: 信号在报文数据场中的起始位位置
Intel格式(小端,推荐):
text
Byte 0: Bit 7 6 5 4 3 2 1 0
Byte 1: Bit 15 14 13 12 11 10 9 8
Byte 2: Bit 23 22 21 20 19 18 17 16
特点: 低字节在前,低位在右
Motorola格式(大端):
text
Byte 0: Bit 0 1 2 3 4 5 6 7
Byte 1: Bit 8 9 10 11 12 13 14 15
特点: 高字节在前,高位在左
设计建议:
- ✅ 新项目统一用Intel
- ✅ 信号尽量字节对齐(起始位=0,8,16,24…)
- ✅ 同一报文内信号连续排列,无间隙
- ❌ 避免不必要的跨字节信号
8.3 位长度(Bit Length)
常用长度:
| 1 | 0-1 | – | 布尔值(开关状态) |
| 2 | 0-3 | – | 枚举(如档位P/R/N/D) |
| 4 | 0-15 | -8~7 | 小范围枚举、计数器 |
| 8 | 0-255 | -128~127 | 百分比、温度 |
| 16 | 0-65535 | -32768~32767 | 电压、电流、转速 |
| 32 | 0-4294967295 | ±2147483648 | 里程、能量累计 |
长度选择原则:
text
公式:
所需位数 = ceil(log2((最大值-最小值)/分辨率 + 1))
示例:电池电压
范围:0-1000V
分辨率:0.1V
数据点数:(1000-0)/0.1 + 1 = 10001
所需位数:log2(10001) = 13.29 → 14位
但实际选择16位(2字节),原因:
✅ 字节对齐
✅ 有扩展余量
✅ 便于处理
8.4 字节序(Byte Order)
Intel (Little Endian,小端序):
text
特点:低字节存储在低地址
示例:数值0x1234存储为
内存地址: [低地址] [高地址]
存储内容: 0x34 0x12
优点:
✅ 现代处理器(x86/ARM)原生支持
✅ 主流车厂采用
示例代码:
uint16_t value = 0x1234;
uint8_t data[2];
memcpy(data, &value, 2);
// data[0] = 0x34 (低字节)
// data[1] = 0x12 (高字节)
Motorola (Big Endian,大端序):
text
特点:高字节存储在低地址
示例:数值0x1234存储为
内存地址: [低地址] [高地址]
存储内容: 0x12 0x34
优点:
✅ 符合人类阅读习惯
✅ J1939等协议采用
示例代码:
uint16_t value = 0x1234;
uint8_t data[2];
data[0] = (value >> 8) & 0xFF; // 0x12
data[1] = value & 0xFF; // 0x34
选择建议:
text
新项目: 推荐Intel(主流)
商用车(J1939): 必须Motorola
混合网络: 禁止!必须统一
8.5 值类型(Value Type)
| Unsigned | 无符号整数 | 0-65535 | 电压、SOC、速度 |
| Signed | 有符号整数 | -32768~32767 | 电流、扭矩、温度 |
| IEEE Float | 32位浮点数 | ±3.4×10^38 | 高精度计算(少用) |
设计建议:
text
✅ 优先使用Unsigned/Signed整数 + 因子/偏移
原因:效率高、确定性好、便于调试
❌ 尽量避免浮点数
原因:
– 不同平台可能有差异
– 无法直接用CAN工具查看
– 效率低
8.6 分辨率/因子(Resolution/Factor)
定义: 原始值转换为物理值的乘数
因子选择原则:
text
1. 精度够用即可
车速:0.01 km/h
电压:0.1 V
2. 优先选择2的幂次(便于位运算)
推荐:0.001, 0.125, 0.25, 0.5, 1, 2, 4, 8…
避免:0.3, 0.7等(浮点运算误差)
3. 参考行业惯例
温度:1°C或0.1°C
电压:0.1V或0.01V
电流:0.1A
转速:1rpm
典型因子表:
| 电压 | 0.1 V | 0-6553.5 V | 覆盖高压电池 |
| 电流 | 0.1 A | 需配合偏移 | 见下文 |
| 温度 | 1 °C | 配合偏移-40 | -40~215°C |
| 转速 | 1 rpm | 0-65535 rpm | 足够精度 |
| 车速 | 0.01 km/h | 0-655.35 km/h | 高精度 |
| SOC | 0.5 % | 0-127.5 % | 200个刻度 |
8.7 偏移量(Offset)
定义: 原始值转换为物理值的加数
主要用途:
text
1. 支持负值范围
原始值(无符号) + 偏移 → 物理值(可为负)
2. 调整零点
传感器输出不从0开始
3. 扩展范围
在有限位数下表示更大范围
示例1:电池电流
text
需求:
– 放电最大: 1000A (负值)
– 充电最大: 500A (正值)
– 精度: 0.1A
优化方案(使用偏移):
– 长度: 16位无符号
– 因子: 0.1
– 偏移: -1000
– 原始值范围: 0~15000
– 物理范围: -1000~500 A ✅
计算:
放电1000A: Raw = (-1000 – (-1000)) / 0.1 = 0
充电500A: Raw = (500 – (-1000)) / 0.1 = 15000
示例2:温度
text
需求:覆盖 -40°C ~ 215°C
方案:
– 长度: 8位无符号
– 因子: 1
– 偏移: -40
– 原始值: 0~255
– 物理范围: -40~215°C ✅
计算:
-40°C: Raw = (-40 – (-40)) / 1 = 0
25°C: Raw = (25 – (-40)) / 1 = 65
215°C: Raw = (215 – (-40)) / 1 = 255
8.8 最小值/最大值
定义: 信号的物理值有效范围
设计原则:
text
理论最大值 = ((2^BitLength – 1) × Factor) + Offset
但定义的最大值可以 ≤ 理论最大值
原因:
– 物理限制
– 安全考虑
– 传感器量程
示例:
text
信号:Pack_Voltage
– 长度: 16位
– 因子: 0.1
– 偏移: 0
– 值类型: 无符号
理论范围:
最大 = (2^16 – 1) × 0.1 + 0 = 6553.5 V
定义范围:
最小值: 0 V
最大值: 800 V (实际系统额定750V,留余量)
无效值: 0xFFFF (6553.5 V) → 超出定义范围,表示信号无效
8.9 单位(Unit)
标准单位表:
| 电压 | V | V, mV, kV | 高压用kV |
| 电流 | A | A, mA | – |
| 功率 | W | W, kW | 电机功率用kW |
| 能量 | J | kWh, Wh | 电池容量用kWh |
| 温度 | K | °C | 汽车行业用°C |
| 速度 | m/s | km/h, mph | 汽车用km/h |
| 转速 | rad/s | rpm | 电机用rpm |
| 扭矩 | Nm | Nm | – |
| 压力 | Pa | bar, psi, kPa | 轮胎用bar |
| 百分比 | % | % | SOC、开度等 |
8.10 初始值(Initial Value)
定义: ECU上电或报文首次发送时的默认值
设计原则:
text
1. 选择安全状态
制动踏板:0% (未踩)
加速踏板:0% (未踩)
2. 选择合理状态
SOC:50% (避免过充/过放报警)
温度:25°C (常温)
3. 避免触发故障
不要选择会触发报警的值
第三部分:CAN Matrix设计
第九章:CAN Matrix概述
9.1 什么是CAN Matrix?
定义:
text
CAN Matrix = 完整的CAN网络描述文档
包含: 节点 + 报文 + 信号 + 关系
三层结构:
text
CAN网络
├── 节点层(Nodes)
│ ├── ECU1, ECU2, ECU3…
│
├── 报文层(Messages)
│ ├── Message1 (ID, 周期, 发送者, CAN网络)
│ └── Message2
│
└── 信号层(Signals)
├── Signal1 (起始位, 长度, 因子…)
└── Signal2
9.2 Matrix表现形式
| DBC文件 | .dbc | 工具自动化 | ⭐⭐⭐⭐⭐ |
| Excel表格 | .xlsx | 人工编辑评审 | ⭐⭐⭐⭐⭐ |
| ARXML | .arxml | AUTOSAR项目 | ⭐⭐⭐⭐ |
最常用组合:Excel(沟通) + DBC(工具)
第十章:Excel Matrix设计
10.1 完整Excel结构
推荐的Sheet划分:
-
Sheet 1: 网络配置
- 项目信息、版本、日期
- CAN网络列表及参数
- 节点统计、报文统计
-
Sheet 2: 节点列表 (Node List)
- 节点名称、缩写、功能描述
- 硬件版本、软件版本、供应商
-
Sheet 3: 报文列表 (Message List)
- 报文ID、名称、CAN网络
- DLC、周期、发送者、接收者
- 网关路由、ASIL等级
-
Sheet 4: 信号定义 (Signal Definition)
- 信号名称、所属报文、CAN网络
- 起始位、长度、字节序
- 分辨率、偏移、范围、单位
-
Sheet 5: 枚举值定义 (Value Tables)
- 信号名称、值、含义
-
Sheet 6: 总线负载分析
- 按报文计算负载
- 按网络汇总负载
-
Sheet 7: 通信矩阵图
- 节点间通信关系
- 跨网络通信标注
-
Sheet 8: 修订历史
- 版本、日期、修改人、修改内容
10.2 Sheet 1: 网络配置
text
┌────────────────────────────────────────────────────┐
│ 项目名称: XX电动汽车CAN网络 │
│ 版本: V1.2 │
│ 日期: 2025-01-15 │
│ 作者: 系统工程师 │
│────────────────────────────────────────────────────│
│ CAN网络列表: │
│ 1. Powertrain CAN – 500 Kbps – 采样点87.5% │
│ 2. Chassis CAN – 500 Kbps – 采样点87.5% │
│ 3. Body CAN – 125 Kbps – 采样点75% │
│ 4. Cockpit CAN – 500 Kbps – 采样点87.5% │
│────────────────────────────────────────────────────│
│ 统计信息: │
│ – 节点总数: 12 │
│ – 报文总数: 85 │
│ – 信号总数: 456 │
│ – 最高总线负载: 38.5% (Powertrain CAN) │
└────────────────────────────────────────────────────┘
10.3 Sheet 2: 节点列表
| 1 | Vehicle_Control_Unit | VCU | 整车控制器 | PT_CAN, BODY_CAN | HW1.0 | SW2.3 | 自研 |
| 2 | Battery_Management_System | BMS | 电池管理系统 | PT_CAN | HW2.1 | SW3.5 | CATL |
| 3 | Motor_Control_Unit | MCU | 电机控制器 | PT_CAN | HW1.5 | SW2.0 | Huichuan |
| 4 | Body_Control_Module | BCM | 车身控制模块 | BODY_CAN | HW3.0 | SW2.1 | Continental |
| 5 | Instrument_Cluster | IC | 仪表盘 | COCK_CAN | HW3.0 | SW4.1 | Visteon |
| 6 | ABS_System | ABS | 防抱死系统 | CHAS_CAN | HW2.0 | SW3.0 | Bosch |
10.4 Sheet 3: 报文列表(完整版)
| 0x100 | Powertrain | PT | PT_BMS_1_Voltage | 8 | 10 | BMS | VCU,MCU | →Cockpit | D | CRC+Counter | 30 | 电池电压电流 |
| 0x101 | Powertrain | PT | PT_BMS_2_SOC | 8 | 100 | BMS | VCU | →Cockpit | B | Counter | 300 | 电池SOC |
| 0x200 | Powertrain | PT | PT_MCU_1_Status | 8 | 10 | MCU | VCU | →Cockpit | D | E2E_P01 | 30 | 电机状态 |
| 0x100 | Body | BODY | BODY_BCM_1_Door | 4 | 100 | BCM | IC | →Cockpit(0x500) | QM | None | 300 | 车门状态 |
| 0x200 | Body | BODY | BODY_PEPS_1_Key | 4 | 100 | PEPS | BCM,VCU | →PT(0x600) | B | Counter | 300 | 钥匙状态 |
| 0x100 | Cockpit | COCK | COCK_IC_1_Display | 8 | 50 | IC | IVI | No | QM | None | – | 显示请求 |
关键列说明:
- CAN网络:完整网络名称
- 网络缩写:用于报文命名
- 网关路由:
- "No" = 不转发
- "→Cockpit" = 转发到座舱CAN,ID不变
- "→Cockpit(0x500)" = 转发到座舱CAN,ID映射为0x500
10.5 Sheet 4: 信号定义(完整版)
| Pack_Voltage | 0x100 | PT | PT_BMS_1_Voltage | 0 | 16 | Intel | Unsigned | 0.1 | 0 | 0 | 800 | V | 0 | 0xFFFF | D | uint16_t | 电池组总电压 |
| Pack_Current | 0x100 | PT | PT_BMS_1_Voltage | 16 | 16 | Intel | Signed | 0.1 | -1000 | -1000 | 1000 | A | 0 | 0x8000 | D | int16_t | 电池组总电流 |
| SOC | 0x101 | PT | PT_BMS_2_SOC | 0 | 8 | Intel | Unsigned | 0.5 | 0 | 0 | 100 | % | 50 | 0xFF | B | uint8_t | 电池荷电状态 |
| Motor_Speed | 0x200 | PT | PT_MCU_1_Status | 0 | 16 | Intel | Signed | 1 | -10000 | -10000 | 55535 | rpm | 0 | 0x8000 | D | int16_t | 电机转速 |
| FL_Door_Status | 0x100 | BODY | BODY_BCM_1_Door | 0 | 2 | Intel | Unsigned | 1 | 0 | 0 | 3 | – | 0 | 3 | QM | uint8_t | 左前门状态 |
10.6 Sheet 6: 总线负载分析
| PT_CAN | 0x100 | BMS_Voltage | 8 | 10 | 130 | 100 | 13,000 | 2.6% | 2.6% |
| PT_CAN | 0x101 | BMS_SOC | 8 | 100 | 130 | 10 | 1,300 | 0.26% | 2.86% |
| PT_CAN | 0x200 | MCU_Status | 8 | 10 | 130 | 100 | 13,000 | 2.6% | 5.46% |
| PT_CAN小计 | – | – | – | – | – | 3850 | 192,500 | 38.5% | – |
| BODY_CAN | 0x100 | BCM_Door | 4 | 100 | 94 | 10 | 940 | 0.75% | 0.75% |
| BODY_CAN小计 | – | – | – | – | – | 580 | 9,400 | 7.5% | – |
负载计算公式:
text
单帧长度 = (47 + DLC×8) × 1.2 // 含20%位填充
帧/秒 = 1000 / 周期(ms)
位/秒 = 单帧长度 × 帧/秒
负载(%) = 位/秒 / 波特率 × 100%
第十一章:DBC文件详解
11.1 DBC文件结构
为每个CAN网络创建独立的DBC文件:
text
项目目录结构:
├── Powertrain_CAN.dbc
├── Chassis_CAN.dbc
├── Body_CAN.dbc
├── Cockpit_CAN.dbc
└── Gateway_Routing.txt (网关路由表)
11.2 DBC核心语法
基本结构:
dbc
VERSION ""
NS_ :
NS_DESC_
CM_
BA_DEF_
…
BS_:
BU_: 节点1 节点2 节点3
BO_ 报文ID 报文名称: DLC 发送节点
SG_ 信号名 : 起始位|长度@字节序符号 (因子,偏移) [最小|最大] "单位" 接收节点
CM_ BO_ 报文ID "报文注释";
CM_ SG_ 报文ID 信号名 "信号注释";
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 10000;
BA_ "GenMsgCycleTime" BO_ 报文ID 周期值;
11.3 完整DBC示例(Powertrain_CAN.dbc)
dbc
VERSION ""
NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_SGTYPE_REL_
SG_MUL_VAL_
BS_:
BU_: VCU BMS MCU OBC DCDC
VAL_TABLE_ Charge_Status 0 "Not_Charging" 1 "Charging" 2 "Complete" 3 "Fault" ;
BO_ 256 PT_BMS_1_Voltage_Current: 8 BMS
SG_ AliveCounter : 0|4@1+ (1,0) [0|15] "" VCU,MCU
SG_ CRC : 8|8@1+ (1,0) [0|255] "" VCU,MCU
SG_ Pack_Voltage : 16|16@1+ (0.1,0) [0|800] "V" VCU,MCU
SG_ Pack_Current : 32|16@1- (0.1,-1000) [-1000|1000] "A" VCU,MCU
SG_ Max_Cell_Voltage : 48|16@1+ (0.001,0) [0|5] "V" VCU
BO_ 257 PT_BMS_2_SOC_SOH: 8 BMS
SG_ SOC : 0|8@1+ (0.5,0) [0|100] "%" VCU
SG_ SOH : 8|8@1+ (0.5,0) [0|100] "%" VCU
SG_ Charge_Status : 16|2@1+ (1,0) [0|3] "" VCU
SG_ Fault_Level : 18|2@1+ (1,0) [0|3] "" VCU
BO_ 512 PT_MCU_1_Motor_Status: 8 MCU
SG_ Motor_Speed : 0|16@1- (1,-10000) [-10000|55535] "rpm" VCU
SG_ Motor_Torque : 16|16@1- (0.1,-500) [-500|6053.5] "Nm" VCU
SG_ Motor_Temp : 32|8@1+ (1,-40) [-40|215] "°C" VCU
CM_ "Powertrain CAN Network – 动力CAN网络";
CM_ BU_ BMS "电池管理系统 – Battery Management System";
CM_ BU_ VCU "整车控制器 – Vehicle Control Unit";
CM_ BU_ MCU "电机控制器 – Motor Control Unit";
CM_ BO_ 256 "BMS核心数据,ASIL-D,10ms周期";
CM_ BO_ 257 "BMS状态信息,ASIL-B,100ms周期";
CM_ BO_ 512 "电机状态,ASIL-D,10ms周期";
CM_ SG_ 256 Pack_Voltage "电池组总电压,无效值:0xFFFF";
CM_ SG_ 256 Pack_Current "电池组总电流,负=放电,正=充电,无效值:0x8000";
CM_ SG_ 257 Charge_Status "充电状态,见枚举表";
BA_DEF_ "BusType" STRING ;
BA_DEF_ "Baudrate" INT 0 1000000;
BA_DEF_ BO_ "GenMsgCycleTime" INT 0 10000;
BA_DEF_ BO_ "GenMsgSendType" ENUM "Cyclic","Event","CyclicAndEvent";
BA_DEF_ BO_ "ASIL_Level" ENUM "QM","ASIL_A","ASIL_B","ASIL_C","ASIL_D";
BA_DEF_ SG_ "GenSigStartValue" FLOAT 0 100000000000;
BA_DEF_DEF_ "BusType" "CAN";
BA_DEF_DEF_ "Baudrate" 500000;
BA_DEF_DEF_ "GenMsgCycleTime" 0;
BA_DEF_DEF_ "GenMsgSendType" "Cyclic";
BA_DEF_DEF_ "ASIL_Level" "QM";
BA_DEF_DEF_ "GenSigStartValue" 0;
BA_ "BusType" "CAN";
BA_ "Baudrate" 500000;
BA_ "GenMsgCycleTime" BO_ 256 10;
BA_ "GenMsgSendType" BO_ 256 "Cyclic";
BA_ "ASIL_Level" BO_ 256 "ASIL_D";
BA_ "GenMsgCycleTime" BO_ 257 100;
BA_ "ASIL_Level" BO_ 257 "ASIL_B";
BA_ "GenMsgCycleTime" BO_ 512 10;
BA_ "ASIL_Level" BO_ 512 "ASIL_D";
BA_ "GenSigStartValue" SG_ 257 SOC 50;
BA_ "GenSigStartValue" SG_ 257 SOH 100;
VAL_ 257 Charge_Status 0 "Not_Charging" 1 "Charging" 2 "Complete" 3 "Fault" ;
VAL_ 257 Fault_Level 0 "Normal" 1 "Warning" 2 "Derate" 3 "Inhibit" ;
11.4 DBC关键语法详解
节点定义
dbc
BU_: 节点1 节点2 节点3
示例:
BU_: VCU BMS MCU IC
报文定义
dbc
BO_ 报文ID 报文名称: DLC 发送节点
示例:
BO_ 256 BMS_Voltage: 8 BMS
解释:ID=256(0x100), 名称=BMS_Voltage, 8字节, BMS发送
信号定义
dbc
SG_ 信号名 : 起始位|长度@字节序符号 (因子,偏移) [最小|最大] "单位" 接收节点
字节序符号:
1 = Intel (小端)
0 = Motorola (大端)
符号:
+ = 无符号
– = 有符号
示例:
SG_ Pack_Voltage : 16|16@1+ (0.1,0) [0|800] "V" VCU,MCU
解释:
– 起始位=16
– 长度=16位
– 字节序=Intel
– 无符号
– 因子=0.1,偏移=0
– 范围=[0, 800]
– 单位=V
– 接收者=VCU和MCU
第十二章:网关路由设计
12.1 网关在多CAN网络中的作用
网关功能:
text
┌─────────────┐
│ 中央网关 │
│ Gateway │
└──────┬──────┘
│
┌───┴───┬───────┬───────┐
│ │ │ │
PT_CAN BODY_CAN CHAS_CAN COCK_CAN
功能:
1. 报文路由转发
2. ID映射(避免冲突)
3. 协议转换
4. 总线隔离
5. 负载平衡
12.2 网关路由表设计
完整路由表(Gateway_Routing配置):
| PT_CAN | 0x100 | BMS_Voltage | COCK_CAN | 0x100 | 直通 | 高 | Yes | 仪表显示 |
| PT_CAN | 0x101 | BMS_SOC | COCK_CAN | 0x101 | 直通 | 中 | Yes | 仪表显示 |
| PT_CAN | 0x200 | MCU_Status | COCK_CAN | 0x200 | 直通 | 高 | Yes | 仪表显示 |
| PT_CAN | 0x300 | VCU_Status | BODY_CAN | 0x300 | 直通 | 高 | Yes | 广播 |
| PT_CAN | 0x300 | VCU_Status | COCK_CAN | 0x300 | 直通 | 高 | Yes | 广播 |
| BODY_CAN | 0x100 | BCM_Door | COCK_CAN | 0x500 | ID映射 | 低 | Yes | 避免冲突 |
| BODY_CAN | 0x200 | PEPS_Key | PT_CAN | 0x600 | ID映射 | 中 | Yes | VCU需要 |
| COCK_CAN | 0x400 | ADAS_Warning | PT_CAN | 0x700 | ID映射 | 高 | Yes | VCU响应 |
转换规则说明:
- 直通:ID不变,数据不变,直接转发
- ID映射:更改目标ID(避免冲突),数据不变
- 数据转换:ID和数据都可能变化(少用)
12.3 网关软件实现
C
// 网关路由表定义
typedef struct {
CAN_Network_e src_network;
uint32_t src_id;
CAN_Network_e dst_network;
uint32_t dst_id; // 如果与src_id不同,则进行ID映射
bool enabled;
} GW_RouteEntry_t;
// 路由表
const GW_RouteEntry_t g_gateway_routes[] = {
// 源网络 源ID 目标网络 目标ID 使能
{CAN_NETWORK_PT, 0x100, CAN_NETWORK_COCK, 0x100, true}, // 直通
{CAN_NETWORK_PT, 0x101, CAN_NETWORK_COCK, 0x101, true}, // 直通
{CAN_NETWORK_PT, 0x200, CAN_NETWORK_COCK, 0x200, true}, // 直通
{CAN_NETWORK_BODY, 0x100, CAN_NETWORK_COCK, 0x500, true}, // ID映射!
{CAN_NETWORK_BODY, 0x200, CAN_NETWORK_PT, 0x600, true}, // ID映射!
{CAN_NETWORK_COCK, 0x400, CAN_NETWORK_PT, 0x700, true}, // ID映射!
};
#define ROUTE_TABLE_SIZE (sizeof(g_gateway_routes) / sizeof(GW_RouteEntry_t))
/**
* @brief 网关路由处理
*/
void Gateway_Route(CAN_Network_e src_network, CAN_RxMessage_t *rx_msg) {
for (int i = 0; i < ROUTE_TABLE_SIZE; i++) {
GW_RouteEntry_t *route = &g_gateway_routes[i];
// 匹配路由条目
if (route->src_network == src_network &&
route->src_id == rx_msg->id &&
route->enabled) {
// 转发到目标网络
uint32_t tx_id = route->dst_id; // 可能是映射后的新ID
CAN_Transmit(route->dst_network, tx_id, rx_msg->data, rx_msg->dlc);
printf("[GATEWAY] Route: %s(0x%03X) -> %s(0x%03X)\\n",
g_can_networks[src_network].name, rx_msg->id,
g_can_networks[route->dst_network].name, tx_id);
// 统计
g_gateway_stats[i].routed_count++;
}
}
}
/**
* @brief 网关主任务
*/
void Gateway_Task(void) {
CAN_RxMessage_t rx_msg;
// 轮询所有CAN网络
for (CAN_Network_e net = 0; net < CAN_NETWORK_MAX; net++) {
if (CAN_Receive(net, &rx_msg) == 0) {
// 接收成功,进行路由
Gateway_Route(net, &rx_msg);
}
}
}
第四部分:高级应用
第十三章:CAN FD
13.1 CAN FD vs CAN 2.0
| 数据场 | 0-8字节 | 0-64字节 |
| 仲裁速率 | 1 Mbps | 1 Mbps |
| 数据速率 | 1 Mbps | 5-8 Mbps |
| CRC | 15位 | 17/21位 |
| 帧开销 | 较大 | 相对较小 |
| 兼容性 | – | 可与CAN 2.0混合组网 |
13.2 CAN FD数据长度码(DLC)
| 0-8 | 0-8 | 0-8 |
| 9 | 保留 | 12 |
| 10 | 保留 | 16 |
| 11 | 保留 | 20 |
| 12 | 保留 | 24 |
| 13 | 保留 | 32 |
| 14 | 保留 | 48 |
| 15 | 保留 | 64 |
13.3 CAN FD应用场景
- ✅ 高清摄像头数据传输(ADAS)
- ✅ 激光雷达点云数据
- ✅ OTA软件升级
- ✅ 高速诊断
- ✅ 网关数据转发
第十四章:诊断协议
14.1 OBD-II / UDS ID分配
text
功能寻址:
0x7DF – 广播请求(所有ECU)
物理寻址(请求):
0x7E0 – 发动机ECU
0x7E1 – 变速箱ECU
0x7E2 – ABS ECU
…
物理寻址(响应):
0x7E8 – 发动机响应 (0x7E0 + 0x08)
0x7E9 – 变速箱响应
…
14.2 示例:读取发动机转速
text
请求: 0x7E0: [02 01 0C 00 00 00 00 00]
Byte0: 02 (后续字节数)
Byte1: 01 (服务模式-当前数据)
Byte2: 0C (PID-发动机转速)
响应: 0x7E8: [04 41 0C 1F 40 00 00 00]
Byte0: 04
Byte1: 41 (0x40+0x01=肯定响应)
Byte2: 0C (PID)
Byte3-4: 1F 40
转速 = (0x1F×256 + 0x40) / 4 = 2000 rpm
第十五章:MCU-less架构
15.1 概念
去微控制器方案的核心思想:
- 将原本散布在各边缘设备中的控制逻辑上收
- 统一交由算力更强的区域控制器管理
- 通过高速以太网或优化总线直接控制
15.2 三种技术路线
路线一:UART over CAN(TI, 英飞凌)
- 低成本
- 复用现有CAN物理层
- 适合车灯等应用
路线二:10BASE-T1S以太网(安森美, ADI)
- 高性能
- E2B硬件解析
- 支持TSN同步
路线三:CAN FD Light(ST)
- 精简协议
- 主从拓扑
- 保留CAN稳健性
15.3 优势
- ✅ 降低BOM成本
- ✅ 简化软件维护
- ✅ 支持OTA升级
- ✅ 软件定义硬件
第五部分:软件开发
第十六章:开发工具
16.1 硬件工具
| PCAN-USB | ¥500-2000 | 性价比高,支持CAN FD | ⭐⭐⭐⭐⭐ |
| CANalyst-II | ¥300 | 国产入门首选 | ⭐⭐⭐⭐ |
| Kvaser Leaf | ¥1000-5000 | Linux支持好 | ⭐⭐⭐⭐ |
| CANoe | ¥5万+ | 行业标准,功能最强 | ⭐⭐⭐⭐⭐ |
16.2 软件工具
Windows:
- PCAN-View (免费)
- Busmaster (开源)
- CANoe/CANalyzer (商业)
Linux:
Bash
# 安装can-utils
sudo apt install can-utils
# 显示报文
candump can0
# 发送报文
cansend can0 123#1122334455667788
# 回放
canplayer -I log.txt
16.3 Python开发
Python
import can
# 连接
bus = can.interface.Bus(channel='can0', bustype='socketcan')
# 发送
msg = can.Message(
arbitration_id=0x123,
data=[0x11, 0x22, 0x33],
is_extended_id=False
)
bus.send(msg)
# 接收
message = bus.recv()
print(f"ID: {hex(message.arbitration_id)}, Data: {message.data.hex()}")
第十七章:驱动开发
17.1 STM32 HAL示例
C
// 初始化
CAN_HandleTypeDef hcan;
hcan.Instance = CAN1;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.Prescaler = 16;
hcan.Init.TimeSeg1 = CAN_BS1_13TQ;
hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
HAL_CAN_Init(&hcan);
// 配置过滤器
CAN_FilterTypeDef filter;
filter.FilterIdHigh = 0x100 << 5;
filter.FilterIdLow = 0;
filter.FilterMaskIdHigh = 0x700 << 5;
filter.FilterMaskIdLow = 0;
filter.FilterMode = CAN_FILTERMODE_IDMASK;
HAL_CAN_ConfigFilter(&hcan, &filter);
// 发送
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8] = {0x11, 0x22, 0x33};
uint32_t TxMailbox;
TxHeader.StdId = 0x123;
TxHeader.DLC = 3;
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
HAL_CAN_AddTxMessage(&hcan, &TxHeader, TxData, &TxMailbox);
// 接收
CAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[8];
HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, RxData);
17.2 多CAN网络管理
C
// CAN网络定义
typedef enum {
CAN_NETWORK_POWERTRAIN = 0,
CAN_NETWORK_BODY = 1,
CAN_NETWORK_COCKPIT = 2,
CAN_NETWORK_MAX
} CAN_Network_e;
// 网络配置
typedef struct {
CAN_Network_e network_id;
const char* name;
uint32_t bitrate;
CAN_HandleTypeDef* hcan;
} CAN_NetworkConfig_t;
// 全局配置
const CAN_NetworkConfig_t g_can_networks[CAN_NETWORK_MAX] = {
{CAN_NETWORK_POWERTRAIN, "PT_CAN", 500000, &hcan1},
{CAN_NETWORK_BODY, "BODY_CAN", 125000, &hcan2},
{CAN_NETWORK_COCKPIT, "COCK_CAN", 500000, &hcan3},
};
/**
* @brief 发送CAN报文(指定网络)
*/
int CAN_Transmit(CAN_Network_e network, uint32_t id, uint8_t *data, uint8_t dlc) {
if (network >= CAN_NETWORK_MAX) {
return -1;
}
CAN_HandleTypeDef *hcan = g_can_networks[network].hcan;
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailbox;
TxHeader.StdId = id;
TxHeader.DLC = dlc;
TxHeader.IDE = CAN_ID_STD;
TxHeader.RTR = CAN_RTR_DATA;
if (HAL_CAN_AddTxMessage(hcan, &TxHeader, data, &TxMailbox) != HAL_OK) {
return -2;
}
printf("[TX] Network=%s, ID=0x%03X, DLC=%d\\n",
g_can_networks[network].name, id, dlc);
return 0;
}
第六部分:实战项目
第十八章:完整项目案例
18.1 项目背景
项目:某纯电动乘用车CAN网络
需求:
- 节点:12个(VCU, BMS, MCU, OBC, DCDC, IC, AC, BCM, PEPS, ABS, ESP, GW)
- CAN网络:4个(PT, BODY, CHAS, COCK)
- 报文:85个
- 波特率:500Kbps / 125Kbps
- 目标总线负载:< 40%
18.2 网络拓扑
text
┌──────────────┐
│ 中央网关GW │
└──────┬───────┘
│
┌────────────────┼────────────────┬───────────┐
│ │ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ ┌──▼────┐
│动力CAN │ │车身CAN │ │座舱CAN │ │底盘CAN │
│500 Kbps │ │125 Kbps │ │500 Kbps │ │500Kbps│
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └───┬───┘
│ │ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐ ┌───┴───┐
│VCU │ │BCM │ │IC │ │ABS │
│BMS │ │PEPS │ │IVI │ │ESP │
│MCU │ │门控 │ │HUD │ │ │
│OBC │ │座椅 │ │ADAS │ │ │
│DCDC │ │空调 │ │ │ │ │
└───────────┘ └───────────┘ └───────────┘ └───────┘
18.3 关键报文设计
BMS_1_Voltage_Current (0x100, PT_CAN, 10ms, ASIL-D)
C
// 报文结构
typedef struct {
uint8_t AliveCounter : 4;
uint8_t Reserved : 4;
uint8_t CRC;
uint16_t Pack_Voltage; // 0.1V
int16_t Pack_Current; // 0.1A, -1000偏移
uint16_t Max_Cell_Voltage; // 0.001V
} __attribute__((packed)) BMS_1_Msg_t;
// 发送端(BMS)
void BMS_Send_Voltage_Current(void) {
static uint8_t counter = 0;
uint8_t data[8];
// 填充数据
data[0] = counter & 0x0F;
uint16_t voltage = (uint16_t)(pack_voltage / 0.1);
data[2] = voltage & 0xFF;
data[3] = (voltage >> 8) & 0xFF;
int16_t current = (int16_t)((pack_current + 1000) / 0.1);
data[4] = current & 0xFF;
data[5] = (current >> 8) & 0xFF;
uint16_t max_cell = (uint16_t)(max_cell_voltage / 0.001);
data[6] = max_cell & 0xFF;
data[7] = (max_cell >> 8) & 0xFF;
// CRC
data[1] = Calculate_CRC8(&data[2], 6);
// 发送到动力CAN
CAN_Transmit(CAN_NETWORK_POWERTRAIN, 0x100, data, 8);
counter = (counter + 1) % 16;
}
// 接收端(VCU)
bool VCU_Receive_BMS_Data(uint8_t *data) {
static uint8_t last_counter = 0xFF;
static uint32_t last_time = 0;
// 超时检查
if ((GetTime() – last_time) > 30) {
Set_Fault(FAULT_BMS_TIMEOUT);
return false;
}
// CRC校验
if (Calculate_CRC8(&data[2], 6) != data[1]) {
Set_Fault(FAULT_BMS_CRC);
return false;
}
// Counter检查
uint8_t counter = data[0] & 0x0F;
if (counter != ((last_counter + 1) % 16) && last_counter != 0xFF) {
Set_Warning(WARN_COUNTER_JUMP);
}
// 解析
uint16_t raw_voltage = data[2] | (data[3] << 8);
float voltage = raw_voltage * 0.1f;
int16_t raw_current = data[4] | (data[5] << 8);
float current = (raw_current * 0.1f) – 1000.0f;
last_counter = counter;
last_time = GetTime();
return true;
}
18.4 总线负载计算
Python
def calculate_bus_load(messages, bitrate=500000):
total_load = 0
print(f"{'ID':<8} {'DLC':<5} {'周期':<8} {'帧/秒':<8} {'位/秒':<10} {'负载%':<8}")
print("-" * 60)
for msg_id, dlc, period in sorted(messages, key=lambda x: x[2]):
frame_bits = (47 + dlc * 8) * 1.2
frames_per_sec = 1000 / period
bits_per_sec = frame_bits * frames_per_sec
load = bits_per_sec / bitrate * 100
total_load += load
print(f"0x{msg_id:03X} {dlc:<5} {period:<8} {frames_per_sec:<8.0f} {bits_per_sec:<10.0f} {load:<8.2f}")
print("-" * 60)
print(f"总线负载: {total_load:.2f}%")
if total_load < 40:
print("✅ 负载正常")
elif total_load < 60:
print("⚠️ 负载偏高")
else:
print("❌ 负载过高")
return total_load
# 动力CAN报文
pt_messages = [
(0x100, 8, 10), # BMS核心数据
(0x101, 8, 100), # BMS SOC
(0x200, 8, 10), # MCU状态
(0x300, 8, 20), # VCU状态
# … 更多报文
]
calculate_bus_load(pt_messages)
第七部分:学习路线图
第十九章:系统学习计划
19.1 8周学习路线
第1周:基础入门
- Day 1-2: CAN概述、物理层、电平信号
- Day 3-4: 掌握帧格式、仲裁机制
- Day 5-6: 理解错误检测、位定时
- Day 7: 总结+搭建测试环境
第2周:报文与信号
- Day 1-2: 学习报文ID设计、属性定义
- Day 3-4: 掌握信号定义、编解码
- Day 5-6: 理解字节序、偏置计算
- Day 7: 手工计算报文练习
第3周:多CAN网络
- Day 1-2: 学习多网络架构、网络标识
- Day 3-4: 掌握网关路由设计
- Day 5-6: 理解ID映射、协议转换
- Day 7: 网关软件实现
第4周:Matrix设计
- Day 1-2: 学习Matrix概念、结构
- Day 3-4: 掌握Excel Matrix编写
- Day 5-6: 学习DBC文件语法
- Day 7: Excel ↔ DBC转换工具
第5周:工具与开发
- Day 1-2: 安装配置硬件工具
- Day 3-4: 学习PCAN-View、Busmaster
- Day 5-6: Python can库编程
- Day 7: Linux can-utils实践
第6周:驱动开发
- Day 1-3: STM32 HAL CAN驱动
- Day 4-5: 过滤器配置、中断处理
- Day 6-7: 完整收发程序
第7周:高级应用
- Day 1-2: CAN FD学习
- Day 3-4: 诊断协议(OBD-II/UDS)
- Day 5-6: CANopen/J1939概览
- Day 7: MCU-less架构理解
第8周:项目实战
- Day 1-5: BMS通信系统开发
- Day 6-7: CAN网关/负载分析工具
19.2 能力检查清单
初级 (1-2周)
- 能解释CAN总线的基本原理
- 能识别标准帧的各个字段
- 理解仲裁机制
- 会使用PCAN-View查看报文
中级 (3-4周)
- 能设计简单的CAN Matrix
- 会编写DBC文件
- 能手工计算信号的编解码
- 会使用Python读写CAN
- 理解多CAN网络架构
高级 (5-8周)
- 能独立设计完整的多CAN网络
- 能开发CAN驱动程序
- 能进行总线负载分析与优化
- 能实现网关、诊断等高级功能
专家级 (持续学习)
- 精通AUTOSAR CAN协议栈
- 能进行ISO 26262安全设计
- 熟悉多种高级协议(CANopen/J1939)
- 能指导团队完成复杂项目
第八部分:附录
附录A:快速参考表
A.1 CAN报文帧结构速查
text
标准帧 (最多130位):
┌────┬────────┬──────┬──────┬─────┬─────┬─────┬────┐
│SOF │仲裁场 │控制场│数据场│CRC场│ACK场│ EOF │IFS │
│1位 │12位 │6位 │0-64位│16位 │2位 │7位 │3位 │
└────┴────────┴──────┴──────┴─────┴─────┴─────┴────┘
仲裁场 = ID(11位) + RTR(1位)
控制场 = IDE(1) + r0(1) + DLC(4)
CRC场 = CRC序列(15) + 界定符(1)
ACK场 = ACK槽(1) + 界定符(1)
A.2 常用波特率配置
| 1 Mbps | 1 μs | 0.0625 μs | 1 | 2 | 5 | 8 | 50% |
| 500 Kbps | 2 μs | 0.125 μs | 1 | 2 | 5 | 8 | 50% |
| 250 Kbps | 4 μs | 0.25 μs | 1 | 2 | 5 | 8 | 50% |
| 125 Kbps | 8 μs | 0.5 μs | 1 | 2 | 5 | 8 | 50% |
A.3 常用数据类型范围
| bool | 1 | 0-1 | – |
| uint8_t | 8 | 0-255 | -128~127 |
| uint16_t | 16 | 0-65535 | -32768~32767 |
| uint32_t | 32 | 0-4294967295 | ±2147483648 |
A.4 典型分辨率选择
| 电压 | 0.1 V | 16 | 0-6553.5 V |
| 电流 | 0.1 A | 16 + offset | -1000~5553.5 A |
| 温度 | 1 °C | 8 + offset | -40~215 °C |
| 转速 | 1 rpm | 16 | 0-65535 rpm |
| 车速 | 0.01 km/h | 16 | 0-655.35 km/h |
| SOC | 0.5 % | 8 | 0-127.5 % |
附录B:常见问题FAQ
Q1:为什么CAN总线不需要地址?
A: CAN采用广播机制,每个报文通过ID标识内容而非目标地址。所有节点都收到,根据需要过滤。
Q2:同一ID可以存在于不同CAN网络吗?
A:
- ✅ 可以! 因为它们在不同的物理CAN总线上,互不干扰
- ⚠️ 但需要在Matrix中明确标注CAN网络
- ⚠️ 如果需要网关转发,可能需要ID映射
Q3:如何选择Intel还是Motorola字节序?
A:
- Intel(小端):现代汽车OEM的主流,工具支持好
- Motorola(大端):J1939等商用车协议
- 建议:新项目统一用Intel
Q4:报文周期如何选择?
A:
text
安全关键: ≤20ms
实时控制: ≤50ms
状态监控: 100-500ms
慢速信号: ≥1000ms
公式: 周期 ≤ 1/(2×最快变化频率)
Q5:总线负载多少算合理?
A:
text
< 40%: 健康
40-60%: 可接受,但需监控
> 60%: 危险,必须优化
> 80%: 紧急,系统将不稳定
附录C:学习资源
官方文档
- ISO 11898-1: CAN协议标准
- ISO 11898-2: 高速CAN物理层
- CAN in Automation (CiA)官网
书籍
- 《CAN总线原理与应用技术》
- 《Automotive Embedded Systems》
- 《Understanding CAN》
在线课程
- Udemy: Automotive CAN Bus
- Coursera: Automotive Networks
开源项目
- SocketCAN (Linux内核)
- python-can
- CANopen协议栈
总结
CAN总线学习的核心框架:
text
1. 基础 → 协议 → 报文 → 网络 → Matrix → 工具 → 项目
↓ ↓ ↓ ↓ ↓ ↓ ↓
物理 帧格式 ID设计 多网络 DBC 编程 实战
电平 仲裁 信号 路由 Excel 驱动 BMS/GW
2. 关键要点
– 掌握帧格式与仲裁
– 理解报文与信号的完整属性
– 重视多CAN网络标识与管理
– 熟练使用Matrix工具(Excel+DBC)
– 动手实践驱动与应用开发
3. 学习建议
– 理论与实践并重
– 循序渐进,8周系统学习
– 多做项目,积累经验
– 关注新技术(CAN FD, 车载以太网)




