第3章 CAN总线基础:CAN协议简介与帧结构
大家好,欢迎来到第三章。这一章我们聊聊CAN总线最核心的东西——协议本身。说实话,很多工程师用了好几年CAN工具,却连CAN帧长什么样都说不清楚。我个人觉得,搞懂CAN协议,就像学开车前先搞懂油门刹车一样,是基本功。
3.1 CAN协议简介(CAN 2.0A/B)
CAN协议最早是德国博世公司搞出来的,初衷很简单——让车上的各种电子模块能互相说话。后来成了国际标准ISO 11898。
目前主流的是CAN 2.0版本,分两种:
- CAN 2.0A:标准帧,ID是11位。你想想看,11位能表示多少ID?2的11次方,2048个。早期够用,现在嘛……有点紧张。
- CAN 2.0B:扩展帧,ID是29位。多了18位扩展标识符,能表示5亿多个ID。我在做新能源车项目时,整车节点一多,标准帧ID根本不够分,最后全切成了扩展帧。
核心区别一句话:标准帧ID短(11位),扩展帧ID长(29位)。两者在总线上可以共存,但扩展帧的仲裁段更长,优先级计算时要注意。
嗯,这里要注意:CAN 2.0B的控制器可以收发2.0A的帧,但反过来不行。老设备升级时经常踩这个坑。
3.2 CAN帧结构详解
一条完整的CAN帧,就像一封信。咱们拆开看看里面都有啥。我习惯把帧结构分成七个部分来记:
| 字段 | 全称 | 长度 | 作用 |
|---|---|---|---|
| SOF | Start of Frame | 1 bit | 帧起始,同步信号 |
| ID | Identifier | 11或29 bit | 标识符,决定优先级 |
| DLC | Data Length Code | 4 bit | 数据长度,0~8字节 |
| Data | Data Field | 0~64 bit | 实际数据 |
| CRC | Cyclic Redundancy Check | 15 bit | 校验,检测传输错误 |
| ACK | Acknowledge | 2 bit | 应答,接收节点确认 |
| EOF | End of Frame | 7 bit | 帧结束 |
咱们一个一个说:
SOF(帧起始)
就1个显性位(逻辑0)。所有节点看到这个信号,就知道要开始收数据了。说白了就是发令枪。
ID(标识符)
这是CAN协议最巧妙的地方。ID越小,优先级越高。为什么?因为CAN总线用“线与”机制,显性位(0)会覆盖隐性位(1)。ID全是0的帧,永远抢到总线。
我在项目中遇到过一个问题:两个ECU同时发ID=0x100和ID=0x200的帧,结果ID=0x100的总是先发出去。后来排查发现,0x100的二进制是0001 0000 0000,0x200是0010 0000 0000,从第3位开始0x100就占了优势。
DLC(数据长度码)
4个bit,能表示0~15。但CAN协议规定数据最多8字节。所以DLC值超过8时,实际数据还是8字节。这个细节很多人不知道。
小技巧:在CANoe中配置DLC时,如果数据只有3个字节,DLC就填3。别填8然后空着5个字节,浪费带宽。
Data(数据段)
0~8字节,具体长度由DLC决定。数据怎么解析?完全看协议约定。比如车速信号可能在第1~2字节,转速在第3~4字节。没有统一规定,全靠设计文档。
CRC(循环冗余校验)
15位CRC码加1位CRC分隔符。发送节点算一遍,接收节点再算一遍。对不上?那就丢帧重传。CAN的可靠性,一半靠这个。
ACK(应答)
2个bit:ACK Slot + ACK Delimiter。发送节点发隐性位,接收节点如果正确收到,就在ACK Slot拉成显性位。如果没人应答,发送节点就知道出问题了。
我曾经调试一个总线故障,发现所有帧都没有ACK应答。查了半天,原来是终端电阻没接,信号反射导致接收节点根本收不到完整帧。
EOF(帧结束)
7个连续的隐性位(逻辑1)。告诉总线:这帧发完了,下一个可以抢总线了。
3.3 CAN总线物理层与终端电阻
聊完软件协议,咱们看看物理层。CAN总线用两根线:CAN_H和CAN_L。差分信号传输,抗干扰能力强。
逻辑状态有两种:
- 显性(Dominant):CAN_H ≈ 3.5V,CAN_L ≈ 1.5V,压差约2V,代表逻辑0
- 隐性(Recessive):CAN_H ≈ 2.5V,CAN_L ≈ 2.5V,压差约0V,代表逻辑1
终端电阻是啥?说白了就是总线两端各接一个120Ω的电阻。为什么是120Ω?因为CAN总线用的双绞线特性阻抗大约是120Ω。接上匹配电阻,能防止信号反射。
注意:终端电阻必须接在总线的最两端。如果接在中间,或者只接一个,信号反射会导致通信不稳定。我见过一个案例,产线上CAN通信时好时坏,最后发现是工人漏接了一个终端电阻。
测量方法很简单:断电状态下,用万用表量CAN_H和CAN_L之间的电阻。正常值应该是60Ω左右(两个120Ω并联)。如果量到120Ω,说明只接了一个;如果量到无穷大,说明一个都没接。
3.4 CANoe中的CAN通道配置
好,理论讲完了,咱们打开CANoe实操一下。配置CAN通道是第一步,也是最容易出错的一步。
在CANoe中配置通道的步骤:
- 打开 Hardware → Network Hardware 配置窗口
- 选择对应的硬件接口(如VN1610、VN1640等)
- 设置通道类型为CAN(或CAN FD)
- 配置波特率(常见:250 kbps、500 kbps、1 Mbps)
- 确认终端电阻设置(硬件上通常有开关)
我个人习惯在配置完成后,先发一条测试帧看看能不能收到ACK。如果收不到,八成是波特率不对或者终端电阻没接。
下面是一个简单的CANoe CAPL脚本,用来发送一条标准CAN帧:
variables
{
message 0x100 msg;
}
on start
{
msg.dlc = 8;
msg.byte(0) = 0xAA;
msg.byte(1) = 0xBB;
msg.byte(2) = 0xCC;
msg.byte(3) = 0xDD;
msg.byte(4) = 0xEE;
msg.byte(5) = 0xFF;
msg.byte(6) = 0x11;
msg.byte(7) = 0x22;
output(msg);
write("Message 0x100 sent!");
}
这段代码干了啥?定义了一个ID为0x100的CAN消息,DLC=8,填充了8个字节的数据,然后发出去。你在CANoe的Trace窗口里就能看到这条帧。
调试建议:刚开始学CANoe时,别急着写复杂脚本。先学会用CANoe的Interactive Generator模块,手动发几帧看看效果。等熟悉了帧结构,再上CAPL。
好了,这一章的内容就到这儿。CAN协议看起来复杂,其实核心就那几样东西:帧结构、仲裁机制、物理层。搞懂了这些,后面学CANoe的各种高级功能就轻松多了。下一章咱们聊聊CANoe的Trace窗口怎么用,那可是调试神器。