文章目录
- [Autosar Nm - 被动唤醒和主动请求时 Nm 报文发送的差异](#Autosar Nm - 被动唤醒和主动请求时 Nm 报文发送的差异)
-
- 前言
- [1. AUTOSAR NM 核心概念与状态机](#1. AUTOSAR NM 核心概念与状态机)
-
- [1.1 主要状态机](#1.1 主要状态机)
- [1.2 两个核心 API(由 ComM 或 ASW 调用)](#1.2 两个核心 API(由 ComM 或 ASW 调用))
- [2. 主动请求(Active Request)的 Nm 报文发送流程](#2. 主动请求(Active Request)的 Nm 报文发送流程)
-
- [2.1 典型路径(自唤醒场景)](#2.1 典型路径(自唤醒场景))
- [2.2 主动请求完整调用时序图(Mermaid Sequence)](#2.2 主动请求完整调用时序图(Mermaid Sequence))
- [3. 被动唤醒(Passive Startup)的 Nm 报文发送流程](#3. 被动唤醒(Passive Startup)的 Nm 报文发送流程)
-
- [3.1 典型路径(被其他节点唤醒)](#3.1 典型路径(被其他节点唤醒))
- [3.2 被动启动调用时序图(与主动路径对比)](#3.2 被动启动调用时序图(与主动路径对比))
- [4. 差异对比总结](#4. 差异对比总结)
- [5. 为什么 OEM会有"主动时第一帧必须是 NM"的要求?](#5. 为什么 OEM会有“主动时第一帧必须是 NM”的要求?)
- [6. 代码层面的关键调用链路与传输决策(便于调试)](#6. 代码层面的关键调用链路与传输决策(便于调试))
-
- [6.1 主动 vs 被动路径调用链(文字版,便于快速 grep)](#6.1 主动 vs 被动路径调用链(文字版,便于快速 grep))
- [6.2 CanNm "是否立即发送第一帧 NM?"决策流程图](#6.2 CanNm “是否立即发送第一帧 NM?”决策流程图)
- [8. 总结](#8. 总结)
Autosar Nm - 被动唤醒和主动请求时 Nm 报文发送的差异
前言
在带网络管理的项目中,ECU 主动请求时需要第一帧发送的报文是网络管理报文,但是被动唤醒时 Nm 报文并不是第一帧发送的报文。本文通过这一疑问来学习其中的差异及原理。
1. AUTOSAR NM 核心概念与状态机
AUTOSAR Network Management(以 CanNm 为例)的主要目标是:
- 协调总线上所有 ECU 的睡眠与唤醒,降低整车功耗。
- 提供"网络请求(保持唤醒)"与"被动参与"的区分机制。
- 支持 Partial Networking (PN)、远程睡眠检测等高级特性。
1.1 主要状态机
状态机对比(非完整Nm状态机),特别标注了主动请求与被动启动两条路径的差异入口:

图 1-1 图片描述 :
AUTOSAR CanNm 简化状态机图(纵向布局)。从 BusSleep(总线睡眠)开始,存在两条主要唤醒路径:
-
左侧红色/橙色路径(主动请求) :由
Nm_NetworkRequest(ComM ActiveRequest_b = TRUE)触发。箭头直接指向 Repeat Message(重复报文状态),并在旁边有醒目标注框:"设置 ImmediateNmTx_u8 = 20、置 Active Wakeup Bit (CBV)、MsgCyclePeriod = 0(立即发送)"。这条路径会强制让 NM 报文以最高优先级(Immediate 突发)成为第一帧。 -
右侧绿色路径(被动唤醒) :由总线收到其他 ECU 的 NM PDU 触发,经过
Nm_NetworkStartIndication→ ComM 判断 →Nm_PassiveStartUp进入 Repeat Message。旁边标注:"不设置 Immediate,仅使用 MsgCycleOffset (100ms),无 Active Wakeup Bit"。因此 NM 报文会延迟发送,期间应用报文可能先发。
图中用一个大容器框标出 "Network Mode (网络模式)",里面包含 Repeat Message(短暂状态,用浅橙色边框突出)、Normal Operation(正常运行)和 Ready Sleep(就绪睡眠)。关键转换条件用文字清晰标注在箭头上,包括 RepeatTime 到期后的分支、NM-Timeout、WaitBusSleepTime 等。底部有图例说明红色/橙色代表主动路径、绿色代表被动路径。
进入 Network Mode 后,Repeat Message 是短暂的"宣告期",结束后根据是否有本地 Network Request 决定去 Normal Operation(持续保持网络)还是 Ready Sleep(可释放)。Ready Sleep 超时后进入 Prepare Bus Sleep,最终回到 Bus Sleep。整个图直观展示了为什么主动请求时 NM 几乎必然是第一帧,而被动时不是。
关键过渡(补充说明):
- Bus Sleep → 收到 NM PDU 或本地 NetworkRequest → 进入 Network Mode。
- 进入 Network Mode 后通常先进入 Repeat Message State(重复报文状态),该状态下会以较快周期发送 NM PDU(Immediate + 正常周期)。
- Repeat Message 结束后,根据是否有本地 Network Request 决定进入 Normal Operation(持续请求)还是 Ready Sleep(可释放)。
1.2 两个核心 API(由 ComM 或 ASW 调用)
| API | 语义 | 是否会"主动保持网络" | 典型调用场景 |
|---|---|---|---|
Nm_NetworkRequest |
主动请求网络(Active) | 是 | 自唤醒、点火、用户操作等 |
Nm_PassiveStartUp |
被动启动参与(Passive) | 否 | 收到其他节点 NM PDU 后的响应 |
ComM 是两者之间的"总指挥":它根据请求来源(ActiveRequest_b 标志)和 NmVariant(FULL/PASSIVE)决定调用哪个 API。
2. 主动请求(Active Request)的 Nm 报文发送流程
2.1 典型路径(自唤醒场景)
-
EcuM / BswM / ASW 检测到需要自唤醒(KL15、唤醒源、遥控等)。
-
通过 ComM_UserRequest 或 BswM 动作请求
COMM_FULL_COMMUNICATION+ Active。 -
ComM 状态机进入
COMM_FULL_COM_NETWORK_REQUESTED,发现ActiveRequest_b == TRUE,调用:cNm_NetworkRequest(channel); -
Nm 转发到 CanNm:
c// CanNm_NetworkRequest CanNm_RamData_s[...].NetworkReqState_en = CANNM_NETWORK_REQUESTED_E; // 如配置了 PnHandleMultiple... 还可能设置自发传输标志 -
CanNm MainFunction 在 BusSleep / PrepareBusSleep 状态处理:
- 检测到
NetworkReqState == REQUESTED - 设置 ImmediateNmTx_u8 = CanNmImmediateNmTransmissions (本项目配置为 20)
- 如果 CanNmActiveWakeupBitEnabled == TRUE,则在 CBV 中置 Active Wakeup Bit
- 调用
CanNm_GotoNetworkMode→CanNm_ChangeState(REPEAT_MESSAGE, NETWORK) - 调用
CanNm_StartTransmission
- 检测到
-
CanNm_StartTransmission关键逻辑:cRamPtr_ps->MsgTxStatus_b = TRUE; if (RamPtr_ps->ImmediateNmTx_u8 == 0) RamPtr_ps->MsgCyclePeriod = ConfigPtr_pcs->MsgCycleOffset; // 100ms else RamPtr_ps->MsgCyclePeriod = 0; // 立即发送! -
紧接着的
CanNm_MainFunctionTx检测到周期为 0(或首次失败重试),立刻调用CanNm_MessageTransmit→ CanIf_Transmit。- 随后根据 Immediate 计数器,用 ImmediateNmCycleTime(本项目 20ms)连续发 20 帧快速 NM。
- 之后恢复正常 MsgCycleTime(1s)。
结果 :ECU 主动请求时,其第一帧(或极早期的帧)必然是带 Active Wakeup Bit 的 NM 报文,并且以最快速度(Immediate 突发)发送。
2.2 主动请求完整调用时序图(Mermaid Sequence)
CAN 控制器 / 总线 CanIf CanNm Nm (Nm_NetworkRequest) ComM (FULL + Active) BswM EcuM / ASW / 唤醒源 CAN 控制器 / 总线 CanIf CanNm Nm (Nm_NetworkRequest) ComM (FULL + Active) BswM EcuM / ASW / 唤醒源 #mermaid-svg-S8z9g5fjePdOmu1S{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-S8z9g5fjePdOmu1S .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-S8z9g5fjePdOmu1S .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-S8z9g5fjePdOmu1S .error-icon{fill:#552222;}#mermaid-svg-S8z9g5fjePdOmu1S .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-S8z9g5fjePdOmu1S .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-S8z9g5fjePdOmu1S .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-S8z9g5fjePdOmu1S .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-S8z9g5fjePdOmu1S .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-S8z9g5fjePdOmu1S .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-S8z9g5fjePdOmu1S .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-S8z9g5fjePdOmu1S .marker{fill:#333333;stroke:#333333;}#mermaid-svg-S8z9g5fjePdOmu1S .marker.cross{stroke:#333333;}#mermaid-svg-S8z9g5fjePdOmu1S svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-S8z9g5fjePdOmu1S p{margin:0;}#mermaid-svg-S8z9g5fjePdOmu1S .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-S8z9g5fjePdOmu1S text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-S8z9g5fjePdOmu1S .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-S8z9g5fjePdOmu1S .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-S8z9g5fjePdOmu1S .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-S8z9g5fjePdOmu1S .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-S8z9g5fjePdOmu1S #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-S8z9g5fjePdOmu1S .sequenceNumber{fill:white;}#mermaid-svg-S8z9g5fjePdOmu1S #sequencenumber{fill:#333;}#mermaid-svg-S8z9g5fjePdOmu1S #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-S8z9g5fjePdOmu1S .messageText{fill:#333;stroke:none;}#mermaid-svg-S8z9g5fjePdOmu1S .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-S8z9g5fjePdOmu1S .labelText,#mermaid-svg-S8z9g5fjePdOmu1S .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-S8z9g5fjePdOmu1S .loopText,#mermaid-svg-S8z9g5fjePdOmu1S .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-S8z9g5fjePdOmu1S .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-S8z9g5fjePdOmu1S .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-S8z9g5fjePdOmu1S .noteText,#mermaid-svg-S8z9g5fjePdOmu1S .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-S8z9g5fjePdOmu1S .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-S8z9g5fjePdOmu1S .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-S8z9g5fjePdOmu1S .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-S8z9g5fjePdOmu1S .actorPopupMenu{position:absolute;}#mermaid-svg-S8z9g5fjePdOmu1S .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-S8z9g5fjePdOmu1S .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-S8z9g5fjePdOmu1S .actor-man circle,#mermaid-svg-S8z9g5fjePdOmu1S line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-S8z9g5fjePdOmu1S :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CanNm_MainFunctionTx 检测到 period==0\n立即执行 CanNm_MessageTransmit 此时任何非 NM 的 Com PDU 在 CanIf PnFilter\n或调度上仍被抑制(见姊妹文档 CanIfTxPduPnFilterPdu) 检测到自唤醒源 (KL15/遥控/内部) 1 ComM_RequestComMode(FULL_COM, ActiveRequest) 2 进入 COMM_FULL_COM_NETWORK_REQUESTED\nActiveRequest_b = TRUE 3 Nm_NetworkRequest(channel) 4 CanNm_NetworkRequest() 5 NetworkReqState = REQUESTED\nImmediateNmTx_u8 = 配置值(20)\n若 ActiveWakeupBitEnabled 则置 CBV Active Bit 6 GotoNetworkMode() → ChangeState(REPEAT_MESSAGE) 7 StartTransmission() → MsgCyclePeriod = 0\nMsgTxStatus_b = TRUE 8 CanIf_Transmit(NM PDU) 9 Can_Write → 仲裁/发送(第一帧 NM) 10 Tx 成功(硬件确认或软件确认) 11 CanNm_TxConfirmation 12 递减 Immediate 计数,使用 ImmediateNmCycleTime(20ms)\n继续突发剩余 19 帧 13 Nm_NetworkMode() 通知上层 14 进入 Ready Sleep 或 Normal Operation 15
图 2-1 图片描述 :
主动请求(自唤醒)端到端时序图。
从左到右展示了完整的软件调用链:EcuM/BswM 检测唤醒源 → ComM 以 ActiveRequest 请求 FULL_COM → NmInterface 转发 Nm_NetworkRequest → CanNm 设置 Immediate 计数器 + Active Wakeup Bit + 把 MsgCyclePeriod 强制设为 0 → CanIf 立刻发出第一帧 NM(带 Active Bit)。
图中用 autonumber 标注关键步骤,并特别用 Note 强调:此时 CanNm 的 Immediate 机制已确保 NM PDU 成为总线上最早出现的帧之一;后续 BswM 才会使能普通应用 PDU 组,进一步加固"第一帧是 NM"的要求。图右侧的 CAN 总线箭头表示硬件实际发送时刻。
3. 被动唤醒(Passive Startup)的 Nm 报文发送流程
3.1 典型路径(被其他节点唤醒)
-
总线上其他 ECU 已经活跃,发送了 NM PDU(或导致收发器唤醒的总线活动)。
-
CanNm 在 BusSleep 收到 RxIndication:
- 设置
PduRxInd_b - 调用
Nm_NetworkStartIndication
- 设置
-
ComM 收到 NetworkStartIndication(或 RestartIndication),判断为被动请求:
c// ComM_LChannelMainFunction.c if (numStateChanges_u8 >= 2) // 被动路径特征 Nm_PassiveStartUp(channel); -
CanNm_PassiveStartUp 执行:
cif (BusSleep || PrepareBusSleep) { CanNm_ChangeState(REPEAT_MESSAGE, NETWORK); Nm_NetworkMode(); #if (!PASSIVE_MODE) CanNm_StartTransmission(CanNm_NetworkHandle); // 直接启动,但不设 Immediate! #endif ... } -
StartTransmission此时看到ImmediateNmTx_u8 == 0(从未被主动路径设置过):cMsgCyclePeriod = MsgCycleOffset; // 本项目 100000us = 100ms -
因此,第一帧 NM 要在 MsgCycleOffset 之后 才会被 MainFunctionTx 触发发送。
-
期间,ECU 可能已经:
- 处理了触发唤醒的 App 报文
- 发送了 Com 层的周期报文或事件报文(如果调度恰好在此时)
- 响应了诊断/功能请求
结果 :被动唤醒时,Nm 报文通常不是 ECU 苏醒后发送的第一帧,而是按照配置的 Offset 正常排队。
3.2 被动启动调用时序图(与主动路径对比)
CanIf CanNm (Tx 侧) ComM (被动) Nm CanNm (Rx 侧) CAN 总线 其他 ECU(已活跃) CanIf CanNm (Tx 侧) ComM (被动) Nm CanNm (Rx 侧) CAN 总线 其他 ECU(已活跃) #mermaid-svg-eSBbdDRiKd6TvOQy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eSBbdDRiKd6TvOQy .error-icon{fill:#552222;}#mermaid-svg-eSBbdDRiKd6TvOQy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eSBbdDRiKd6TvOQy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eSBbdDRiKd6TvOQy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eSBbdDRiKd6TvOQy .marker.cross{stroke:#333333;}#mermaid-svg-eSBbdDRiKd6TvOQy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eSBbdDRiKd6TvOQy p{margin:0;}#mermaid-svg-eSBbdDRiKd6TvOQy .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eSBbdDRiKd6TvOQy text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-eSBbdDRiKd6TvOQy .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-eSBbdDRiKd6TvOQy .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-eSBbdDRiKd6TvOQy .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-eSBbdDRiKd6TvOQy .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-eSBbdDRiKd6TvOQy #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-eSBbdDRiKd6TvOQy .sequenceNumber{fill:white;}#mermaid-svg-eSBbdDRiKd6TvOQy #sequencenumber{fill:#333;}#mermaid-svg-eSBbdDRiKd6TvOQy #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-eSBbdDRiKd6TvOQy .messageText{fill:#333;stroke:none;}#mermaid-svg-eSBbdDRiKd6TvOQy .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eSBbdDRiKd6TvOQy .labelText,#mermaid-svg-eSBbdDRiKd6TvOQy .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-eSBbdDRiKd6TvOQy .loopText,#mermaid-svg-eSBbdDRiKd6TvOQy .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-eSBbdDRiKd6TvOQy .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-eSBbdDRiKd6TvOQy .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-eSBbdDRiKd6TvOQy .noteText,#mermaid-svg-eSBbdDRiKd6TvOQy .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-eSBbdDRiKd6TvOQy .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eSBbdDRiKd6TvOQy .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eSBbdDRiKd6TvOQy .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eSBbdDRiKd6TvOQy .actorPopupMenu{position:absolute;}#mermaid-svg-eSBbdDRiKd6TvOQy .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-eSBbdDRiKd6TvOQy .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eSBbdDRiKd6TvOQy .actor-man circle,#mermaid-svg-eSBbdDRiKd6TvOQy line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-eSBbdDRiKd6TvOQy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CanNm_MainFunctionTx 必须等到 Offset 到期\n才真正调用 CanIf_Transmit 在这 100ms 窗口内,ECU 很可能已经\n发送了 App 周期报文或响应了诊断请求 发送 NM PDU(或导致收发器唤醒的总线活动) 1 CanNm_RxIndication(NM PDU) 2 设置 PduRxInd_b\n调用 Nm_NetworkStartIndication 3 Nm_NetworkStartIndication() 4 ComM_Nm_NetworkStartIndication / RestartIndication 5 判断为被动请求 (numStateChanges >=2)\n进入 COMM_FULL_COM_READY_SLEEP 路径 6 Nm_PassiveStartUp(channel) 7 CanNm_PassiveStartUp() 8 ChangeState(REPEAT_MESSAGE, NETWORK)\nNm_NetworkMode() 9 StartTransmission()\n注意:ImmediateNmTx_u8 仍为 0 10 MsgCyclePeriod = MsgCycleOffset (100ms) 11 (100ms 后)CanIf_Transmit(NM PDU) 12 发送本 ECU 的第一帧 NM(无 Active Bit) 13 TxConfirmation 14 CanNm_TxConfirmation 15
图 3-1 图片描述 :
被动唤醒(被其他 ECU 叫醒)时序图,与图 2-1 形成鲜明对比。
关键差异点用 Note 突出:
- 入口是 RxIndication 而非本地 NetworkRequest。
- ComM 走被动分支,调用
Nm_PassiveStartUp而非Nm_NetworkRequest。 - CanNm StartTransmission 时 Immediate 计数器为 0,周期直接使用配置的 MsgCycleOffset(本项目 100ms)。
- 因此在 NM PDU 真正上总线之前,ECU 的应用层报文已有足够时间被调度和发送。
- 最终发出的 NM PDU 不带 Active Wakeup Bit,语义上是"参与者"而非"唤醒源"。
4. 差异对比总结
| 维度 | 主动请求 (Nm_NetworkRequest) | 被动启动 (Nm_PassiveStartUp) |
|---|---|---|
| 调用来源 | ComM ActiveRequest / ASW 明确请求 | 收到 NM PDU → NetworkStartIndication → ComM |
| ImmediateNmTx_u8 | 被显式设置为配置值(本项目 20) | 保持为 0(除非之前残留) |
| MsgCyclePeriod 初始值 | 0(立即)或受 Immediate 影响 | MsgCycleOffset(本项目 100ms) |
| Active Wakeup Bit (CBV) | 设置(若 ActiveWakeupBitEnabled) | 不设置 |
| 第一帧 NM 发送时机 | 极早(可作为 ECU 苏醒后的第一帧) | 延迟 Offset 时间 |
| 是否声明"是我唤醒的网络" | 是(通过 Active Bit + 立即 NM) | 否(只是参与) |
| Repeat Message 行为 | 正常进入 + 可能多帧 Immediate | 可能直接因 RepeatTime=0 快速进入 Ready Sleep |
5. 为什么 OEM会有"主动时第一帧必须是 NM"的要求?
常见工程与诊断原因:
- 唤醒源识别:整车 Sleep Coordinator 或 Gateway 需要通过 NM 的 Active Bit + 最早出现的 NM 报文,快速判断"是谁把总线从 Sleep 拉起来的"。
- 总线仲裁与时序:保证唤醒节点在总线稳定后第一时间宣告存在,避免其他节点因看不到 NM 而误判睡眠。
- 诊断与售后:UDS 或车厂工具可通过"第一帧是否 NM + 是否带 Active Bit"区分正常被动参与 vs 异常自唤醒(有助于定位暗电流、误唤醒问题)。
- Partial Networking 兼容:PN 场景下,主动请求的节点必须用 NM(含 PN 信息)快速把相关 Partial Network 叫醒。
- 与 Com 调度解耦:把"网络管理宣告"放在 NM 层统一处理,不依赖上层 SWC 的周期报文调度顺序。
被动唤醒场景下,ECU 只是"被叫醒的参与者",不需要(也不应该)抢先宣告自己是唤醒源,因此让 App 报文或正常周期先发是允许的。
6. 代码层面的关键调用链路与传输决策(便于调试)
6.1 主动 vs 被动路径调用链(文字版,便于快速 grep)
主动路径:
ComM_RequestComMode(FULL, Active)
→ ComM_Prv_FullComNetworkRequested_StateHandling
→ Nm_NetworkRequest
→ CanNm_NetworkRequest (设 REQUESTED + 可设 PnHandleMultiple)
→ CanNm_MainFunction (CaseBusSleep/Prepare)
→ 设 Immediate + ActiveBit
→ CanNm_GotoNetworkMode
→ CanNm_StartTransmission (period=0)
→ MainFunctionTx → CanIf_Transmit (第一帧 NM)
被动路径:
CanNm_RxIndication (NM PDU)
→ Nm_NetworkStartIndication
→ ComM_Nm_NetworkStartIndication
→ ComM 状态机 (被动)
→ Nm_PassiveStartUp
→ CanNm_PassiveStartUp
→ CanNm_StartTransmission (period = Offset)
→ MainFunctionTx(100ms 后才发 NM)
6.2 CanNm "是否立即发送第一帧 NM?"决策流程图
#mermaid-svg-jCxOyINRpjkEgwwm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jCxOyINRpjkEgwwm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jCxOyINRpjkEgwwm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jCxOyINRpjkEgwwm .error-icon{fill:#552222;}#mermaid-svg-jCxOyINRpjkEgwwm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jCxOyINRpjkEgwwm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jCxOyINRpjkEgwwm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jCxOyINRpjkEgwwm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jCxOyINRpjkEgwwm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jCxOyINRpjkEgwwm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jCxOyINRpjkEgwwm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jCxOyINRpjkEgwwm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jCxOyINRpjkEgwwm .marker.cross{stroke:#333333;}#mermaid-svg-jCxOyINRpjkEgwwm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jCxOyINRpjkEgwwm p{margin:0;}#mermaid-svg-jCxOyINRpjkEgwwm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jCxOyINRpjkEgwwm .cluster-label text{fill:#333;}#mermaid-svg-jCxOyINRpjkEgwwm .cluster-label span{color:#333;}#mermaid-svg-jCxOyINRpjkEgwwm .cluster-label span p{background-color:transparent;}#mermaid-svg-jCxOyINRpjkEgwwm .label text,#mermaid-svg-jCxOyINRpjkEgwwm span{fill:#333;color:#333;}#mermaid-svg-jCxOyINRpjkEgwwm .node rect,#mermaid-svg-jCxOyINRpjkEgwwm .node circle,#mermaid-svg-jCxOyINRpjkEgwwm .node ellipse,#mermaid-svg-jCxOyINRpjkEgwwm .node polygon,#mermaid-svg-jCxOyINRpjkEgwwm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jCxOyINRpjkEgwwm .rough-node .label text,#mermaid-svg-jCxOyINRpjkEgwwm .node .label text,#mermaid-svg-jCxOyINRpjkEgwwm .image-shape .label,#mermaid-svg-jCxOyINRpjkEgwwm .icon-shape .label{text-anchor:middle;}#mermaid-svg-jCxOyINRpjkEgwwm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jCxOyINRpjkEgwwm .rough-node .label,#mermaid-svg-jCxOyINRpjkEgwwm .node .label,#mermaid-svg-jCxOyINRpjkEgwwm .image-shape .label,#mermaid-svg-jCxOyINRpjkEgwwm .icon-shape .label{text-align:center;}#mermaid-svg-jCxOyINRpjkEgwwm .node.clickable{cursor:pointer;}#mermaid-svg-jCxOyINRpjkEgwwm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jCxOyINRpjkEgwwm .arrowheadPath{fill:#333333;}#mermaid-svg-jCxOyINRpjkEgwwm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jCxOyINRpjkEgwwm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jCxOyINRpjkEgwwm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jCxOyINRpjkEgwwm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jCxOyINRpjkEgwwm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jCxOyINRpjkEgwwm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jCxOyINRpjkEgwwm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jCxOyINRpjkEgwwm .cluster text{fill:#333;}#mermaid-svg-jCxOyINRpjkEgwwm .cluster span{color:#333;}#mermaid-svg-jCxOyINRpjkEgwwm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jCxOyINRpjkEgwwm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jCxOyINRpjkEgwwm rect.text{fill:none;stroke-width:0;}#mermaid-svg-jCxOyINRpjkEgwwm .icon-shape,#mermaid-svg-jCxOyINRpjkEgwwm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jCxOyINRpjkEgwwm .icon-shape p,#mermaid-svg-jCxOyINRpjkEgwwm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jCxOyINRpjkEgwwm .icon-shape .label rect,#mermaid-svg-jCxOyINRpjkEgwwm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jCxOyINRpjkEgwwm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jCxOyINRpjkEgwwm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jCxOyINRpjkEgwwm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RxIndication 被动
NetworkReqState==REQUESTED 主动
是
否
CanNm_MainFunction
检测到进入 BusSleep/PrepareBusSleep
收到 RxIndication
或 NetworkReqState==REQUESTED ?
设置 FirstMessage 标志
调用 Nm_NetworkStartIndication
设置 ImmediateNmTx_u8 = 配置值
若 ActiveWakeupBitEnabled 则置 CBV
CanNm_GotoNetworkMode
CanNm_StartTransmission
ImmediateNmTx_u8 > 0 ?
MsgCyclePeriod = 0
准备立即发送
MsgCyclePeriod = MsgCycleOffset
正常排队
MainFunctionTx 下一轮
立刻 CanNm_MessageTransmit
→ CanIf_Transmit
(第一帧 NM)
等待 Offset 到期后
才发送第一帧 NM
递减 Immediate 计数
后续用 ImmediateNmCycleTime 连续发送
Immediate 用完后
恢复正常 MsgCycleTime
图 6-1 图片描述 :
CanNm 内部"第一帧是否立即发送 NM"的核心决策流程图。
左侧分支(主动):NetworkReqState == REQUESTED → 显式设置 Immediate 计数 + Active Bit → StartTransmission 把周期压到 0 → MainFunctionTx 立即发送。
右侧分支(被动):仅 RxIndication 触发 → 不碰 Immediate 计数 → 直接使用配置的 MsgCycleOffset。
这个流程图对应 CanNm_Inl.h 中的 CaseBusSleep、CasePrepareBusSleep、CanNm_GotoNetworkMode、CanNm_StartTransmission 以及 CanNm_MainFunctionTx 的联合逻辑,是理解"主动 vs 被动"差异的最核心代码路径可视化。
8. 总结
- 根本差异 来自 CanNm 对
NetworkReqStatevsRxIndication两条路径的差异化处理:主动路径会强制设置 Immediate 计数 + Active Wakeup Bit 并把 MsgCyclePeriod 压到 0;被动路径只做最基本的StartTransmission,使用配置的MsgCycleOffset。 - 第一帧是否为 NM 不是 Nm 模块的"硬性规定",而是 配置参数(ImmediateNmTransmissions、MsgCycleOffset、ActiveWakeupBitEnabled) + 调用时机(ComM Active vs Passive) 共同作用的结果。
- 第一帧保证是Nm报文可以通过CanIfTxPduPnFilterPdu 配置实现
理解这一机制后,在设计唤醒策略、诊断策略、以及 Com 报文调度时,就能有意识地利用 NM 的这一行为,而不是盲目依赖上层 SWC 的初始化顺序。