4. CAN远程帧与错误帧:远程帧的作用、错误帧的分类与处理、总线关闭状态
好,咱们今天聊点硬核的。远程帧和错误帧,这两个东西在CAN协议里不算最常用的,但一旦用上,往往都是关键时刻。我刚开始做呼吸机CAN通信那会儿,远程帧就帮我解决过一个挺头疼的同步问题。至于错误帧,嗯,这东西你最好永远别见到它——但真见到了,你得知道怎么处理。
4.1 远程帧:请求数据的“遥控器”
远程帧,说白了就是一个“请求”。它不携带数据,只带一个ID。发送方发出远程帧,告诉总线上的某个节点:“嘿,把你那个ID对应的数据发给我。”
你想想看,这在呼吸机系统里有什么用?举个例子:
- 主控板想知道当前流量传感器的实时数据
- 但传感器平时不主动上报,只在被问的时候才回答
- 这时候主控板发一个远程帧,传感器收到后立刻回复数据帧
这就是典型的“请求-响应”模式。我个人习惯把远程帧叫做“点名帧”——点到谁,谁回答。
远程帧的结构
远程帧和普通数据帧长得几乎一样,区别就两点:
- RTR位(Remote Transmission Request)置为1,表示这是远程帧
- 数据场长度为0,没有数据字节
看个标准帧的例子:
// 标准远程帧格式
// ID = 0x321,请求传感器数据
// RTR = 1,DLC = 0
// 发送远程帧的伪代码
void send_remote_frame(uint16_t id) {
CAN_TxHeaderTypeDef tx_header;
tx_header.StdId = id;
tx_header.RTR = CAN_RTR_REMOTE; // 远程帧
tx_header.DLC = 0; // 无数据
tx_header.IDE = CAN_ID_STD;
HAL_CAN_AddTxMessage(&hcan, &tx_header, NULL, &tx_mailbox);
}
我在项目中遇到过一个问题:某个传感器节点收到远程帧后,回复的数据帧和另一个节点的数据帧冲突了。后来发现是ID优先级没规划好。所以我的建议是——远程帧的ID最好比对应的数据帧ID低一点,这样远程帧的优先级更高,能更快拿到数据。
4.2 错误帧:总线的“警报器”
错误帧,是CAN节点发现自己或别人出错时,主动发出的一个“警报”。它不携带任何用户数据,纯粹是告诉总线:“出问题了,大家注意!”
错误帧分两种:主动错误帧和被动错误帧。这两种的区别,说白了就是“嗓门大小”不一样。
主动错误帧
当一个节点处于“主动错误”状态时,它发现错误后会发出6个连续的显性位。这6个位会破坏当前正在传输的帧,强制所有节点都检测到错误。这就是所谓的“错误主动”——嗓门大,谁都听得见。
// 主动错误帧结构
// 6个显性位(错误标志) + 8个隐性位(错误界定符)
// 波形示意:
// 正常帧... | 111111 | 00000000 | 正常帧...
// ^错误标志 ^界定符
被动错误帧
被动错误帧就“温柔”多了。节点发出6个连续的隐性位。因为隐性位会被显性位覆盖,所以如果总线上有其他节点在发数据,这个被动错误帧根本不会被注意到。说白了,就是“小声嘀咕”,只有自己知道。
// 被动错误帧结构
// 6个隐性位(错误标志) + 8个隐性位(错误界定符)
// 注意:隐性位可能被其他节点的显性位覆盖
为什么会这样?因为CAN协议的设计理念是:错误越严重的节点,发言权越小。主动错误节点可以大声喊,被动错误节点只能小声说,而总线关闭的节点——直接闭嘴。
4.3 错误处理机制:从“小错”到“闭嘴”
CAN控制器内部有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。这两个计数器决定了节点处于什么状态。
| 状态 | TEC | REC | 行为 |
|---|---|---|---|
| 主动错误 | 0-127 | 0-127 | 正常通信,发现错误发主动错误帧 |
| 被动错误 | 128-255 | 128-255 | 可通信,发现错误发被动错误帧 |
| 总线关闭 | >255 | - | 完全断开,不参与任何通信 |
我曾经在一个项目中遇到过总线关闭的问题。那台呼吸机的CAN总线时不时就“死”一次,重启后又好了。查了半天,发现是一个传感器节点的CAN收发器接触不良,导致发送错误频繁,TEC一路飙升到255以上,最终总线关闭。
4.4 总线关闭状态:最严重的错误
总线关闭,是CAN节点能进入的最糟糕的状态。这时候节点完全从总线上“消失”了,不发送任何数据,也不接收任何数据。说白了,就是“装死”。
为什么会进入总线关闭?通常是因为发送错误太多。比如:
- CAN收发器硬件故障
- 总线短路或断路
- 波特率配置错误
- ID冲突导致持续仲裁失败
在呼吸机系统里,总线关闭是绝对不能接受的。你想,如果流量传感器因为总线关闭而停止上报数据,呼吸机的通气控制就会出大问题。
如何恢复总线关闭?
有两种方式:
- 硬件复位:直接复位CAN控制器,TEC和REC清零
- 软件恢复:等待128个总线空闲信号(11个隐性位),然后自动恢复
我个人建议在呼吸机系统中使用硬件复位+软件重连的策略:
// 总线关闭恢复示例代码
void CAN_BusOff_Recovery(CAN_HandleTypeDef *hcan) {
// 1. 关闭CAN控制器
HAL_CAN_Stop(hcan);
// 2. 等待一段时间,让总线安静
HAL_Delay(10);
// 3. 重新初始化
HAL_CAN_Start(hcan);
// 4. 重新配置过滤器
CAN_FilterTypeDef filter;
// ... 配置过滤器参数 ...
HAL_CAN_ConfigFilter(hcan, &filter);
// 5. 重新激活通知
HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
// 6. 记录日志,方便排查
Error_Log("CAN bus-off recovered");
}
4.5 实战建议:呼吸机中的错误处理策略
在呼吸机这种医疗设备里,CAN总线的可靠性直接关系到患者安全。我总结了几条经验:
- 定期监控TEC/REC:每隔一段时间读取错误计数器,提前预警
- 实现冗余通信:关键数据通过两条CAN总线同时传输
- 总线关闭自动恢复:不要依赖人工重启,软件自动处理
- 错误日志记录:每次错误都记录下来,方便事后分析
嗯,远程帧和错误帧的内容就这些。你想想看,远程帧像不像一个“遥控器”?错误帧像不像一个“警报器”?理解了这两个角色,CAN协议的错误处理机制也就掌握了大半。下一章咱们聊聊CAN的滤波器设置,那东西在节点多的系统里特别有用。