☰
【AUTOSAR】 Classic Platform COM 模块--从入门到放弃
2026/9/26 11:08:33 网站建设 项目流程

【AUTOSAR】 Classic Platform COM 模块–从入门到放弃

这份材料主要依据AUTOSAR_CP_SWS_COM.pdf(R23-11)整理,另外参考了 RTE(AUTOSAR_CP_SWS_RTE.pdf)、PDU Router(AUTOSAR_CP_TPS_ECUConfiguration.pdf)、BswM(AUTOSAR_CP_SWS_BSWModeManager.pdf)、System Template(AUTOSAR_CP_TPS_SystemTemplate.pdf)以及 Transformer、LdCom 等规范。内容偏入门,重点是把"COM 到底在干什么、哪些配置项会影响行为"讲清楚。


目录

  1. COM 模块是干什么的
  2. 基本概念:Signal、Signal Group 与 I-PDU
  3. 打包、解包与字节序
  4. 什么时候发:传输属性与传输模式
  5. 收到之后:过滤与超时监控
  6. 和周边模块怎么配合
  7. 常用 API 与回调
  8. 上手建议

1. COM 模块是干什么的

AUTOSAR 的分层结构里,每一层只跟紧邻的层打交道,跨层的细节都被接口挡在后面。应用层的软件组件(SWC)脑子里装的是"车速"“转速”"车门开没开"这类带物理含义的值,而总线那头只认字节。COM 就是负责把这两种表达方式对接起来的那一层。

它的位置夹在 RTE 和 PDU Router(PduR)之间:

  • 往上,SWC 通过 RTE 读写信号。跨 ECU 通信时 RTE 会直接调用 COM 的接口,比如Com_SendSignal、Com_ReceiveSignal;软件集群(Software Cluster)则通过 ComProxy 访问 COM。
  • 往下,COM 不直接操作 CAN、LIN 或以太网驱动。它收发的单位统一是 I-PDU,全部交给 PduR:要发送时调PduR_ComTransmit;总线收到报文时 PduR 反过来回调Com_RxIndication;底层发完了再回调Com_TxConfirmation。
  • 旁边还有一组管状态的模块。BswM 根据 ECU 当前状态决定哪些 I-PDU 组该开、哪些该关(Com_IpduGroupStart/Com_IpduGroupStop),也能按需打开或关掉接收超时监控(Com_EnableReceptionDM/Com_DisableReceptionDM);EcuM 负责上电时Com_Init、下电时关闭通信。

有一点要说清楚:COM 从不自己判断"现在该不该通信"。它只是按上层给的指令干活——什么时候开、什么时候关、发送周期多长,全部来自配置和模式管理模块。

把这些关系画出来就是这样:

从图上能看出 COM 的价值:它在"信号"和"I-PDU"这两套数据视图之间做了隔离。上层改个信号名,不用动总线配置;总线换一帧格式,也不用动应用代码。


2. 基本概念:Signal、Signal Group 与 I-PDU

2.1 信号(ComSignal)

ComSignal 是 COM 眼里最小的数据单位,对应通信矩阵里的一行,比如车速VehicleSpeed、发动机转速EngineSpeed。

每个信号在配置时会绑定到某个 I-PDU,并说明自己在那帧里的位置:从第几个字节的第几位开始、占多少位。位置信息来自通信矩阵,工具(DaVinci Configurator、EB tresos 之类)最终把它翻译成Com_Cfg.c里的打包描述。

2.2 信号组(ComSignalGroup)与 Shadow Buffer

有些数据必须成组出现。发动机转速和时间戳就是一对:如果转速更新了、时间戳没跟上,接收端会拿到"新转速 + 旧时间戳"这种自相矛盾的组合。信号组就是为这种场景准备的——一组信号要么一起更新,要么一起不更新。

实现手段是给每个信号组配一块 Shadow Buffer(影子缓冲区)。发送和接收两条路径上的用法不太一样。

发送侧:

  1. 应用先逐个调用Com_UpdateShadowSignal(),把各个子信号写进 Shadow Buffer。此时真正的 I-PDU 发送缓冲区还没动过。
  2. 所有子信号写完后,再调一次Com_SendSignalGroup()。COM 在这一个调用里把 Shadow Buffer 整体拷进 I-PDU,不存在拷到一半被打断的可能。
  3. 之后该不该发、什么时候发,仍然由这个 PDU 的传输模式决定。

接收侧:

  1. 收到含信号组的 I-PDU 时,COM 先把整组数据解包进接收侧的 Shadow Buffer。
  2. 应用调用Com_ReceiveSignalGroup(),这个调用会锁定 Shadow Buffer 并拿到一份快照。
  3. 然后用Com_ReceiveShadowSignal()逐个读子信号。因为读的是同一份快照,读出来的值必然来自同一帧。

顺序别搞反:接收侧必须"先锁再读"。否则读到一半又来了新帧,前后两次读到的数据就分属不同帧,等于白锁。

2.3 I-PDU(ComIPdu)

ComIPdu 是 COM 交给 PduR 的数据块,里面装着一到多个信号或信号组。一个 ComIPdu 通常就对应一帧 CAN 报文、一帧 LIN 报文,或者一个以太网 payload。

发不发、多久发一次、发几次——这些参数都挂在 I-PDU 上,不挂在信号上。这一点在排查发送异常时很关键。

2.4 Update Bit(更新位)

Update Bit 是可选功能,作用是在 PDU 里额外占一位,明确告诉对面"这个信号我这轮更新过"。

  • 发送侧:上层调用Com_SendSignal时,COM 自动把该信号对应的 Update Bit 置 1;等这个 I-PDU 真正发出去、或者被Com_TriggerTransmit取走之后,Update Bit 清零。
  • 接收侧:COM 收到 I-PDU 后先看 Update Bit。为 1,说明对方确实更新了这个信号,正常解包并通知上层;为 0,则认为对方这轮没动它,直接跳过。

它的价值在于区分"信号值变了"和"信号值恰好和上轮一样"。像 E2E 里带计数器的场合,没有 Update Bit 就很难判断对面是真的重发还是压根没发。


3. 打包、解包与字节序

3.1 支持的数据类型

  • 整型:boolean、uint8、uint16、uint32、uint64、sint8、sint16、sint32、sint64
  • 浮点:float32、float64
  • 字节数组:uint8[n],分固定长度(UINT8_N)和动态长度(UINT8_DYN)两种

3.2 字节序转换

COM 要处理两层字节序的差异:CPU 内存里怎么放,和总线上约定怎么放。这两者不一定一致,转换由 COM 完成,应用层不用管。

  • 小端(Little-Endian,Intel 格式):低位字节在低地址,规范里写作MostSignificantByteLast。配置项startPosition指的是信号**最低有效位(LSB)**所处的位置。
  • 大端(Big-Endian,Motorola 格式):高位字节在低地址,规范里写作MostSignificantByteFirst。startPosition指的是信号**最高有效位(MSB)**所处的位置。
  • Opaque(不透明类型):用于uint8[n]数组这类不关心字节序的数据。COM 原样搬运,不做任何颠倒。

字节序最容易踩的坑,就是把startPosition的含义记混。同一个起始位数值,在小端和大端里指的是信号的两端,配错了偏移量会整体偏掉。

位编号方面,AUTOSAR 统一采用 Sawtooth(锯齿)模式、位序递减:一帧的第 0 字节里,bit 0 是 LSB、bit 7 是 MSB;信号填充时从 LSB 往 MSB 逐位递增映射。

3.3 符号扩展

当有符号信号的位宽小于承载它的变量类型时(例如线上传 10 bit 的sint10,本地用sint16接收),COM 在解包时会自动做符号扩展。

举个例子:收到的 10 bit 值是11 1111 1101b,按有符号数读出来是 -3。拷进 16 bit 变量时,如果高位补 0 就变成正数 1013 了,所以 COM 把高位全部补 1,得到1111 1111 1111 1101b,在 16 bit 下依然是 -3。

这个动作不需要应用层参与,但理解它有助于解释"为什么接收缓冲区里读到的负值,和原始帧里的字节看着对不上"。


4. 什么时候发:传输属性与传输模式

一个 I-PDU 什么时候被发出去,由两件事共同决定:信号级的传输属性(Transfer Property)和PDU 级的传输模式(Transmission Mode)。前者回答"上层写了个新值,要不要马上发",后者回答"这个 PDU 平时按什么节奏发"。

4.1 传输属性(Transfer Property)

每个 ComSignal / ComSignalGroup 在配置时都得选一个:

  1. PENDING(挂起):调Com_SendSignal只更新 COM 内部缓冲区,不触发发送。数据等着这个 PDU 周期性发出去,或者被同一个 PDU 里别的 TRIGGERED 信号顺带带出去。
  2. TRIGGERED(触发):调Com_SendSignal会触发该 PDU 立即发送,最迟在下一次Com_MainFunctionTx里执行。
  3. TRIGGERED_ON_CHANGE(变化触发):只有新值和 COM 缓冲区里的旧值(或初始值)不一样时才触发发送。值没变就不发,能省不少总线负载。
  4. TRIGGERED_WITHOUT_REPETITION/TRIGGERED_ON_CHANGE_WITHOUT_REPETITION:触发行为同上两条,但忽略配置的重复发送次数(ComTxModeNumberOfRepetitions),只发一次。

第 3 条的判断基准是 COM 缓冲区里的值,不是总线上的值。中间如果发送被取消过,比较基准可能和预期不一致。

4.2 传输模式(Transmission Mode)

每个 ComTxIPdu 会配置一种传输模式:

传输模式常量发送行为
直接/多次模式DIRECT由事件(如 TRIGGERED 信号更新)触发,立即连发1 + N次,N 为ComTxModeNumberOfRepetitions
周期模式PERIODIC按固定间隔ComTxModeTimePeriod循环发送
混合模式MIXED两者结合:平时按周期发;一旦有 TRIGGERED 信号更新,额外插入一次直接发送
无发送模式NONECOM 不主动发起发送。常见于由 PduR 轮询调用的Com_TriggerTransmit场景,或用作 TMS 条件为假时的禁用状态

4.3 动态切换:TMS

每个 Tx I-PDU 都可以在ComTxModeTrue和ComTxModeFalse两套参数之间动态切换,这套机制叫 TMS(Transmission Mode Selection,传输模式选择)。

评估原理:

  1. I-PDU 里可以配置若干参与 TMS 评估的信号,每个信号带一个ComFilter条件。
  2. 只要有一个参与信号的ComFilter评估为真,该 I-PDU 的 TMS 状态就是 True,采用ComTxModeTrue;所有参与信号都评估为假时,状态为 False,采用ComTxModeFalse。
  3. TMS 状态一旦变化,COM 会在当前或下一次Com_MainFunctionTx中立刻按新模式发包,不需要额外触发。

典型用法是让一个 PDU 在"空闲时慢发、有事件时快发"之间切换,把周期性通信的确定性和事件驱动通信的实时性放在同一帧上兼顾。


5. 收到之后:过滤与超时监控

5.1 接收过滤(Reception Filtering)

收到新信号时,COM 可以按配置先做一次筛选,只有通过过滤的数据才会更新缓冲区并通知上层。常用的ComFilterAlgorithm有:

  • ALWAYS:始终放行。
  • NEVER:始终丢弃。
  • MASKED_NEW_EQUALS_X:(New_Value & Mask) == X时放行。
  • MASKED_NEW_DIFFERS_X:(New_Value & Mask) != X时放行。
  • MASKED_NEW_DIFFERS_MASKED_OLD:(New_Value & Mask) != (Old_Value & Mask)时放行,也就是只有关心的那些位变了才放行。
  • NEW_IS_WITHIN/NEW_IS_OUTSIDE:数值落在(或超出)区间[Min, Max]时放行。
  • ONE_EVERY_N:每收到 N 次数据只放行一次,用于降低上层处理频率。

5.2 接收截止时间监控(Rx DM)

总线断了、节点掉线、报文丢了,这些在实车上都可能发生。Rx DM 就是用来发现这类问题的。

监控参数(配在 ComRxIPdu 上):

  • ComFirstTimeout:从 I-PDU 启动(Com_IpduGroupStart)到收到第一帧报文的允许等待时间。
  • ComTimeout:连续两帧报文之间的最大允许间隔。

超时之后 COM 做两件事:

  1. 处理数据。按ComRxDataTimeoutAction的配置决定:保持旧值(NONE)、替换为指定的替代值ComTimeoutSubstitutionValue(REPLACE),或者替换成初始值ComSignalInitValue(SUBSTITUTE)。
  2. 通知上层。调用预先配置的回调<ComUser_CbkRxTOut>,RTE 侧对应Rte_COMCbkRxTOut,把超时事件交给应用处理。

需要注意的是,Rx DM 默认是开的,但可以按 I-PDU 组关掉再打开(Com_DisableReceptionDM/Com_EnableReceptionDM),比如某些模式切换过程中不希望报超时。

5.3 发送截止时间监控(Tx DM)

Tx DM 监控的是另一个方向的问题:发送请求发出去之后,底层(PduR / CAN 驱动)有没有在规定时间内回Com_TxConfirmation。

如果在ComTimeout周期内没等到确认,COM 会取消超时定时器并调用<ComUser_CbkTxTOut>,把发送失败这件事告知应用层。它的典型用途是发现"CAN 控制器忙不过来了"或"总线一直仲裁失败"这类发送侧异常。


6. 和周边模块怎么配合

6.1 COM 与 RTE 的收发流程

发送(Sender-Receiver,跨 ECU):

  1. 应用 SWC 调用Rte_Write_p_data(value)。
  2. RTE 按配置映射,调用Com_SendSignal(SignalId, &value)。
  3. COM 做字节序转换、符号扩展,打包进 I-PDU 缓冲区,置 Update Bit。
  4. COM 按该信号的 Transfer Property 判断是否需要立即发送,需要就调PduR_ComTransmit(PduId, &PduInfo)。
  5. 底层发送成功,PduR 回调Com_TxConfirmation(),COM 再通过Rte_COMCbkTAck()通知 RTE。

接收:

  1. 总线收到报文,PduR 调用Com_RxIndication(PduId, &PduInfo)。

  2. COM 按固定顺序做预处理:

    1. 重置该 I-PDU 的 Rx DM 定时器
    2. 检查 Update Bit
    3. 字节序转换与符号扩展
    4. 无效值检查(Data Invalidation)
    5. 接收过滤(Reception Filtering)
    6. 重置信号级 DM 定时器
    7. 通过回调(如Rte_COMCbkRxAck())通知 RTE
  3. SWC 调用Rte_Read_r_data(&value)读出最新的信号值。

这个顺序是规范定死的。测到"值更新了但回调没来"这类现象时,按这七步从头对一遍,基本能定位到问题出在哪一步。

6.2 COM Based Transformer(ComXf)

面对结构体或组合数据类型,逐个处理 Primitive 信号效率不高。AUTOSAR 提供了 ComXf(COM 转换器):

  • 定位:ComXf 位于 RTE 内部/上层,把应用层的结构体对象按信号组格式序列化成线性字节数组。
  • 和普通 COM 的区别:普通 COM 逐个处理 Primitive 信号;ComXf 配合Com_SendSignalGroupArray()/Com_ReceiveSignalGroupArray(),直接以连续内存数组的形式把数据交给 COM,省掉逐信号的打包开销。

6.3 Large Data COM(LdCom)

传大块数据时(刷写 Data Block、诊断大数据包、传感器点云),传统 COM 的打包、解包、过滤、Update Bit 处理会带来不小的 CPU 和内存开销,得不偿失。LdCom 就是为这类场景准备的:

  • 零拷贝、直接传递:绕过信号打包和过滤流程,在 RTE 与 PduR 之间直接传字节数组指针。
  • 轻量:只支持基于事件的非周期大块传输,开销显著更低。

6.4 E2E 保护配合

传输安全关键(ASIL B~D)的信号时,数据里要嵌入 CRC 校验码和序列计数器(Sequence Counter)。做法是在 COM 上层挂一个E2E Transformer(E2EXf):它在序列化数据的前端或后端写入 CRC 与 Counter,再交给 COM(或 ComXf)填入 I-PDU 空间。这样即便中间经过的总线、网关都不可信,端到端仍能检测出数据被篡改或重复——也就是所谓"黑通道(Black Channel)"保护。


7. 常用 API 与回调

7.1 上层(应用 / RTE)调用 COM

函数原型说明
Com_SendSignalStd_ReturnType Com_SendSignal(Com_SignalIdType SignalId, const void* SignalDataPtr)更新指定信号的发送缓冲区,并按该信号的 Transfer Property 决定是否触发 I-PDU 发送
Com_ReceiveSignalStd_ReturnType Com_ReceiveSignal(Com_SignalIdType SignalId, void* SignalDataPtr)从接收缓冲区读出指定信号的最新值
Com_SendSignalGroupStd_ReturnType Com_SendSignalGroup(Com_SignalGroupIdType SignalGroupId)把 Shadow Buffer 整体原子地刷进 I-PDU
Com_ReceiveSignalGroupStd_ReturnType Com_ReceiveSignalGroup(Com_SignalGroupIdType SignalGroupId)锁定并快照信号组数据到接收侧 Shadow Buffer
Com_UpdateShadowSignalStd_ReturnType Com_UpdateShadowSignal(Com_SignalIdType SignalId, const void* SignalDataPtr)写发送侧 Shadow Buffer 中的子信号
Com_ReceiveShadowSignalStd_ReturnType Com_ReceiveShadowSignal(Com_SignalIdType SignalId, void* SignalDataPtr)读接收侧 Shadow Buffer 中的子信号
Com_IpduGroupStartvoid Com_IpduGroupStart(Com_IpduGroupIdType IpduGroupId, boolean Initialize)启动指定的 I-PDU 组
Com_IpduGroupStopvoid Com_IpduGroupStop(Com_IpduGroupIdType IpduGroupId)停止指定的 I-PDU 组,同时取消组内挂起的发送请求和 DM 定时器
Com_EnableReceptionDMvoid Com_EnableReceptionDM(Com_IpduGroupIdType IpduGroupId)使能指定 I-PDU 组的接收超时监控
Com_DisableReceptionDMvoid Com_DisableReceptionDM(Com_IpduGroupIdType IpduGroupId)关闭指定 I-PDU 组的接收超时监控

7.2 底层(PduR)调用 COM

函数原型说明
Com_RxIndicationvoid Com_RxIndication(PduIdType RxPduId, const PduInfoType* PduInfoPtr)PduR 收到总线报文后调用,把原始 I-PDU 字节流交给 COM
Com_TxConfirmationvoid Com_TxConfirmation(PduIdType TxPduId, Std_ReturnType result)底层发送完成后,经 PduR 告知 COM 发送结果
Com_TriggerTransmitStd_ReturnType Com_TriggerTransmit(PduIdType TxPduId, PduInfoType* PduInfoPtr)由 LIN、FlexRay 等轮询式总线调用,请求 COM 把最新 I-PDU 数据拷到给定缓冲区

7.3 COM 通知上层的回调

这些回调由工具根据配置生成,最终接到 SWC 的运行实体上:

  • <ComUser_CbkRxAck>(RTE 中为Rte_COMCbkRxAck):信号/信号组成功接收并读取后回调。
  • <ComUser_CbkTxAck>(RTE 中为Rte_COMCbkTAck):发送请求拿到底层Com_TxConfirmation确认后回调。
  • <ComUser_CbkRxTOut>(RTE 中为Rte_COMCbkRxTOut):接收超时(Rx DM)触发时回调。
  • <ComUser_CbkTxTOut>:发送确认超时(Tx DM)触发时回调。

8. 上手建议

COM 的配置项不少,但真正决定行为的其实就两条线索:

  • 数据怎么走:应用变量 → RTE 接口 → COM 打包与字节序转换 → I-PDU 缓冲区 → PduR 路由 → 总线驱动。这条线决定了"值对不对"。
  • 什么时候走:BswM / ComM 的模式切换 → I-PDU 组的启停 → 截止时间监控的开关。这条线决定了"发不发、什么时候发"。

调试的时候按这两条线分开看,比堆在一起翻配置快得多。

给几个具体建议:

  1. 对着生成的代码看配置。工具最终会产出Com_Cfg.h和Com_Cfg.c,里面能看到每个信号的实际ComSignalType、ComBitPosition、ComBitSize、字节序,以及每个 I-PDU 的ComTxMode。配合 CANoe 抓报文对照,比只看 ARXML 直观。
  2. 发不出去先查三处:I-PDU 组开了没有、Transfer Property 是不是配成了PENDING、TMS 当前落在ComTxModeFalse还是True。
  3. 收不到先查三处:Rx DM 是不是已经超时并替换了数据、ComRxDataTimeoutAction配的是哪种动作、接收过滤是否把值挡掉了(尤其MASKED_*系列,掩码配错会很隐蔽)。
  4. 信号组务必"先锁再读",并且不要在Com_ReceiveSignalGroup和Com_ReceiveShadowSignal之间插入可能阻塞或耗时的操作。
  5. Update Bit 不是默认能力,它是配置项,且会真实占用 PDU 空间。设计通信矩阵时要提前和信号对齐好位宽。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询