排错专题:CAN 错误计数与 Bus-Off
当程序显示 TWAI 初始化成功,但收不到电机响应时,先不要改 SDO 对象地址。CAN 控制器的错误状态能帮助判断问题还在物理总线,还是已经进入 CANopen 层。
先看三个现象
| 现象 | 首先怀疑 |
|---|---|
| 一帧都收不到,发送错误持续增加 | 电机未供电、CANH/CANL、位速率、终端 |
能收到 0x701,但某个 SDO 返回 Abort | CAN 已通,转查对象和权限 |
| 曾能通信,随后进入 Bus-Off | 总线持续错误、接线松动、位速率异常或严重干扰 |
TEC 和 REC 是什么
CAN 控制器内部维护错误计数:
| 计数 | 英文 | 主要反映 |
|---|---|---|
| TEC | Transmit Error Counter | 本节点发送时遇到的错误 |
| REC | Receive Error Counter | 本节点接收时遇到的错误 |
计数不是简单的“错误总次数”。CAN 规则会按错误类型增加或减少它们,并据此改变节点错误状态。

三种关键状态
| 状态 | 可以怎样理解 |
|---|---|
| Error Active | 正常通信,发现错误时主动通知总线 |
| Error Passive | 错误计数已经较高,控制器限制自己的错误影响 |
| Bus-Off | 发送错误严重,控制器退出总线发送 |
Bus-Off 不是 CANopen 状态,也不是电机的 CiA 402 Fault。它发生在 CAN 控制器层。
ACK 能证明什么
一帧发送时,总线上至少另一个正常接收节点会在 ACK 槽给出确认。如果没有任何节点 ACK,发送端会认为本次发送失败并重试,TEC 往往继续增加。
因此:
- TX 成功可以证明总线上有节点正确接收到了 CAN 帧并给出 ACK;
- ACK 不能证明电机应用接受了命令;
- SDO 下载成功还需要看到电机的
0x581成功响应; - 电机实际执行还要再读状态和反馈。
按顺序检查,不要同时改五项
- 给电机和底板断电。
- 测量 CANH 与 CANL 之间电阻,双端 120 Ω 时应接近 60 Ω。
- 核对 CANH 对 CANH、CANL 对 CANL,不能互换。
- 核对电机和控制板共地。
- 重新供电,确认电机 LED 有正常状态。
- 确认两端均使用 500 kbit/s Classical CAN。
- 只运行接收实验,观察是否出现
0x701。 - 接收正常后再运行 SDO 实验。
如果第 7 步能稳定收到 0x701,就不应继续把主要精力放在终端电阻或 TX/RX 引脚上。
TX 和 RX 引脚别与 CANH/CANL 混淆
本实验信号路径是:
| 芯片侧 | 收发器侧 | 总线侧 |
|---|---|---|
| ESP32 GPIO19 | TJA1050 TXD | CANH/CANL |
| ESP32 GPIO18 | TJA1050 RXD | CANH/CANL |
GPIO19/18 是 3.3 V 数字信号;CANH/CANL 是差分总线信号。不能把 ESP32 GPIO 直接接电机 CANH/CANL。
Bus-Off 恢复不是快速重启循环
进入 Bus-Off 后,应用可以发起恢复,但必须让控制器和总线满足 CAN 规定的恢复条件。正确思路是:
- 记录 Bus-Off 时间和错误计数;
- 停止继续提交 CANopen 请求;
- 等待一段受控时间,不在日志循环中不停重启;
- 调用当前 ESP-IDF 驱动提供的恢复流程;
- 确认恢复完成后,再逐步恢复通信;
- 如果短时间再次 Bus-Off,停止自动尝试并检查硬件。
无限快速重启会刷屏、增加总线干扰,还会掩盖真正的接线或位速率问题。
ESP-IDF v5.5.4 TWAI 官方文档:Two-Wire Automotive Interface
记录故障时保存什么
不要只发最后一句 Bus-Off。保存:
- 首次错误前后至少 5 秒日志;
- TEC、REC 和总线错误计数;
- TX 成功、TX 失败、仲裁丢失、Error Passive、Bus-Off 的时间顺序;
- 当时运行的工程和命令;
- 电机 LED、供电电压和 CANH/CANL 断电电阻;
- 是否能收到任何其他节点的 Heartbeat。
这些信息足以把“完全没有物理通信”和“通信后协议被拒绝”分开。