CAN总线学习笔记

协议规定

差分信号

差值为0为逻辑1(隐性电平)

差值不为0为逻辑1(显性电平) (具体差值范围多少和高速\低速CAN协议有关)

可记为电平拉开为0, 闭合为1

异步半双工

帧格式

数据帧 遥控帧 错误帧 过载帧 帧间隔

EOF之后仍需3位显性电平的帧间隔才能进入下一个通信

位填充机制:兼容错误帧和过载帧 避免错位和防止被误认为是空闲

位同步

通过引入位时序

解决时序起始位不同步->硬同步

解决时序传输过程中的偏差->再同步

can总线中码元指的是一个位时序, 波特率也因此由一个位时序所需时间决定

仲裁

连续11个隐形电平->空闲状态

两种仲裁机制护航:

  1. 先到先得 只有当检测到空闲状态的情况下才尝试发送数据
  2. 通过仲裁段进行仲裁

ID + RTR 作为仲裁段

线与特性: can总线拉开代表显性0, 当多个主机中任一主机拉开总线时 总线代表显性0 当所有主机都释放总线时 总线才代表隐性1

回读机制: 每个主机在发送数据后都会读取总线的状态

根据上述两个机制得到仲裁的特性

当主机发出隐性1但是读回的确实显性0时, 说明当前有主机正在使用该主线发送数据 但是发送0的这个主机却不知道有另一个主机正在使用这条主线

这时发送1的主机仲裁失败, 回到读取状态

这也决定了总是发送ID号小的帧的数据仲裁会成功

Q: 位填充是否会影响到仲裁?

A: 结论是不会, 假设位填充会影响到仲裁 那么位填充之前的位均为一样的, 这种情况显然不会影响,因为填充的那位也是相同的 如果位填充之前的位都不一样, 那更不可能了 如果只有一个主机产生位填充 那很明显前面5位绝不相同, 这点也毋庸置疑

那么两个主机发送同一个ID号的数据帧和遥控帧的情况呢

数据帧的RTR位为0, 遥控帧的RTR位为1

发送遥控帧的主机会仲裁失败

回到实际意义来讲, 发送遥控帧的主机获得的数据永远是最新的, 不会导致数据不同步的错误情况, 这点是符合实际项目要求的

那么拓展格式的情况呢

同样分标准数据帧和标准遥控帧来讨论, 拓展格式中数据帧和遥控帧和上面讨论情况相同

先说标准数据帧

拓展格式的SRR位(对应的就是标准格式的RTR位)为隐性1, 所以面对标准数据帧, 拓展格式是仲裁失利的

那么标准遥控帧呢

遥控帧的RTR位为1, 同时仲裁段也只是ID + RTR 这一段

那么遥控帧就有了必胜的理由,那么我们接着看

RTR位紧跟着的就是IDE位, 拓展格式的这一位规定置为隐性1, 所以必定会仲裁失败

这里和前面帧格式为什么要这么定义形成逻辑闭环

错误处理

5种错误: 位错误(回读), 填充错误, CRC错误, 格式错误(各种界定符错误), ACK错误

错误状态机制与TEC(Transmit Error Counter), REC

TEC和REC并不总是每次-1, 很大可能是一次-8, 但需要8次正确传输才能补救回来

0

Q: 为什么结束的EOF要给7个隐性1

A: 一是7个隐性1 + ACK界定符(一位) + 3位帧间隔 刚好11位隐性1, 这之后满足进入空闲状态的要求

二是留给错误处理置错误标志一段时间, 避免接收方还未意识到发送的数据错了就已经进入下一帧了

Q: 在哪个区段里会产生位填充现象

SOF到CRC校验之间的数据

Q: 延迟传送的作用

其中一个作用是使其处于仲裁失利, 避免易出错的设备优先去发送数据占用总线资源

位屏蔽机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//32位屏蔽#2
//这里如果不 | 0x04则会混入标准格式0x1xx的数据 具体分析见注释的二进制数
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
uint32_t ID = (0x04000000u << 3);//这里和0x100 << 21效果相同 //0b 001 0000 0000 0000 0000 0000 0000 00 000
CAN_FilterInitStructure.CAN_FilterIdHigh = ID >> 16; //0x10100000 0b 1 0000 0001 0000 0000 0000 0000 0000 000
//0b 0 0100 0000 0000 0000 0000 0000 0000 000 0x04000000
CAN_FilterInitStructure.CAN_FilterIdLow = ID; //uint32强转为uint16时会舍弃掉高4位
uint32_t Mask = (0x1C000000u << 3);//这里和0x700 << 21效果相同
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = Mask >> 16; //0b 111 0000 0000 0000 0000 0000 0000 00 000
CAN_FilterInitStructure.CAN_FilterMaskIdLow = Mask; //0b 1 1100 0000 0000 0000 0000 0000 0000 000

CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);

中断

中断宏 含义 触发时机
CAN_IT_TME 发送邮箱空 可以发送下一帧
CAN_IT_FMP0 FIFO0 有消息 接收到数据
CAN_IT_FF0 FIFO0 满 3 条消息满了
CAN_IT_FOV0 FIFO0 溢出 数据覆盖
CAN_IT_FMP1 FIFO1 有消息 接收到数据
CAN_IT_FF1 FIFO1 满
CAN_IT_FOV1 FIFO1 溢出
CAN_IT_EWG 错误警告 错误计数 > 96
CAN_IT_EPV 错误被动 错误计数 >= 128
CAN_IT_LEC 最后错误码变化 出现新的错误
CAN_IT_ERR 任意错误事件 CRC/ACK/stuff 等错误
CAN_IT_WKU 唤醒 从 sleep→正常模式
CAN_IT_SLK 睡眠完成