1.7 CAN

这一章开始我们将使用真正的大疆主控板来完成接下来的学习

我这里采用的大疆 A 板, 它的参数如下

主控单元为: STM32F427IIH6 12MHz 晶振, 可配置最高主频 180MHz


与之前我们学习到的UART 通信协议一样, CAN 通信以及所有的通信协议都需要具有通信分层的思想, 物理信号层, 链路传输层, 应用数据层(可以回看之前 UART 的那一章)[[1.4 UART通信|1.4 UART通信]] # CAN - Controller Area Network

CAN 的接线方式, 每个设备接出两根线 CAN_HCAN_L, 不同设备的 CAN_H 相互接在一起, CAN_L 相互接在一起

Pasted image 20260705223730 CAN_H 和 CAN_L 通过两个 120Ω的电阻连接在一起, 将总线阻抗变成 60Ω, 主要有两个作用:

  • 防止总线上传输的高频信号存在反射振铃效应导致信号不稳定
  • 防止寄生电容导致信号响应速度变慢

在 CAN 通信中, 比特率(bps)等于波特率(symbol/s), 因为有效信息码元直接对应二进制码元


异步通信 – 物理层

还记得我们之前学习的 UART 通信吗? 他是靠每个字节起始位的电平跳变检测来同步的, 聚焦到每个 bit, 是依靠超采样来确定电平的

但是在 CAN 通信中, 每个 bit 位的电平依靠一套位时间结构来确定

can 通信同样是异步协议, 它没有时钟线, 那他怎么知道什么时候去读取每一位的电平信息? can 通信把每个 bit 位在时间上切成很多个小时间片(Time Quantum, Tq)

一个 bit 从头到尾被切成了 4 段Pasted image 20260705231905

同步段 SYNC_SEG, 固定占 1 个 Tq 所有节点都约定: 一个 bit 的电平跳变应该发生在这一段内。接收方就靠检测跳变落在哪、有没有偏来判断自己的节奏是快了还是慢了, 进而做重同步

传播段 PROP_SEG, 补偿物理延迟用 因为信号在导线上跑要时间、收发器(CAN 驱动器)处理也要时间, 总线越长延迟越大, 这一段就是故意留出一段延迟的余量。不过你也知道, 电信号传播速度接近光速, 并且车载总线又短, 所以大多数情况这段影响很小

相位缓冲段 1 / 2 PHASE_SEG1/PHASE_SEG2 采样点(sample point)就卡在这两段的交界处 硬件在这个时刻去读总线电平, 判定当前 bit 是 0 还是 1(CAN 里 0 是显性/dominant, 1 是隐性/recessive) 这两段还承担重同步的调整作用: 如果发现边沿早到了就把 PHASE_SEG1 拉长一点, 如果晚到了就把 PHASE_SEG2 缩短一点, 能伸缩的最大幅度就是 SJW(重同步跳转宽度)

几项配置:

  • Prescaler: 决定 Tq 有多长(Tq = Prescaler / f_APB)
  • Time Quantum in Bit Segment 1 (BS1): PROP_SEG + PHASE_SEG1
  • Time Quantum in Bit Segment 2 (BS2): PHASE_SEG2
  • ReSync Jump Width (SJW): 伸缩上限

计算

1 bit 的总时间(单位 Tq) = 1(SYNC) + BS1 + BS2

动动脑子想一想, 理解一下这些公式


差分信号 – 物理层

Pasted image 20260705234021 差分信号传输, 在长距离传输时可以保证信号的质量, 差分信号有两种电平状态, 一种是等势的隐性电平, 一种是差分的显性电平

隐性电平, 等势, 代表逻辑 1: CAN_H 和 CAN_L 的电平相同, 都为 2.5v

显性电平, 差分, 代表逻辑 0: CAN_H 为 3.5v, CAN_L 为 1.5v

如果在一条 can 总线上的多个设备分别想发显性电平和隐性电平, 那么最终总线表现出来的是显性电平(逻辑 0), 这也就是线与

线与AND: 全发隐性电平(逻辑 1): AND(1, 1, 1, 1) = 1 只要有一个显性电平(逻辑 0): AND(…, 0, …) = 0

我的理解是, 差分的显性电平是一个收发器的主动驱动动作, 他需要把 CAN_H 拉动到 3.5v 把 CAN_L 压低至 1.5v 才能表现出 2v 的压差, 是一个主动动作. 而等势的隐性电平则是收发器不驱动的放手行为, 收发器不做任何事情, 让 CAN_H 和 CAN_L 自动回到等势的 2.5v. 所以分差的显性电平(逻辑 0)会覆盖掉等势的隐性电平(逻辑 1)


位填充 Bit Padding, BP – 物理层

can 通信是异步通信协议, 他没有一个统一的时钟信号来用于同步, 他依靠的是我们之前说到的每1 bit上的同步段进行调整的, 但是如果长时间的发送同一种电平, 同步段就起不到作用了, 这就可能导致数据错位

为了避免长时间发送同一种电平, 导致同步段起不到作用, 进而导致数据错位的问题. 于是我们才去位填充的方式来预防

位填充 BP: 如果发送的数据中连续出现了 5 个相同的电平格式, 则在其后自动插入一个相反的电平格式

于是我们通过计数5 个相同电平格式的后面是否有一个相反电平格式, 如果有那么同步段就能利用这个相反的电平来调整和校准; 如果没有, 则表明这段数据出现了一些错位, 就采取错误数据处理的方案


CAN 的收发模式 – 物理层

看上去与 URAT 有点不一样

  • 常规模式: 又收又发, 从总线接收, 向总线发送
  • 回环模式: 只发不收, 向总线和本机发送, 不从总线接收, 只从本机接收
  • 静默模式: 只收不发, 不向总线发送, 仅向本机发送, 从总线和本机接收
  • 回环静默模式: 自收自发, 不向总线发送, 也不从总线接收, 而是自收自发

CAN ID – 链路层

CAN ID 只对它发送的报文负责

CAN ID 是一帧的标识符, 这个和我们在计算机网络中学习到的地址的概念不尽相同. 在计算机网络中, 比如 IP 地址常常描述的是一个网络内唯一指定的一个设备, 是这个设备的门牌号. 但是 CAN ID 虽然也叫做地址, 但是他却不是这个设备的门牌号, 因为他指定的对象不是单个设备, 而是这一帧数据, CAN ID 描述的是这一帧数据的 ID, 不是某个设备的 ID, 这一点常常在初识CAN 的时候弄错, 因为地址这个词听起来太具有迷惑性了, 所以 CAN 设备并没有一个固定的地址, 而是以 CAN ID 的方式加以区分不同的帧的用途

CAN ID 只对它发送的报文负责

标准的 CAN 通信, 其 ID 有 11 位, 是从 0x000 到 0x7ff之间的一个数值


冲突检测与避免(仲裁) – 链路层

如果一个设备 A 想发送显性电平, 另一个设备 B 想发送隐性电平. 而我们知道一根导线上不可能既是高电平又是低电平, 所以这个时候就发生了冲突

有了冲突, 通常有两种方式去对症, 一种是冲突检测, 在冲突已经出现了之后再进行处理, 一种是冲突避免, 在冲突出现之前就采取方式去避免出现真正的冲突

而 CAN 通信采用的冲突检测的方式

冲突检测 CD: 出现冲突, 然后解决冲突 因为 CAN 总线上的设备是可以一边发送一边监听的, 那么在 A 设备发送显性信号, B 设备发送隐性信号的情况下, 真实的总线上会变成显性信号, B 设备上一看: 链路上的信号怎么变成显性了?我发的明明是隐性啊, 原来有其他设备也在发消息, 我才是多余的. B 设备就会闭嘴. 这样就解决了这个冲突

冲突避免 CA: 预防可能出现的冲突 这个主要出现在通信工程中, 我们今天学习的 CAN 协议采用的冲突检测而不是冲突避免 在计算机网络中的冲突避免主要通过 CSMA/CA(载波侦听多路访问/冲突避免) 协议实现,常用于无线局域网中, 由于无线信号衰减及隐蔽终端问题无法做到实时冲突检测(CSMA/CD),系统改用礼貌等待和预约的方式防患未然

关于计算机网络的更多想了解的可以看看这里, 不过这和我们今天学的 CAN 有点不搭架😂


误码校验 – 链路层

在 CAN 协议中采用的是 CRC 校验 - Cyclic Redundancy Check

CAN 2.0 采用的是 CRC-15 简单来说就是生成多项式在搞来搞去的, 它相较于我们之前提到的奇偶校验有很大的优势, 任意一帧的 5 个随机位翻转都能被查出来

有关 CRC 校验的更多内容在番外篇中会继续讲解


CAN 的通信帧 – 链路层

在计算机网络上一般而言, 物理层叫信号, 链路层叫帧, 网络层叫报文, 传输层叫流, 再往上上叫数据, 当然也可以统一叫数据

CAN 的帧与 UART 只有一种 UART 帧不一样, CAN 的帧分为 4 种: 数据帧, 遥控帧, 错误帧, 过载帧 再加上用于分隔帧与帧之间的帧间隔

前两者需要开发者进行主动接收发送的, 后三者不用我们操心, 属于硬件设备自行发送的

  • 数据帧(DATA FRAME) 将数据从发送器传送给各接收器, 搬运数据用的. 你要发的信息就装在它里面
  • 遥控帧(REMOTE FRAME) 由某个总线单元发出, 用来请求发送具有相同标识符(IDENTIFIER)的数据帧, 点名要数据用的. 发一个带某 ID 的遥控帧, 意思是”负责这个 ID 的设备, 请把你的数据帧发出来”, 它自己不带任何数据, 遥控帧在 CAN FD 里已取消
  • 错误帧(ERROR FRAME) 由任何检测到总线错误的单元发出, 任何节点检出错误时发的. 它会打断当前这一帧、触发自动重传
  • 过载帧(OVERLOAD FRAME) 用于在前一帧与后一帧数据帧或遥控帧之间提供额外的延时. 某个接收设备忙不过来时, 用来往后插一小段延时、给自己争取喘息时间的, 似乎现在已经用不到了

数据帧和遥控帧通过帧间隔(INTERFRAME SPACE) 与前面的帧分隔开. 分隔两个主动帧(数据/遥控)之间的空档。这段时间里主动帧不能发, 但错误帧、过载帧这类自动帧可以插进来

后来的工程师发现 CAN 的 11 位不太够了, 于是出现了拓展帧…其分为拓展数据帧和拓展遥控帧

有关每个帧的具体内容, 我还是希望大家先去看手册再结合下面的表格来学习, 如果你的英文不尽如人意还是自己加把劲吧, 但是我这里也找到了对应的中文译本 ***

标准数据帧格式:

Pasted image 20260706020357 ### 标准遥控帧格式:

Pasted image 20260706022316 ### 拓展数据帧格式:

Pasted image 20260706022646

拓展遥控帧

Pasted image 20260706024029


接收过滤 – 链路层

为什么需要滤波器? CAN 是广播总线, 总线上的任何一帧所有节点都能够听到, 但是你这个节点通常只关心其中几个你需要的 ID. 如果没有过滤机制, CPU 就得为每一帧都被中断一次, 然后在软件里判断这个 ID 是否是我需要的. 这极大的浪费了 CPU 的资源

所以, CAN 控制器里做了个硬件滤波器, 你提前设定好哪些 ID 被接收, 其他的不需要的会被默默的抛弃

过滤接收与过滤排除

这是两种不同的模式, CAN 协议采取的是过滤接受模式, 它相当于白名单, 你去设定要接受的 ID 然后滤波器给你放进来

过滤排除是黑名单, 他排除你设定的不需要的东西, 没被排除的就会被接收进来

滤波器的工作

滤波器主要靠两个东西工作:

  • ID_filter(过滤目标): 你想要的 ID 长什么样
  • MASK(掩码): 逐位规定哪些位要被确定, 哪些位不用管可以随意

MASK 掩码就像是一张SMT钢网 Pasted image 20260707130112 mask 的某位是 1, 表示该位必须和过滤目标一致. 相当于钢网的镂空部分

mask 的某位是 0, 表示不关心该位的值, 可以随意. 类似于钢网的实心部分

举例:

  1. 一个CAN ID 为 0x114 的滤波器, 如果我要配置一个滤波器只能接收这个 CAN ID 的帧的话, 就需要配置它的过滤目标为 0x114, 掩码为 0x7ff
  2. 一个CAN ID 为 0x114~0x117 的滤波器, 如果我要配置一个滤波器只能接收这个 CAN ID 的帧的话, 就需要配置它的过滤目标为 0x114, 掩码为 0x7fc Pasted image 20260707130535

如果我们接收到一帧时, 判断收不收的标准是:(CANID&MASK)==(MASK&FILTERID)(相等接收,不等不收)