Xcp DAQ-STIM在线标定与多传输层协同机制

2026-09-213

把测量变量加入 A2L 并不意味着工具端一定看到正确的值。一次 Xcp_Event() 发生在控制算法数据刚刚稳定的时刻,数据可能先进入核内发送队列,过若干个传输层主函数周期才真正上总线;多个 ODT 可能来自同一次 DAQ 触发,却未必具有整个 DAQ List 级别的原子一致性;STIM 数据已经到达 ECU,也要等关联 Event Channel 到来才写入目标地址。只看总线报文,很容易把“采样时刻、入队时刻、发送时刻、接收时刻”混成一个时间点。

XCP 的另一条边界更敏感:它不仅读取测量量,还能修改标定页、写普通内存、擦写 Flash、下载 RAM Flash Kernel。协议层可以解析命令,却无法替应用判断某个地址能否访问、某个标定页如何切换、擦除期间是否还能维持正常运行。这些决定落在 XcpAppl 回调、Seed & Key、内存保护和运行时使能机制上。

因此,集成 XCP 不能只验证 CONNECT 和 UPLOAD。真正需要闭环的是三条链:CTO 命令在协议层、传输层和应用回调之间如何流动;DTO 数据在 Event、ODT、队列和总线之间如何保持时间含义;标定与编程操作怎样受到状态、资源保护和硬件能力约束。

协议层、传输层与应用回调的职责边界

该实现支持 ASAM XCP 1.1,并位于 AUTOSAR Classic 基础软件通信路径之上。XCP Protocol Layer 处理连接状态、命令语义、DAQ/STIM 表、分页和编程流程;XcpOnCanXcpOnFrXcpOnTcpIp 分别把协议映射到 CAN、FlexRay 和 Ethernet,向下连接 CanIfFrIfSoAd。硬件相关和项目相关动作不硬编码在协议层,而由应用提供 XcpAppl 回调。开发期非法调用则经 Det_ReportError() 报告。

图 1:XCP Protocol Layer、传输层与应用回调的相邻接口

命令传输对象(Command Transfer Object,CTO)承载 CONNECT、UPLOAD、DOWNLOAD、页面切换和编程等命令及响应;数据传输对象(Data Transfer Object,DTO)承载 DAQ、STIM、EV 和 SERV。协议层知道 CTO/DTO 的逻辑格式,传输层决定可用的 MAX_CTOMAX_DTO 和实际发送通道。Xcp_SetActiveTl() 由传输层写入通道使用的最大 CTO、最大 DTO 与 XCP_TRANSPORT_LAYER_CANXCP_TRANSPORT_LAYER_FRXCP_TRANSPORT_LAYER_ETHXcp_GetActiveTl() 让传输层查询当前选择。

这种拆分有一个直接后果:传输层若运行在另一个核,其触发的应用回调也在那个上下文执行。XcpAppl_GetPointer()、内存访问回调或命令扩展不能默认自己永远位于某个应用任务。配置支持 VARIANT-PRE-COMPILE,协议行为、缓冲区和接口开关在生成阶段固定;运行时变化只发生在协议明确提供的状态和 API 范围内。

初始化、连接状态与周期处理

Xcp_InitMemory() 只在启动代码没有把相关内存清零时需要调用,并且必须先于其他 XCP 服务。它同步、可重入,可在任务或中断级调用,但执行期间必须关闭全局中断,通常应放在 ECU 初始化早期。随后任务级、同步、不可重入的 Xcp_Init() 初始化协议层和内部变量。使用 EcuM 时由 EcuM 组织顺序;没有 EcuM 时,应用必须保证先 Xcp_InitMemory()、后 Xcp_Init()

连接状态不只是“连上/断开”两态:

状态 CTO 行为 DTO 行为 典型入口
XCP_CON_STATE_DISCONNECTED 只接受 CONNECT CTO 不接收、不发送 Xcp_Init()、Disconnect CTO、Xcp_Disconnect()
XCP_CON_STATE_CONNECTED 完整支持命令与响应 可接收、可发送 Master 发送 CONNECT
XCP_CON_STATE_RESUME 除 CONNECT 外的 CTO 被拒绝 可接收、可发送 已持久化 DAQ 的上电自动测量

图 2:DISCONNECTED、CONNECTED 与 RESUME 连接状态机

Xcp_Disconnect(XcpChannel) 在已连接时主动转入 DISCONNECTED,未连接时不动作。XcpAppl_ConStateNotification() 会把 XCP_CON_STATE_CONNECTEDXCP_CON_STATE_DISCONNECTEDXCP_CON_STATE_RESUME 通知应用。应用若要在运行期关闭 XCP,先断开连接再执行 XCP_DEACTIVATE,可以避免在仍有会话和待发数据时突然撤掉协议层与传输层;XCP_ACTIVATE/XCP_DEACTIVATE/MICROSAR/Xcp/XcpGeneral/XcpControl 使能,并同时控制两层。

两个周期函数承担不同工作。Xcp_MainFunction() 分块执行大内存区的异步校验和计算,并处理 Resume Mode 的持久化请求;Xcp_TlMainFunction(ActiveTl) 按通道触发 DAQ、EV 和 SERV 的发送。前者通常由 SchM 周期调用,后者通常由相应传输层主函数调用,其发送粒度也就受传输层周期限制。只配置 DAQ 而漏调 Xcp_TlMainFunction(),采样值可以进入队列却不会按预期上总线;启用 Resume Mode 而漏调 Xcp_MainFunction(),DAQ 配置保存和上电恢复链也不完整。

CTO 事务与 Block Transfer 的吞吐边界

标准通信模型是一条请求对应一条响应或错误报文,不允许 Master 在收到上一条响应前继续发送多条请求,因此不支持 Interleaved Communication。UPLOAD、DOWNLOAD 和 PROGRAM 可改用 Block Transfer:Master Block 模式允许 Master 在一个块内连续发出最多 MAX_BS 个请求,Slave 在最后一个请求后响应;Slave Block 模式允许 Slave 对一次请求连续返回多个响应。

Block Transfer 的总块大小上限为 255 Byte。它不是在任何总线上都更快:当 MAX_CTO 已接近 254 Byte 时,一个标准请求本身就能承载很大的有效载荷,额外启用块模型可能得不到收益,甚至增加状态和调度开销。通信尺寸必须与传输层的 MAX_CTOMAX_DTO 一起评估,不能只按“启用块传输”判断吞吐。

协议响应还存在异步完成语义。应用回调返回 XCP_CMD_PENDING 后,协议层不能立即结束 CTO 事务;应用完成动作并准备好 CRM 后调用 Xcp_SendCrm()。该服务只在没有其他报文待发时发送 RES 或 ERR。XCP_CMD_OK 表示已完成,XCP_CMD_DENIED 表示访问被拒绝,XCP_CMD_BUSY 表示当前未执行;这些返回值描述的是命令处理状态,不等价于底层 Flash 或 NVM 永久写入已经得到独立验证。

DAQ 从 Event Channel 到 ODT 发送队列

DAQ 使用动态配置:MCS 把测量对象地址组织为 DAQ List,DAQ List 再由多个 ODT 组成,并关联到 Event Channel。静态 DAQ List 不受支持,DAQ List 总数限制为 0xFF,也不支持 DAQ List/Event Channel 优先级、DAQ bit offset 和 ODT Optimization。配置工具为 /MICROSAR/Xcp/XcpConfig/XcpEventChannel 生成从 0 开始且连续的符号编号,应用必须在数据有效的真实执行点调用 Xcp_Event(EventChannel),而不是随便放进一个近似周期任务。

原文图示 1

Xcp_Event() 同步执行,可在任务或中断级调用,对不同 Event Channel 可重入;前提是协议层已初始化、处于 CONNECTED 且 /MICROSAR/Xcp/XcpCmdConfig/XcpDaqAndStim 已启用。返回值让调用点立即知道本次 Event 的实际结果:

返回值 含义 集成判断
XCP_EVENT_NOP DAQ 未运行或 Event 未配置 不是队列错误,先检查会话与 DAQ 绑定
XCP_EVENT_DAQ 本次执行了 DAQ 采样 数据已采集,不代表已经上总线
XCP_EVENT_DAQ_OVERRUN DAQ 队列溢出并丢数 增大队列或缩短发送延迟,不能靠工具端补回
XCP_EVENT_STIM 本次执行了 STIM 写入 缓冲中的激励值已参与事件处理
XCP_EVENT_STIM_OVERRUN 没有可用 STIM 数据 需要结合单次/连续模式判断是否符合预期

Xcp_EventEx() 在相同机制上增加 Xcp_DaqConfigType。先用 Xcp_DaqConfigInit() 写入安全默认值,再按需设置 AddressOffsetApplTimestampUseApplTimestamp。地址偏移适合动态分配对象或同构实例;应用时间戳则把采样时间绑定到调用点。它与 Xcp_Event() 具有相同的一致性和重入限制,不是一条独立的高原子性路径。

Send Queue 存放完整 ODT,大小由每个 /MICROSAR/Xcp/XcpConfig/XcpCoreDefinition/XcpSendQueueSize 决定。容量至少要覆盖一次 Xcp_Event() 采集的全部 ODT,以及这些 ODT 等待总线发送期间继续产生的数据。开启 /MICROSAR/Xcp/XcpCmdConfig/XcpDaqAndStim/XcpOverrunIndication 后,溢出可在 PID 的最高位标识,但标识只能暴露丢数,不能恢复数据。

STIM 与旁路控制的事件语义

STIM 是 DAQ 的反向链路。传输层先接收 DTO,STIM Processor 把数据放入缓冲;只有关联 Event Channel 到来并执行 Xcp_Event() 时,缓冲值才写入 ECU 地址。由此,报文到达时刻与控制变量生效时刻天然分离。/MICROSAR/Xcp/XcpCmdConfig/XcpDaqAndStim/XcpSynchronousDataStimulation 控制功能是否生成,且该实现只支持一个 STIM DAQ List,不支持 bitwise stimulation。

Xcp_SetStimMode() 选择写入策略:XCP_STIM_SINGLE_SHOT_MODE 只把有效数据写一次,也是默认模式;XCP_STIM_CONTINUOUS_MODE 在后续 Event 中持续写入最后一份有效数据。Xcp_StimEventStatus(EventChannel, Action) 可用 STIM_CHECK_ODT_BUFFER 查询缓冲,得到 XCP_NO_STIM_DATA_AVAILABLEXCP_STIM_DATA_AVAILABLE,也可用 STIM_RESET_ODT_BUFFER 清理缓冲。这个 API 同步、可重入,可在任务或中断级使用,但要求 XCP 已连接且 STIM 已配置。

DAQ 与 STIM 同时使用可以构成旁路(Bypassing):ECU 把中间量经 DAQ 送给 MCS,MCS 算出替代值后再经 STIM 注入。XCP 只提供数据通路,不负责管理被旁路函数,也不替应用做激励值合理性检查。边界、超限动作和函数切换必须由标定方法与应用逻辑承担;不能因为 STIM 报文通过协议校验,就把它视为业务上安全的控制量。

ODT 一致性、临界区与 Type Safe Copy

默认保证只到 ODT 级:一个 ODT 的组装受保护,但一条 DAQ List 包含多个 ODT 时,另一个 Event 可以插入两个 ODT 之间。图中的 Event x 被 Event y 打断仍是允许的,因为每个 ODT 自身完整;同一 Event x 重入自身会把一个 DAQ List 拆成交错序列,这不允许,调用方必须在 DAQ 级增加中断锁。

图 3:多 ODT 在单次 Xcp_Event 调用内的数据一致性边界

三个临界区承担不同保护:

临界区 保护对象 持续时间与风险
XCP_EXCLUSIVE_AREA_0 多个不可重入的长路径 包含若干子函数,可能持续较长时间
XCP_EXCLUSIVE_AREA_1 Xcp_Event() 的 DAQ ODT 组装 MAX_DTO 影响,CAN 较短、Ethernet 可能很长
XCP_EXCLUSIVE_AREA_2 Xcp_Event() 的 STIM ODT 处理 同样随 MAX_DTO 增长

三个 Exclusive Area 不得互相抢占。增大 MAX_DTO 虽能提高单帧载荷,却也会延长 AREA 1/2 中一次 ODT 拷贝的锁定时间;这是一项吞吐与中断延迟之间的直接交换,而不只是通信参数变化。

Type Safe Copy 解决的是另一类问题。启用后,模板化的 XcpAppl_CalibrationWrite()XcpAppl_MeasurementRead() 对齐访问 uint16uint32 等基本类型时采用原子读写,避免跨核访问过程中读到撕裂值;关闭时按字节复制,不具备这种原子性。它不能提供 ODT 级或 DAQ 级一致性,而且会增加运行时间。MCS 若把多个变量合并成一次优化访问,也会破坏“一个基本类型对应一次原子访问”的前提,因此该优化必须关闭。

默认只有 polling 访问经过上述两个应用回调,DAQ/STIM 为性能直接访问内存。/MICROSAR/Xcp/XcpGeneral/XcpDAQMemAccessByApplication 可以让 DAQ/STIM 也走应用回调;这既是访问控制点,也会改变采样路径的执行时间,必须与 Event 的中断预算一起验证。

多核队列与时间戳重建真实时间线

每个调用 Xcp_Event()/Xcp_EventEx() 的核都需要一个 /MICROSAR/Xcp/XcpConfig/XcpCoreDefinition 和自己的无锁 Send Queue。每个 Event Channel 通过 XcpEventChannelCoreRef 绑定到唯一 Core Definition;从错误核调用会触发 DET。应用还必须只把该核可安全访问的测量对象放进相应 Event Channel,生成工具无法替运行时数据归属兜底。

图 4:多核系统中 Xcp_Event 与核内无锁队列

各核并行采样,BSW 核上的传输层主函数却顺序发送。如果主函数周期偏长,多个核的 ODT 会积压后成批发送。工具端若只使用报文到达时间,就可能把先后发生的采样显示为几乎同时发生,甚至得到错误的跨核排序。FlexRay 或 Ethernet 把多个 XCP Frame 拼接到一个总线帧时也有同样问题。

Slave Timestamp 用采样端时间修正这条链。默认由 XcpAppl_GetTimestamp() 返回配置为 uint8uint16uint32 的时间值,该回调在活动的中断锁内执行,必须足够短;也可由 Xcp_EventEx()ApplTimestampUseApplTimestamp=TRUE 直接提供。计数器分辨率与增量要和 A2L/配置一致,否则时间值本身单调也会被 MCS 按错误单位解释。

Resume Mode 让冷启动期间也能自动 DAQ。XcpAppl_DaqResumeStore() 把完整动态 Xcp_ChannelStruct 和是否启动测量的标志写入非易失存储,旧配置必须随新存储失效;XcpAppl_DaqResume() 在启动时恢复到 RAM,XcpAppl_DaqResumeClear() 清除保存项。恢复成功后进入 RESUME,DTO 可以流动而普通 CTO 仍被拒绝。Xcp_MainFunction() 必须持续运行,才能处理这些持久化请求。

互补通道绕开常规总线瓶颈

互补通道面向 PCIe 等高带宽内存映射链路,DAQ 采样机制不变,只把完整 ODT 的缓冲管理和发送交给应用。Xcp_DaqSetComplementaryChannel(XcpChannel, TRUE) 切换到互补模式,FALSE 回到常规传输;开关由 /MICROSAR/Xcp/XcpCmdConfig/XcpDaqAndStim/XcpComplementaryDAQ 生成。

图 5:互补通道的缓冲区获取、提交与发送序列

建立 DAQ 配置时,XcpAppl_DaqPrepare() 申请或准备外部接口资源,返回 XCP_CMD_OK 才能继续,接口不可用时返回 XCP_CMD_ERRORXcp_EventGetDaqSize()XcpChannelEventChannel 计算关联 DAQ List 的总大小与优先级,E_OK 表示可据此分配缓冲,E_NOT_OK 表示 DAQ List 无效。采样时 XcpAppl_GetSendQueueRef() 按通道和 Event 索引取得缓冲,无法提供空间时返回 NULL_PTR;填充完成后由 XcpAppl_TransmitSendQueueElement() 触发发送。动态 DAQ List 释放时,XcpAppl_DaqCleanup() 归还资源。

这套接口把缓冲所有权交接说得很清楚:Prepare 管生命周期,GetSendQueueRef 移交可写缓冲,Transmit 提交已填内容,Cleanup 收尾。实现不能让同一块缓冲在提交后仍被采样路径改写,也不能在 Cleanup 后继续由外部接口持有。XcpAppl_MemCpy() 可作为任务或中断上下文中的统一复制入口,但它同步且不可重入,不能暗中引入会阻塞 Event 的异步传输。

在线标定的页切换、复制与 Freeze

在线标定把参考数据放在 Flash Page,把可修改工作数据放在 RAM Page。SET_CAL_PAGE 通过 XcpAppl_SetCalPage(Segment, Page, Mode) 选择页面;Mode 的 Bit 1 表示 ECU access,Bit 2 表示 XCP access,两者可以同时或分别设置。XcpAppl_GetCalPage(Segment, Mode) 返回对应访问方当前使用的逻辑页。该实现只支持一个 Segment、两个 Page,不支持 GET_PAGE_INFOGET_SEGMENT_INFO

XcpAppl_SetCalPage() 可同步返回 XCP_CMD_OK,也可返回 XCP_CMD_PENDING 后在完成时调用 Xcp_SendCrm();越界 Segment、无效 Page 和无效 Page Mode 分别用 XCP_CRC_OUT_OF_RANGEXCP_CRC_PAGE_NOT_VALIDXCP_CRC_PAGE_MODE_NOT_VALID 拒绝。页面切换由 /MICROSAR/Xcp/XcpCmdConfig/XcpPageSwitching 控制,XcpGeneralPagingInfo 决定 MCS 能否自动读取分页处理器概况。

COPY_CAL_PAGEXcpCopyPage 使能,并调用 XcpAppl_CopyCalPage(SrcSeg, SrcPage, DestSeg, DestPage)。目标页写保护时返回 XCP_CRC_WRITE_PROTECTED,Segment 或 Page 无效时分别返回相应错误,也允许 XCP_CMD_PENDING 异步完成。复制不是页面切换:前者改变目标页内容,后者改变 ECU 或 XCP 当前解析地址时选择的页,两者必须分别验证。

Freeze Mode 由 SET_SEGMENT_MODEGET_SEGMENT_MODEXcpFreezeMode 组合。XcpAppl_SetFreezeMode() 让应用保存某 Segment 的 Freeze 状态,XcpAppl_GetFreezeMode() 返回它;XcpAppl_CalResumeStore() 把当前标定数据保存到非易失存储,冷启动或复位后的恢复由应用负责。Freeze 的价值是让在线改动获得明确的跨电源周期策略,不意味着协议层自动决定保存介质、掉电原子性或恢复失败动作。

Flash 编程的两条实现路径

若 MCU 允许一边从 Flash 执行、一边擦写另一个内部扇区,或者目标是外部 Flash,可以由 ECU 应用直接完成编程。/MICROSAR/Xcp/XcpCmdConfig/XcpProgramming 使能 PROGRAM_STARTPROGRAM_CLEARPROGRAMPROGRAM_NEXTPROGRAM_MAX 和复位流程;XcpAppl_ProgramStart() 先准备 ECU,XcpAppl_FlashClear(Address, Size) 擦除,XcpAppl_FlashProgram(Data, Address, Size) 写入,最后 XcpAppl_Reset() 复位。Prepare/Erase/Program 可以用 XCP_CMD_PENDING 延后 CRM,错误用 XCP_CMD_ERROR

XcpSector 可向 MCS 提供扇区数、起始地址、长度、清除/编程序号以及压缩、加密能力。不过标准 PROGRAM_FORMATPROGRAM_VERIFY 不受支持;实现列出的 PROGRAM_FORMAT1PROGRAM_VERIFY1 属于扩展命令,不能把它们等同于完整 ASAM 标准能力。GET_SECTOR_INFO 也不返回 Program Sequence Number。

若硬件不允许执行代码期间重编内部 Flash,则必须使用 RAM Flash Kernel。/MICROSAR/Xcp/XcpGeneral/XcpBootloaderDownload 开启这条路径。协议层先调用 XcpAppl_DisableNormalOperation(Address, Size),应用停止正常 ECU 功能并确认 Kernel 下载区;随后下载 Kernel 到 RAM,XcpAppl_StartBootLoader() 转交控制,正常情况下该函数不应返回。Xcp_GetXcpDataPointer() 可以取得完整通道数据供 Kernel 继承会话。由于 RAM 内容和正常运行上下文可能被破坏,这不是普通异步作业,而是系统运行模式的切换点。

Seed & Key、地址转换与访问保护

Seed & Key 可分别保护 XCP_RM_CAL_PAGXCP_RM_DAQXCP_RM_STIMXCP_RM_PGMXcpAppl_GetSeedLength(Resource) 给出当前资源的 Seed 长度,XcpAppl_GetSeed(Resource, Position, Length, Seed) 分片生成 Seed;Master 回传的 Key 先由 XcpAppl_UnlockWriteKey() 按 Position 拼装,再由 XcpAppl_Unlock() 校验并返回允许解锁的资源位。Seed 和 Key 长度都不能超过 MAX_CTO-2

Xcp_ModifyProtectionStatus(XcpChannel, AndState, OrState) 允许应用在运行期逐资源清除或设置保护位,状态保持到下一次 Xcp_Init(),届时所有保护重新建立。该 API 不是绕开鉴权的通用后门;它应由已经具备授权依据的应用状态驱动,并且只有启用 /MICROSAR/Xcp/XcpCmdConfig/XcpStandard/XcpSeedKey 才存在。

MCS 传来的地址由 32 位地址加 8 位扩展组成,XcpAppl_GetPointer() 把它转换成 Xcp_AddressPtrType。地址扩展可以区分地址空间或存储类型,项目没有这种需求时才可忽略。XcpAppl_CalibrationWrite()XcpAppl_MeasurementRead() 既执行复制,也能按地址拒绝为 XCP_CMD_DENIED,或对 EEPROM 等介质返回 XCP_CMD_PENDING

范围型操作还需要前置检查。XcpMemoryReadProtection 使能 XcpAppl_CheckReadAccess(XcpChannel, Address, Size),在 CRC 或 WRITE_DAQ 等命令接受前检查整个区间;XcpProgrammingWriteProtection 使能 XcpAppl_CheckProgramAccess(Address, Size),用于 Flash 擦除和编程区间。只在单次 copy 回调里检查起始地址而忽略 Size,会让跨边界请求穿过保护区。

内建校验和支持 XCP ADD 算法和 CRC16CCITT;使用 AUTOSAR CRC 模块时还可支持 CRC32,最大校验区为 0xFFFFXcpCustomCRC 改用 XcpAppl_CalculateChecksum(MemArea, Result, Length),同步完成返回 XCP_CMD_OK,后台计算返回 XCP_CMD_PENDING,无法处理返回 XCP_CMD_DENIED。大区间的内建计算由 Xcp_MainFunction() 分块,避免一次 CTO 长时间占用 CPU。

公共 API 与传输层握手

公共服务可以按“生命周期、事件触发、异步消息、运行控制”理解,而不是按编号逐个调用:

接口组 API 关键语义
初始化与版本 Xcp_InitMemory()Xcp_Init()Xcp_GetVersionInfo() 前两者建立内存与协议状态;版本接口返回 BCD 版本、Vendor ID 和 Module ID,受 XcpVersionInfoApi 控制
DAQ/STIM Xcp_Event()Xcp_DaqConfigInit()Xcp_EventEx()Xcp_StimEventStatus() Event 在任务/中断级采样或写 STIM;扩展结构必须先初始化
异步消息 Xcp_SendEvent()Xcp_PutChar()Xcp_Print() EV 返回 E_OK/E_NOT_OK;SERV 在报文满或遇到 0x00 时发送,均不保证被 Master 确认
会话与资源 Xcp_Disconnect()Xcp_ModifyProtectionStatus()Xcp_GetSessionStatus() 主动断开、逐资源保护、读取 XCP_SESSION_CONNECTED/POLLING/DAQ 位图
延后响应 Xcp_SendCrm() 应用异步动作完成后发送 RES/ERR,要求已收到 CMD 且无其他报文待发
专用链路 Xcp_GetXcpDataPointer()Xcp_DaqSetComplementaryChannel()Xcp_EventGetDaqSize() 分别服务 Flash Kernel、互补通道切换与缓冲容量计算

Xcp_GetSessionStatus()/MICROSAR/Xcp/XcpGeneral/XcpSessionStatusAPI 控制。XCP_SESSION_CONNECTED 只表示会话连接,XCP_SESSION_DAQ 表示 DAQ 活跃,XCP_SESSION_POLLING 表示正在进行 polling 测量。ECU 的 post-event 保持计时应只在连接且 DAQ 或 polling 活跃时重载;polling 请求可能很稀疏,XCP_TIMEOUT_TIMER_RELOAD 设得过短会让 ECU 在合法测量间隙提前释放通信请求。

传输层与协议层之间是严格的单报文确认握手:

方向 API 协议约束
Transport -> Protocol Xcp_TlRxIndication(XcpChannel, CmdPtr) 每收到一个 CTO,传入已经从传输层封装中取出的协议消息
Protocol -> Transport <Bus>Xcp_Send(XcpChannel, len, msg) 请求发送 CTO 或 DTO;成功后必须回调确认
Transport -> Protocol Xcp_TlTxConfirmation(XcpChannel) 未确认前协议层不会再次调用 Send;确认会继续推动待发 DAQ
Protocol -> Transport <Bus>Xcp_SendFlush(XcpChannel, FlushType) XCP_FLUSH_CTOXCP_FLUSH_DTOXCP_FLUSH_ALL 清缓冲
双向状态 Xcp_TlQueryMessagePending()Xcp_SetActiveTl()Xcp_GetActiveTl() 查询队列尾部并协商活动传输层与帧尺寸
特定命令 <Bus>Xcp_TlService(XcpChannel, pCmd) 处理传输层命令,可返回 OK、PENDING、SYNTAX、BUSY、UNKNOWN、OUT_OF_RANGE

如果 <Bus>Xcp_Send() 内部同步调用 Xcp_TlTxConfirmation(),会形成递归发送,必须预留足够调用栈。更稳妥的判断依据是当前传输层的完成语义,而不是假设所有 CanIfFrIfSoAd 路径都能立即确认。

XcpAppl 回调按责任域组织

所有 XcpAppl 服务都不可重入,调用上下文可能是任务级,也可能是任务/中断级;实现时首先要按表中的责任域确认执行时间和共享数据所有权。

责任域 回调 输入、返回值与用途
时间与地址 XcpAppl_GetTimestamp()XcpAppl_GetPointer() 返回配置宽度的时间戳;把 AddrExt+Addr 转成项目指针
设备标识 XcpAppl_GetIdData() 由 Id 选择标识数据,回传指针和 uint32 长度,受 XcpGetIdGeneric 控制
Seed & Key XcpAppl_GetSeedLength()XcpAppl_GetSeed()XcpAppl_UnlockWriteKey()XcpAppl_Unlock() 分片交换 Seed/Key,最终返回被解锁资源位或 0
普通内存 XcpAppl_CalibrationWrite()XcpAppl_MeasurementRead()XcpAppl_CheckReadAccess()XcpAppl_CheckProgramAccess() 按地址和 Size 复制或拒绝;读写可异步,范围检查同步
命令扩展 XcpAppl_UserService()XcpAppl_OpenCmdIf() 前者处理用户命令,后者扩展协议命令并写 Response/Length;可返回 PENDING
发送阻塞 XcpAppl_SendStall() Xcp_PutChar()/Xcp_SendEvent() 的发送停滞选择拒绝新报文 0 或继续 1
Flash Kernel XcpAppl_DisableNormalOperation()XcpAppl_StartBootLoader() 校验 RAM 下载区并停止正常运行;随后转入 Bootloader
直接编程 XcpAppl_Reset()XcpAppl_ProgramStart()XcpAppl_FlashClear()XcpAppl_FlashProgram() 复位、准备、按 Address/Size 擦除、按 Data/Address/Size 编程
DAQ 恢复 XcpAppl_DaqResume()XcpAppl_DaqResumeStore()XcpAppl_DaqResumeClear() 在 RAM 与非易失存储间恢复、保存、清除完整动态 DAQ 结构
标定持久化 XcpAppl_CalResumeStore()XcpAppl_GetCalPage()XcpAppl_SetCalPage()XcpAppl_CopyCalPage() 保存标定数据,查询/切换/复制逻辑页
Freeze 与 CRC XcpAppl_SetFreezeMode()XcpAppl_GetFreezeMode()XcpAppl_CalculateChecksum() 保存 Segment Freeze 状态;按内存区计算自定义校验和
状态与复制 XcpAppl_ConStateNotification()XcpAppl_MemCpy() 通知新连接状态;在任务/中断级同步复制 Size 字节
互补通道 XcpAppl_DaqPrepare()XcpAppl_DaqCleanup()XcpAppl_GetSendQueueRef()XcpAppl_TransmitSendQueueElement() 准备/释放资源,获取 Event 缓冲并提交填充完成的 ODT

XcpAppl_UserService()XcpUserDefinedCommand 控制,接收完整 Cmd 并返回 XCP_CMD_OKXCP_CMD_PENDINGXCP_CMD_SYNTAXXcpAppl_OpenCmdIf()XcpOpenCommandInterface 控制,还能直接构造 Response 和 Length,未知命令返回 XCP_CMD_UNKNOWN。两者都能扩展协议,但不能改变标准命令已经规定的状态和安全约束。

多通道 PDU 配对与资源尺寸配置

Single Client Mode 可以配置多个通道,但同一时刻只激活收到 CONNECT 的一个通道;Multi Client Mode 允许多个通道独立同时活动。对 CAN 和 Ethernet,每个逻辑通道需要一个 Rx PDU 和一个 Tx PDU,并由 /MICROSAR/Xcp/XcpConfig/XcpPdu/XcpRxPdu/XcpTxPduContRef 配成一对。FlexRay 只有一个通道,通过 PDU Type 区分方向,不采用同样的配对方式。

图 6:一个 XCP 通道的 Rx PDU 与 Tx PDU 配对

DAQ 资源不能只按变量个数估计:

配置参数 决定的资源 估算方式与约束
XcpMaxNumberOfOdtDaq DAQ 方向最大 ODT 数 默认最大值 123 的资源代价较低;启用 8 位 Absolute ODT PID 时受 PID 空间限制
XcpMaxNumberOfOdtEntries 全部 DAQ List 共享的测量对象池 计划测 100 个独立变量时至少配置 100,与单个变量大小无关
XcpMaxNumberOfOdtStim STIM 方向最大 ODT 数 与 DAQ ODT 是相反方向的独立容量
XcpSendQueueSize 某个核可积压的完整 ODT 需覆盖一次 Event 产生量和传输层最坏发送等待窗口
XcpDAQMessageHeader PID 编码 默认 8 位 Absolute ODT;16 位 Relative ODT/Absolute DAQ 扩大配置但占用更多带宽

以标准 CAN 为例,DTO 中扣除 PID 后最多可放 7 个 1 Byte 测量量,10 个变量至少需要 2 个 ODT。16 位 PID 适合超大 DAQ 配置,却会进一步压缩标准 CAN 的有效载荷。RAM 评估应把 ODT Entry Pool、各核 Send Queue、动态 Xcp_ChannelStruct 和互补通道缓冲一起计算;A2L 的 IF_DATA XCP 可用 DAQ_MEMORY_LIMIT 表达 XcpMaxNumberOfOdtEntries,但 ODT_SIZEDAQ_SIZE 等平台相关结构无法仅靠该表精确建模。CPU 时间同样依赖平台、编译器和选项,不能从一个通用常数推出。

DET、返回值与明确不支持的能力

开启 /MICROSAR/Xcp/XcpGeneral/XcpDevErrorDetect 后,开发错误默认交给 Det_ReportError();也可替换为同签名服务。XCP Module ID 为十进制 212。该模块不向 DEM 报告 production error,因此现场没有 DEM Event 不能推出 XCP 路径没有异常。

DET 错误码 触发条件
0x0A Xcp_Init() 使用错误参数
0x0B Channel ID 非法,或该通道未配置调用 API 所需功能
0x0C Event Channel ID 非法,或未配置所需功能
0x0D 指针参数为 NULL_PTR
0x0E Channel ID 非法或通道功能不匹配的另一类服务检查
0x10 模块未初始化就调用服务
0x11 模块已初始化后再次调用 Xcp_Init()
0x12 Xcp_Event() 从错误 Core 调用

常用 Service ID 包括 Xcp_Init0x00Xcp_SendEvent0x03Xcp_Disconnect0x06Xcp_TlRxIndication0x0BXcp_TlTxConfirmation0x0CXcp_SetActiveTl0x0FXcp_ModifyProtectionStatus0x14Xcp_DaqConfigInit0x15Xcp_DaqSetComplementaryChannel0x16Xcp_EventGetDaqSize0x17Xcp_Event0xC9Xcp_StimEventStatus0xFDXcp_MainFunctionXcp_TlMainFunction 没有对应 Service ID。

接口返回错误与 DET 要分开读。Xcp_SendEvent()E_NOT_OK 表示 EV 发送被拒,应由调用方稍后重试;Xcp_EventGetDaqSize()E_NOT_OK 表示 DAQ List 无效;XCP_EVENT_DAQ_OVERRUN 表示已丢测量数据;XCP_EVENT_STIM_OVERRUN 表示需要写入时没有新 STIM 数据;XCP_CMD_BUSY 则表示命令当前没有执行。它们都是运行期结果,不应被开发错误开关吞掉。

能力边界还包括:不支持 GET_SLAVE_ID、CDD Transport Layer、Xcp_SetTransmissionMode、bitwise STIM、静态 DAQ、Interleaved Communication、SERV_RESET、事件形式的原生 Overload Indication、标准 PROGRAM_FORMAT、标准 PROGRAM_VERIFY、ODT Optimization;Xcp_<Module>TriggerTransmit 仅对 FrIf Transport Layer 提供。事件包本身不保证送达,因为 EV 不被确认;需要可靠状态闭环时,应用不能把单次 Xcp_SendEvent() 当作持久确认通道。

生成文件、A2L 与内存映射约束

协议实现位于 Xcp.c,公共声明和类型分别在 Xcp.hXcp_Types.h,内部声明在 Xcp_Priv.h。项目需要修改的是 XcpAppl.c/XcpAppl.h,因为地址转换、访问保护、分页、Flash 和互补通道都依赖应用。配置工具生成 Xcp_Cfg.hXcp_Lcfg.cXcp_Lcfg.h,其中 Event Channel 的符号名位于 Xcp_Lcfg.h

工具链还生成 XCP.a2lXCP_daq.a2lXCP_events.a2lXCP_Checksum.a2l。它们应在主 A2L 的 IF_DATA XCP 与 DAQ 区正确 include,传输层还需要自己的 A2L 描述。ECU 中 Station Identifier 应随软件版本变化,让 MCS 自动选择正确数据库;通用 GET_ID 则通过 XcpAppl_GetIdData() 返回项目定义的标识。若标识与实际链接地址不匹配,协议连接正常仍可能访问错误对象。

XCP_START_SEC_VAR_NOCACHE_NOINIT_32BIT 必须映射到 32 位对齐、满足其缓存属性的段。TriCore 等不支持非对齐访问的架构若把该段放错,会在运行时触发 trap。这个约束必须在 linker map 上验证,不能仅凭 MemMap 宏名称判断链接器最终放置正确。

小结

XCP 的关键不是能否建立一个测量连接,而是能否保留数据的时间含义并限制写入能力。Xcp_Event() 决定采样点,ODT 和 Exclusive Area 决定一致性粒度,各核 Send Queue 与 Xcp_TlMainFunction() 决定发送延迟,Slave Timestamp 才能把工具端时间线重新对齐。把这些边界混在一起,最快的总线也会显示错误顺序。

写路径则必须经过另一组明确决策:Seed & Key 控制资源,地址/Size 回调控制内存范围,Page Switching 控制在线标定视图,应用直编程与 RAM Flash Kernel 取决于 MCU 能否边执行边擦写。最后还要把 MAX_DTO、队列容量、临界区时长、回调上下文和 32 位内存映射放在同一份集成预算中。只有这两条链同时成立,DAQ/STIM 的高吞吐与在线标定的高权限才不会互相掩盖风险。


作者:不脱发的程序猿。本文同步自作者 CSDN 博客。

原文:https://handsome-man.blog.csdn.net/article/details/163764280

创作许可协议

本项目采用 CC BY(署名) 进行许可。

评论(0)
- 没有更多了 -