4、Prepare Bus-Sleep Mode:进入条件、停留时间(T_WaitBusSleep)、超时处理机制

好,咱们接着聊网络管理的状态机。前面我们把 Repeat 和 Ready Sleep 都捋了一遍,现在终于到了准备进入休眠的最后一站——Prepare Bus-Sleep 模式。

这个模式,说白了就是“临门一脚”。车上的 ECU 已经决定要睡了,但还不能直接断电,得先跟总线打个招呼,确保没有遗漏的消息。我个人习惯把这个阶段叫做“礼貌性等待”——给总线一个缓冲时间。

4.1 进入条件:什么时候触发 Prepare Bus-Sleep?

进入这个模式的条件其实很清晰,就两条路:

  • 从 Ready Sleep 模式进入:当网络管理器处于 Ready Sleep 状态,并且 CanNm_PrepareBusSleep 这个内部事件被触发时,就会跳转过来。这个事件通常由 CanNm 模块自己产生,说白了就是“我准备好了,可以睡了”。
  • 从 Bus-Sleep 模式被唤醒后超时:嗯,这个情况有点绕。如果 ECU 已经睡下去了(Bus-Sleep),但被一个无效的唤醒事件给叫醒了,然后在一定时间内没有收到有效的网络报文,它也会回到 Prepare Bus-Sleep。这算是一种“假醒”后的自我保护。

我在项目中遇到过一种情况:某个传感器节点因为电源纹波干扰,频繁产生“假唤醒”。结果它就在 Bus-Sleep 和 Prepare Bus-Sleep 之间反复横跳,功耗根本降不下来。后来查了半天,是硬件滤波没做好。

核心判断逻辑:说白了,只要网络管理器认为“当前没有通信需求,且所有应用都同意休眠”,它就会尝试进入 Prepare Bus-Sleep。

4.2 停留时间:T_WaitBusSleep 到底等多久?

进入 Prepare Bus-Sleep 后,ECU 并不会立刻断电。它会停留一段固定的时间,这个时间就是 T_WaitBusSleep

这个参数在 AUTOSAR 规范里是有明确规定的。你想想看,为什么要等?因为总线上可能还有最后一个网络管理报文正在传输,或者别的节点正在发送。如果直接睡过去,可能会漏掉关键信息。

参数 典型值 说明
T_WaitBusSleep 通常配置为 1~2 秒 从进入 Prepare Bus-Sleep 到真正进入 Bus-Sleep 的等待时间
最小值 至少大于一个网络管理报文的发送周期 确保不会错过任何正在传输的报文

嗯,这里要注意:T_WaitBusSleep 的计时是连续的。一旦进入这个状态,定时器就开始跑。如果在等待期间收到了任何有效的网络管理报文,那对不起,你得立刻跳出这个状态,回到 Repeat 或者 Normal 模式。

我的经验:我曾经在一个项目中把 T_WaitBusSleep 配成了 500ms,结果发现总线负载高的时候,ECU 总是睡不下去。后来分析日志才发现,是因为等待时间太短,总线上最后一个报文还没发完,定时器就超时了,导致状态机混乱。建议至少配成 1.5 倍的网络管理报文周期。

4.3 超时处理机制:等不到就睡,还是继续等?

好,现在 ECU 在 Prepare Bus-Sleep 里等着。如果 T_WaitBusSleep 计时结束,期间没有任何网络活动,那会发生什么?

答案是:ECU 会正常进入 Bus-Sleep 模式。这是最理想的情况,一切顺利。

但现实往往没那么简单。我们来看看几种超时场景:

  • 场景一:正常超时 —— 计时结束,无任何网络事件。ECU 调用 CanNm_Deactivate() 关闭网络管理,进入 Bus-Sleep。这是标准流程。
  • 场景二:超时前收到有效报文 —— 计时器还没跑完,总线上来了一个有效的网络管理报文。ECU 立刻停止计时,退出 Prepare Bus-Sleep,进入 Repeat 或 Normal 模式。这相当于“被叫醒”。
  • 场景三:超时前收到本地唤醒事件 —— 比如用户按了一下开关,或者诊断仪连上了。ECU 同样会退出 Prepare Bus-Sleep,进入 Repeat 模式,重新开始网络通信。

避坑指南:我曾经遇到过一个 bug,ECU 在 Prepare Bus-Sleep 状态下收到了一个无效的唤醒事件(比如一个毛刺信号),结果它没有正确处理,直接跳到了 Bus-Sleep。然后因为唤醒源没有清除,又立刻被唤醒,形成了一个死循环。正确的做法是:在进入 Bus-Sleep 前,必须确认所有唤醒源都已经清除。

超时处理机制的代码逻辑,其实就是一个简单的状态机判断:

// 伪代码示例:Prepare Bus-Sleep 超时处理
void CanNm_PrepareBusSleepTimeout(void)
{
    // 检查是否有待处理的网络事件
    if (CanNm_IsNetworkActive() == TRUE)
    {
        // 有网络活动,不能睡
        CanNm_GoToRepeatMode();
    }
    else if (CanNm_IsLocalWakeupPending() == TRUE)
    {
        // 有本地唤醒事件,也不能睡
        CanNm_GoToRepeatMode();
    }
    else
    {
        // 一切安静,进入 Bus-Sleep
        CanNm_GoToBusSleep();
    }
}

你看,逻辑其实不复杂。但关键在于时序。T_WaitBusSleep 的计时精度、网络报文的到达时间、本地唤醒事件的检测时机,这些都需要在系统设计时仔细考虑。

4.4 小结一下

Prepare Bus-Sleep 这个模式,说白了就是“最后的确认”。它给了 ECU 一个机会,在真正休眠前再听一听总线上有没有动静。T_WaitBusSleep 这个参数,配短了容易出问题,配长了又影响休眠效率。我个人建议:根据项目实际的总线负载和报文周期来调整,不要直接用默认值

下一章我们会讲 Bus-Sleep 模式本身,以及如何从休眠中优雅地唤醒。到时候再聊。