4、通信控制(0x28):报文发送使能/禁止、网络管理报文的控制策略

4.1 这个服务到底在管什么?

通信控制服务,服务ID是0x28。说白了,它就是一把“总闸”。

你想想看,BMS在工作的时候,CAN总线上跑着各种报文:有周期性的状态报文,有诊断请求/响应,还有网络管理报文。但有些特殊场景下,比如产线刷写、Bootloader跳转、或者某些故障保护模式,我们需要临时关掉一部分报文,或者只允许特定报文通过。

0x28服务就是干这个的。它允许诊断仪(Tester)去控制ECU的通信行为——让某些报文发,或者不让某些报文发。

我个人习惯把0x28服务理解成“通信权限管理器”。它不修改报文内容,只控制报文的“发送权”和“接收权”。

核心要点:0x28服务控制的是ECU的通信行为,不是控制总线上的其他节点。它只影响被诊断的ECU自己。

4.2 控制类型与子功能

0x28服务通过子功能参数来区分具体要控制什么。常见的子功能有这几个:

子功能(SubFunction) 含义 典型场景
0x00 使能Rx/Tx(恢复通信) 退出诊断会话后恢复所有报文
0x01 禁止Rx/Tx(完全静默) Bootloader跳转前禁止所有通信
0x02 禁止Tx(只禁止发送) 产线测试时不让ECU发报文干扰
0x03 禁止Rx(只禁止接收) 特殊故障注入测试
0x04 使能Rx/Tx + 网络管理报文 恢复通信但保留网络管理报文
0x05 禁止Rx/Tx + 网络管理报文 静默模式但保持网络管理活跃

嗯,这里要注意:子功能0x04和0x05是专门针对网络管理报文的。很多工程师容易忽略这一点。

4.3 网络管理报文的特殊地位

为什么要把网络管理报文单独拎出来?

我在项目中遇到过这样一个坑:有一次做产线刷写,我们想通过0x28服务把BMS的所有报文都禁掉,只留诊断通信。结果刷到一半,整车网络管理器发现BMS的NM报文丢了,直接判定BMS“离线”,触发了整车下电。

你想想看,刷写过程中突然下电,后果是什么?ECU变砖的风险极高。

所以,0x28服务专门设计了针对网络管理报文的控制位。你可以选择:

  • 完全禁止所有报文(包括NM报文)——适用于ECU即将进入休眠或Bootloader跳转
  • 只禁止应用报文,保留NM报文——适用于需要保持网络活跃的诊断场景
  • 只禁止NM报文,保留应用报文——这个用得少,但某些测试场景会用到

我的建议:在产线刷写或在线升级场景中,尽量使用子功能0x05(禁止Rx/Tx + 保留网络管理报文)。这样既能保证诊断通信不受干扰,又能让整车网络管理器认为BMS还在“活着”。

4.4 报文控制的具体策略

0x28服务控制的是“报文级别”,不是“信号级别”。什么意思呢?

比如BMS会周期发送0x100(电池状态)、0x200(故障码)、0x300(NM报文)。当你执行0x28禁止Tx时,这三个报文全部停止发送。但如果你只想停掉0x100,保留0x200和0x300,那0x28服务就做不到了——你得用其他手段,比如修改应用层的发送使能标志位。

在实际代码实现中,我一般会这样做:

/* 伪代码示例:0x28服务处理逻辑 */
void Diag_Handle_28(uint8_t subFunc, uint8_t *data, uint16_t len)
{
    switch(subFunc)
    {
        case 0x00:  /* 使能所有通信 */
            g_ComCtrl_TxEnable = TRUE;
            g_ComCtrl_RxEnable = TRUE;
            g_ComCtrl_NM_Enable = TRUE;
            break;
            
        case 0x01:  /* 禁止所有通信 */
            g_ComCtrl_TxEnable = FALSE;
            g_ComCtrl_RxEnable = FALSE;
            g_ComCtrl_NM_Enable = FALSE;
            break;
            
        case 0x05:  /* 禁止Rx/Tx,但保留NM */
            g_ComCtrl_TxEnable = FALSE;
            g_ComCtrl_RxEnable = FALSE;
            g_ComCtrl_NM_Enable = TRUE;  /* NM报文不受影响 */
            break;
            
        default:
            /* 返回NRC 0x12(子功能不支持) */
            break;
    }
    
    /* 注意:这里要立即生效,不能有延迟 */
    CanIf_SetCommunicationMode(g_ComCtrl_TxEnable, 
                               g_ComCtrl_RxEnable, 
                               g_ComCtrl_NM_Enable);
}

警告:0x28服务执行后必须立即生效。不能等到下一个10ms周期任务再去处理。否则在临界状态下,ECU可能已经发出了不该发的报文。

4.5 避坑指南:我曾经踩过的雷

我曾经在一个项目中,把0x28服务的控制逻辑放在了应用层任务里。结果呢?诊断仪发了禁止Tx的请求,但应用层任务要等10ms后才执行。这10ms里,BMS又发出了一帧报文,恰好被整车控制器收到,触发了故障。

后来我改了设计:0x28服务直接在CAN接口层(CanIf)处理,不经过应用层。这样能做到“请求到达,立即生效”。

还有一个坑:0x28服务控制的是“发送使能”,不是“发送完成”。有些工程师误以为禁止Tx后,正在发送的报文会被中断。其实不会——正在发送的报文会正常发完,只是下一帧不再发送。

4.6 与诊断会话的关系

0x28服务跟诊断会话状态是联动的。我记得有一次测试,发现0x28服务在默认会话下也能执行成功,但退出诊断后,ECU自动恢复了所有通信。

这是因为UDS协议规定:

  • 0x28服务可以在任何会话下执行(默认会话、扩展会话、编程会话)
  • 但退出诊断会话时,ECU必须自动恢复通信(相当于执行了一次子功能0x00)
  • 如果ECU进入休眠或复位,通信也会自动恢复

所以,0x28服务的效果是“临时的”。它不会永久改变ECU的通信行为。这一点在开发时要特别注意——不要指望通过0x28服务永久关掉某个报文。

4.7 实际应用场景总结

最后,我总结一下0x28服务在BMS开发中的典型用法:

  1. 产线刷写:使用子功能0x05,禁止应用报文,保留NM报文。刷写完成后用0x00恢复。
  2. Bootloader跳转:使用子功能0x01,完全静默。跳转前确保所有报文都停了。
  3. 故障注入测试:使用子功能0x02或0x03,单独控制发送或接收,模拟通信故障。
  4. 网络管理测试:使用子功能0x04或0x05,验证NM报文的独立控制逻辑。

嗯,0x28服务看起来简单,但用不好真的会出大问题。尤其是跟网络管理报文相关的控制策略,一定要跟整车网络团队对齐。别像我当年那样,刷写刷到一半被整车下电,那滋味可不好受。