详解AUTOSAR:CanXcp传输链路与PDU离线队列机制
ECU 完成 XCP 初始化后,测量标定工具仍可能收不到任何响应。一个常见原因不是 CAN 通道或 A2L 文件错误,而是 CanXcp 初始化后处于 CANXCP_SET_OFFLINE:协议层可以产生响应,传输层却不会把帧发到总线。如果项目没有 CanSM,或者使用的状态管理器不会切换 CanXcp 的 PDU 模式,就必须由应用显式调用 CanXcp_SetPduMode() 进入在线状态。
这个启动细节揭示了 CanXcp 的核心职责。XCP Protocol Layer 负责命令和会话语义,CanXcp 作为 XCP on CAN Transport Layer,负责在协议层与 AUTOSAR CanIf 之间搬运 CTO/DTO、管理发送确认、缓存暂时无法发送的帧,并通过主函数重试队列和触发 DAQ 发送。
CanXcp 是硬件无关的传输协议实现,能够移植到不同 CAN 控制器,但微控制器、编译器和存储模型的组合很多,并不承诺在所有组合上都无需适配即可运行。这里所说的 Application 也不局限于用户应用代码,而是泛指调用 CanXcp 的任意上层软件。
1. CanXcp 的职责边界
XCP 栈可拆成协议语义和总线传输两层。协议层解析 CONNECT、测量、标定、DAQ 等 XCP 语义;CanXcp 不重复实现这些命令,而是把接收到的 XCP L-SDU 交给协议层,再把协议层产生的响应或异步数据交给 CanIf。
| 层次 | 主要职责 | 与 CanXcp 的接口 |
|---|---|---|
| XCP Master | 发出命令、接收响应和测量数据 | 通过 CAN/CAN FD 报文与 ECU 通信 |
| XCP Protocol Layer | 命令解析、会话状态、DAQ/STIM 等协议处理 | 调用 CanXcp_Send,接收 Xcp_Command 和 Xcp_SendCallBack |
| CanXcp | XCP on CAN 报文收发、PDU 模式、发送队列、确认转发 | 对上连接协议层,对下连接 CanIf |
CanIf |
CAN L-PDU 收发与确认 | 调用 Xcp_CanIfRxIndication、Xcp_CanIfTxConfirmation |
CAN 与 CAN FD 的差异直接体现在单个 XCP Packet 的长度上:经典 CAN PDU 最多承载 8 Byte;CAN FD Mode 2 最多承载 64 Byte。该上限还会影响全局 MaxCto、MaxDto 在各通道上的实际有效值。
| 总线类型 | XCP Packet 长度 | CanXcp 发送接口的 len 范围 |
|---|---|---|
| Classical CAN | 最多 8 Byte | 1..8 |
| CAN FD Mode 2 | 最多 64 Byte | 1..64 |
2. 从命令接收到发送确认的完整链路
接收路径从 CanIf 回调开始。CAN Interface 收到 XCP L-PDU 后调用 Xcp_CanIfRxIndication(),CanXcp 根据接收 PDU ID 和 PduInfoType 中的长度、数据指针,把 Packet 交给 XCP Protocol Layer。协议层处理命令后调用 CanXcp_Send() 提交响应,CanXcp 再通过 CanIf_Send() 发送。
发送成功并不以 CanIf_Send() 返回为终点。CAN Interface 完成 L-PDU 发送后调用 Xcp_CanIfTxConfirmation(),CanXcp 再通过 Xcp_SendCallBack 把确认传回协议层。SERV、EV 和 DAQ 等异步 XCP Packet 也沿用这条发送与确认链路。

这条链路中,Rx PDU ID 用于识别收到的 CAN L-PDU,Tx PDU ID 用于关联成功发送的 L-PDU。所有相关 API 都是 void,接口表没有定义返回错误码,因此链路推进依赖正确的 PDU 映射、回调配置和发送确认,而不是上层轮询返回值。
3. PDU 模式、发送队列与重试
CanXcp 初始化后的 PDU 模式固定为 CANXCP_SET_OFFLINE。通常由 CAN State Manager 把它切换为在线;没有 CanSM,或状态管理器来自其他供应商时,应由集成代码调用:
CanXcp_SetPduMode(XcpNwH, CANXCP_SET_ONLINE);
如果没有完成这一步,CanXcp 不会发送 XCP 帧。进入离线模式并不等价于立即丢弃待发送数据:帧会保存在 Send Queue 中,直到重新在线或队列发生 overrun。

CanXcp_MainFunction() 负责处理未能立即发送而进入队列的消息,并触发 CAN 上的 DAQ 消息。一般情况下由 Schedule Manager(SchM)周期调用;没有 SchM 或使用第三方 SchM 时,应用必须自行周期调度。

主函数周期有两种不同要求,不能只记住一个固定值。
| 场景 | 建议周期 | 原因 |
|---|---|---|
CanIf 具备正常发送排队能力 |
多数情况下 5 ms 或 10 ms 足够 |
主函数主要处理未能立即发送的排队消息,并触发 DAQ |
CanIf 的 transmit queue 被关闭 |
推荐 1 ms |
总线忙时 XCP Slave 消息可能需要多次请求;调用越快,重新参与总线仲裁越早 |
当 CanIf 没有发送队列时,CanXcp 自身的主函数重试就成为关键补偿路径。周期不是越短越好,而是要在 XCP 响应延迟、DAQ 节拍和 ECU 任务负载之间选择;明确给出的参考值是常规场景 5/10 ms,关闭 CanIf transmit queue 时 1 ms。
4. PDU Mode 与组件启停不是同一个开关
CanXcp 提供两组容易混淆的运行期控制:CanXcp_SetPduMode() 只控制 XCP 帧能否发送;XCP_ACTIVATE() 和 XCP_DEACTIVATE() 控制 XCP Protocol Layer 与 Transport Layer 的整体功能。
| 控制接口 | 控制范围 | 被阻止的数据如何处理 | 典型用途 |
|---|---|---|---|
CanXcp_SetPduMode(..., CANXCP_SET_OFFLINE) |
总线发送能力 | 帧保存在 Send Queue,直到在线或 overrun | CAN 总线尚未可通信、网络切换 |
CanXcp_SetPduMode(..., CANXCP_SET_ONLINE) |
总线发送能力 | 允许排队帧继续发送 | CanSM 通知总线上线 |
XCP_DEACTIVATE() |
协议层和传输层全部功能 | 组件功能被锁定 | 量产 ECU 默认关闭 XCP,由诊断条件解锁 |
XCP_ACTIVATE() |
协议层和传输层全部功能 | 恢复组件功能 | 满足授权或诊断条件后启用 XCP |
XCP 默认处于组件已激活状态,但 PDU 模式在初始化后是离线状态。也就是说,“组件可执行”与“允许向 CAN 发送”是两个正交状态。量产项目若同时使用诊断激活和网络状态管理,必须分别维护这两个状态,不能用一个接口代替另一个。

4.1 Resume Mode 下为什么应使用 PDU Mode
Resume Mode 启用后,如果 CAN 总线尚未就绪,Resume Mode 触发的帧可能无法发送;随后 XCP 再次初始化,初始化过程又包含 Resume Mode 初始化,从而存在递归风险。对应的规避方法是使用 PDU Mode:总线未就绪时先保持 CANXCP_SET_OFFLINE,让帧进入队列;总线可通信后再切换为 CANXCP_SET_ONLINE。
这一路径的关键不是“延后初始化”,而是把协议/会话初始化与总线可发送状态解耦。PDU Mode 能在总线未准备好时有效阻止发送,同时在队列未 overrun 的前提下保留帧。
5. MaxCto、MaxDto 配置与混合 CAN/CAN FD 通道
CanXcp 使用两个全局上限参数:
/MICROSAR/Xcp/XcpConfig/XcpTransportLayer/XcpOnCan/XcpOnCanMaxCto
/MICROSAR/Xcp/XcpConfig/XcpTransportLayer/XcpOnCan/XcpOnCanMaxDto
它们选择系统允许的最大 CTO/DTO 长度,但每个通道的实际值仍受该通道 PDU 大小限制。混合多通道系统可能同时存在 8 Byte Classical CAN PDU 和 64 Byte CAN FD PDU,此时全局 MaxCto/MaxDto 可以配置为 64,经典 CAN 通道会自动限制为 8。
| 全局配置 | 通道 PDU 大小 | 通道实际最大值 |
|---|---|---|
MaxCto = 64、MaxDto = 64 |
Classical CAN:8 Byte | 自动限制为 8 Byte |
MaxCto = 64、MaxDto = 64 |
CAN FD Mode 2:64 Byte | 最大可使用 64 Byte |
因此,全局参数描述的是“最大可能值”,不是所有通道都强制使用同一帧长。协议层生成 Packet 时仍必须遵守当前逻辑通道对应的有效上限。
6. API、回调与协议层服务
6.1 协议层调用的 CanXcp 服务
| API | 参数 | 功能 | 返回值 | 上下文与可重入性 |
|---|---|---|---|---|
CanXcp_Send |
Xcp_Channel:逻辑通道;len:Packet 长度;msg:数据指针 |
请求发送 DTO 或 CTO | 无 | 不可重入;可从 XcpEvent、XcpBackground 和 CAN Interface 上下文调用 |
CanXcp_MainFunction |
无 | 处理传输层周期任务、队列重试和 DAQ 发送触发 | 无 | 不可重入;由 SchM 或应用周期调用 |
CanXcp_Send() 的 Xcp_Channel 取值受 Multi Client 配置影响:Multi Client 关闭时始终为 0;开启时反映协议层逻辑 XCP 通道。len 在经典 CAN 上为 1..8,在 CAN FD 上为 1..64。
6.2 CanIf 调用的传输层回调
| API | 参数 | 功能 | 返回值 | 上下文与可重入性 |
|---|---|---|---|---|
Xcp_CanIfRxIndication |
CanCanXcpRxPduId:接收目标 PDU ID;PduInfoPtr:数据指针和长度 |
通知收到 CTO/DTO Packet,并交给协议层 | 无 | CAN Interface 上下文;不可重入 |
Xcp_CanIfTxConfirmation |
CanTxPduId:成功发送的 PDU ID |
确认 CTO/DTO 已成功发送,并通知协议层 | 无 | CAN Interface 上下文;不可重入 |
这两个函数需要在生成工具中配置为 CanIf 的接收通知和发送确认回调。漏配 TxConfirmation 会让 CanXcp 无法把发送完成事件传递给协议层;Rx PDU ID 映射错误则会让接收到的 XCP L-SDU 无法进入正确逻辑通道。
6.3 其他层调用的服务与宏
| API/宏 | 参数 | 功能 | Service ID | 约束 |
|---|---|---|---|---|
CanXcp_Init |
ConfigPtr:Post-Build 配置指针 |
初始化 XCP on CAN Transport Layer | 未给出 | 系统初始化期间调用;不可重入;仅 Post-Build 配置评估该指针 |
CanXcp_GetVersionInfo |
Versioninfo:版本信息输出地址 |
返回组件版本、Vendor ID 和 AUTOSAR Module ID,版本采用 BCD 编码 | 未给出 | 未列出额外限制 |
CanXcp_SetPduMode |
XcpNwH:通常为 0;PduMode:ONLINE/OFFLINE |
控制 XCP 帧发送,离线时使用 Send Queue | 7 |
Task level;同步;不可重入 |
XCP_ACTIVATE() |
无 | 启用 XCP Protocol Layer 与 Transport Layer 全部功能 | 无 | Task level;同步;不可重入 |
XCP_DEACTIVATE() |
无 | 锁定 XCP Protocol Layer 与 Transport Layer 全部功能 | 无 | Task level;同步;不可重入 |
CanXcp_InitMemory |
无 | 启动代码未初始化 RAM 时完成显式内存初始化 | 未给出 | AUTOSAR 外扩展;应在依赖已初始化内存的调用之前执行 |
6.4 CanXcp 调用的协议层接口
| 协议层服务 | CanXcp 使用目的 |
|---|---|
Xcp_Command(uint8 Xcp_Channel, const uint32* pCommand) |
把接收命令交给协议层处理 |
Xcp_SendCallBack(uint8 Xcp_Channel) |
把发送确认传回协议层 |
Xcp_SetActiveTl(uint8 Xcp_Channel, uint8 Tl) |
设置当前活动 Transport Layer |
Xcp_GetActiveTl(uint8 Xcp_Channel) |
读取当前活动 Transport Layer |
Xcp_GetSessionStatus(uint8 Xcp_Channel) |
读取 XCP 会话状态 |
Xcp_TlMainFunction(uint8 ActiveTl) |
转发 Transport Layer 周期处理 |
这些接口属于 XCP Protocol Layer,CanXcp 只负责在 CAN 传输事件发生时调用它们,不定义其内部协议行为。
7. 并发保护与独占区
CanXcp 使用 CANXCP_EXCLUSIVE_AREA_0 保证关键区的原子性。该独占区必须映射到允许嵌套调用的 interrupt lock/unlock 函数,因为 Xcp_Event、Xcp_SendCallBack、Xcp_MainFunction 和 Xcp_Command 可能相互打断。

CanXcp_Send、两个 CanIf 回调和主函数都不可重入,独占区配置不能只保护某一个入口。调度分析应同时纳入 CAN ISR、DAQ 事件上下文、后台处理和周期 Task,确认嵌套锁不会在回调链中被错误释放。
8. 交付文件、版本与 A2L 集成
8.1 实现和生成文件
| 文件 | 作用 | 集成约束 |
|---|---|---|
CanXcp.c |
XCP on CAN Transport Layer 实现 | 用户不得修改 |
CanXcp.h |
传输层 API | 应先于 XcpProf.h 包含;应用不得修改 |
CanXcp_Types.h |
传输层类型定义 | 内部使用,不应单独包含或修改 |
CanXcp_Cfg.h |
XCP on CAN 配置外部声明 | 由 GENy 生成 |
CanXcp_Lcfg.c |
Link-Time 参数定义 | 由 GENy 生成 |
CanXcp_PBcfg.c |
Post-Build 参数定义 | 由 GENy 生成 |
未使用 GENy 配置 AUTOSAR CAN Interface 时,与项目适配直接相关的是 CanXcp_Cfg.h、CanXcp_Lcfg.c 和 CanXcp_PBcfg.c 这些生成文件;其余实现文件仍不应手工改写。
8.2 BCD 版本常量
CanXcp 提供三个可由应用随时读取的外部常量,用 BCD 形式表达主版本、次版本和发布版本:
kXcpOnCanAsrMainVersion = (CP_XCPONCANASR_VERSION >> 8);
kXcpOnCanAsrSubVersion = (CP_XCPONCANASR_VERSION & 0x00ff);
kXcpOnCanAsrReleaseVersion = CP_XCPONCANASR_RELEASE_VERSION;
例如版本 1.00.00 对应主版本 0x01、次版本 0x00、发布版本 0x00。CanXcp_GetVersionInfo() 返回的版本信息同样采用 BCD 编码,并同时提供 Vendor ID 和 AUTOSAR Module ID。
8.3 A2L 文件
GENy 会导出 CanXCPAsr.a2l,其中包含 XCP_ON_CAN IF_DATA 段,可供 CANape 等 XCP Master 的主 A2L 模板引用:
/begin IF_DATA XCP
/include "CanXCPAsr.a2l"
/end IF_DATA
这个文件解决的是 Master 侧 XCP on CAN 传输参数接入,不替代 ECU 内 CanIf PDU、CanXcp 通道和 PDU Mode 的配置。
9. 限制与明确不支持的能力
CanXcp 对 CAN Identifier、Slave 发现和 Multiple Identity 有三项明确限制。
| 限制 | 具体边界 | 对标定系统的影响 |
|---|---|---|
| DAQ List 分配 CAN Identifier | 仅有一个 CAN Identifier 用于 CMD/STIM,另一个用于 RES/EV/DAQ;不支持 GET_DAQ_ID、SET_DAQ_ID |
不能为各 DAQ List 动态分配独立 CAN ID |
| 网络内 Slave 自动发现 | 不支持 GET_SLAVE_ID |
Master 不能依靠该命令枚举网络中的全部 XCP Slave |
| Multiple Identity | 只在单 CAN 通道配置下可用 | 多通道项目不能同时使用该 Multiple Identity 能力 |
这些限制应在 CANape 工程、A2L 组织和 ECU 通道规划阶段处理。特别是多通道方案,不能在集成后期再假设 Multiple Identity 或按 DAQ List 分配 CAN ID 可以由配置开关补上。
10. 集成与调试落点
| 观察点 | 正常条件 | 异常表现 |
|---|---|---|
| 初始化后的 PDU Mode | CanSM 或应用切换到 CANXCP_SET_ONLINE |
能接收命令但没有任何 XCP 帧发出 |
| Rx 回调 | CanIf 调用正确的 Xcp_CanIfRxIndication,PDU ID 与逻辑通道匹配 |
协议层看不到 CMD/STIM |
| Tx 回调 | 发送完成后调用 Xcp_CanIfTxConfirmation |
协议层收不到 Xcp_SendCallBack,发送流程不能正确确认 |
| Send Queue | 主函数周期处理,队列不 overrun | 总线忙或离线期间帧积压、后续新增帧无法保存 |
| 主函数周期 | 常规 5/10 ms;CanIf 无发送队列时推荐 1 ms | XCP Slave 重新参与仲裁过慢,响应和 DAQ 延迟增大 |
| 报文长度 | 全局 MaxCto/Dto 与通道 PDU 上限一致 | 64 Byte 全局配置被经典 CAN 通道限制为 8 Byte |
| 组件状态 | XCP_ACTIVATE/DEACTIVATE 与 PDU ONLINE/OFFLINE 分别维护 |
误把总线离线当成组件停用,或量产 ECU 意外保留 XCP 功能 |
| 并发 | CANXCP_EXCLUSIVE_AREA_0 使用可嵌套中断锁 |
事件、命令、发送确认和主函数之间出现共享状态竞争 |
排查“XCP 无响应”时,可以按固定方向缩小范围:先确认组件未被 XCP_DEACTIVATE(),再确认 PDU 已在线,然后检查 Rx PDU 是否进入 Xcp_CanIfRxIndication、协议层是否调用 CanXcp_Send、帧是否进入 Send Queue、主函数是否周期执行,最后确认 Xcp_CanIfTxConfirmation 是否返回。这个顺序与实际数据路径一致,能区分协议问题、网络状态问题和调度问题。
11. 小结
CanXcp 的关键不在 XCP 命令解析,而在传输状态的闭环:CanIf 接收回调把 Packet 送到协议层,CanXcp_Send 把 CTO/DTO 送回 CAN,发送确认再通过 Xcp_SendCallBack 返回协议层。PDU 离线或 CanIf 暂时无法发送时,Send Queue 与 CanXcp_MainFunction 共同维持重试路径。
集成风险主要集中在三处:初始化后没有切换到 CANXCP_SET_ONLINE,把组件启停与 PDU Mode 混用,以及主函数周期和独占区没有覆盖实际并发场景。再加上 MaxCto/Dto 的通道限制和三项协议能力边界,CanXcp 的运行行为就能在 CAN、CAN FD、单通道和多通道项目中被明确约束。
作者:不脱发的程序猿。本文同步自作者 CSDN 博客。
原文:https://handsome-man.blog.csdn.net/article/details/162874569




