【AUTOSAR】 Classic Platform LdCom(LargeDataCOM)–从入门到放弃
这份材料以
AUTOSAR_CP_SWS_LargeDataCOM.pdf为核心,另外参考了 System Template(AUTOSAR_CP_TPS_SystemTemplate.pdf)、RTE(AUTOSAR_CP_SWS_RTE.pdf)、PduR 以及 TP 相关规范。适合刚接触 AUTOSAR 通信栈、需要配置或测试大数据传输的人。
目录
- LdCom 是干什么的
- 在通信栈里的位置
- 两种 API 形态:IF-API 与 TP-API
- 配置上的硬性约束
- 和 RTE 与 Transformer 的配合
- 配置容器与关键参数
- 排障:三个常见问题
- 小结
1. LdCom 是干什么的
Standard COM 擅长的是小信号:1 bit 的开关、16 位的车速、几十字节的信号组。它的那套机制——信号打包解包、字节序转换、信号过滤、影子缓冲区、Update Bit——都是为这类数据设计的。
问题出在数据变大之后。雷达点云、相机图像块、刷写数据块、SOME/IP 或 ComXf 序列化之后的字节流,动辄几百字节到几 KB。这些数据如果还走 Standard COM,每帧都要按信号逐个提取、拷贝、再做一遍转换和过滤,CPU 和 RAM 的消耗都不划算——而且对一个"连续字节块"来说,这些处理本身就没有意义。
LdCom(Large Data COM)就是为这种场景准备的。它的思路很直接:把 Standard COM 里那些昂贵的处理全砍掉,只留下 ID 映射和透传。
它的主要特点:
- 不申请本地缓冲区。数据在上层和底层之间靠指针和回调直接传递,LdCom 自己不占 RAM。
- 不做任何数据处理。没有过滤、没有字节序转换,也没有超时监控,数据原样透传。
- 1 个 PDU 只放 1 个信号,不做多信号打包。
- 只支持事件触发发送,没有周期性发送。
两者的分工看图更清楚:
小信号走 Standard COM,大块字节数组和 Transformer 的输出走 LdCom,最后都汇到 PduR。
2. 在通信栈里的位置
LdCom 和 Standard COM 处于同一层(服务层),都夹在 RTE 和 PduR 之间。
往上看,LdCom 的用户有三类:
- RTE:应用 SWC 的数据经 RTE 交给 LdCom。
- Transformer 链:启用了 SOME/IP Serializer 之类的转换器时,序列化后的字节数组由 RTE 直接交给 LdCom。
- SwCluC_LdComProxy:多核、多软件集群架构下,跨集群的代理。
往下看,只有 PduR。LdCom 把上层的 SignalId 换算成 PduId,调 PduR 的接口把数据送出去;反过来 PduR 收到数据后回调 LdCom。
这个换算本身很简单,但它是理解 LdCom 全部接口的钥匙:
上层调用时传的是 SignalId(也就是配置里的LdComHandleId),PduR 收到的却是 PduId。中间的查表由 LdCom 在内部完成,上下两层都看不到对方的编号体系。
2.1 多核与 ECUC 分区
多核 ECU 上,LdCom 需要按 ECUC 分区的规范来配,AUTOSAR 提供了两种模式:
- 分区特定(Partition Specific):LdCom 为每个分区提供独立的回调实例,上层模块(比如 RTE)在各自分区的上下文里响应回调,不涉及跨核同步。
- 分区无关(Partition Agnostic):所有分区共用一套回调函数。这就要求回调必须是可重入的,能被不同核同时安全调用。
选哪种,取决于上层模块都跑在哪些分区,以及回调里有没有共享状态。共用一套回调看着省事,但只要回调里存了一点静态数据,就会变成跨核竞争。
3. 两种 API 形态:IF-API 与 TP-API
LdCom 有两种工作模式,由配置项LdComApiType决定,生成出来的接口完全不同。
| 对比项 | IF-API(LDCOM_IF) | TP-API(LDCOM_TP) |
|---|---|---|
| 适用场景 | 数据能装进一帧,不需要分片 | 数据超过一帧,需要 CAN-TP、SOME/IP-TP 之类分片 |
| 数据传递方式 | 一次调用带指针拷完(PduInfoType*) | 流式回调,TP 层分段来取 / 送数据 |
| 缓冲区要不要锁 | 调用返回就结束,不用锁 | 整个传输期间必须锁住,直到TpTxConfirmation/TpRxIndication |
| 涉及的下层接口 | PduR_LdComTransmit、LdCom_RxIndication | PduR_LdComTransmit、LdCom_CopyTxData、LdCom_CopyRxData等 |
3.1 IF-API 的发送流程
四步就完事,而且都在同一次调用里跑完:
- RTE 调
LdCom_Transmit(SignalId, PduInfoPtr)。 - LdCom 把 SignalId 换成 PduId,调
PduR_LdComTransmit。 - 驱动把帧发出去,
E_OK逐层返回。 - 硬件发完之后,PduR 回调
LdCom_TxConfirmation,LdCom 再触发<LdComUser_LdComCbkTxConfirmation>通知 RTE。
第 3 步和第 4 步是分开的,这点容易混淆:E_OK只代表"已受理",不代表"已经发到总线上"。真正发完要等TxConfirmation。
3.2 TP-API 的发送流程
- RTE 调
LdCom_Transmit,随后锁定发送缓冲区。锁住的原因很直接:后面 TP 层是异步来取数据的,中间应用要是把数据改了,发出去的包就前后不一致。 - LdCom 把请求转给 PduR,TP 层开始分片。
- 每要一段数据,TP 层调
LdCom_CopyTxData,LdCom 转成<LdComUser_LdComCbkCopyTxData>找 RTE 要。RTE 把指定长度的片段写进 TP 给的缓冲区。 - 第 3 步反复执行,直到所有分片发完。
- TP 层回调
LdCom_TpTxConfirmation,LdCom 通知 RTE,解锁发送缓冲区。
第 3 步的写法是关键:是"TP 层来拉",不是"LdCom 主动推"。正因为如此,LdCom 不需要任何中间缓冲区,数据直接从 RTE 的内存流到 TP 的帧里。
3.3 TP-API 的接收流程
- TP 层收到首帧(First Frame),调
LdCom_StartOfReception,把总长度TpSduLength和一个bufferSizePtr传进来。 - LdCom 转给 RTE 的
<LdComUser_LdComCbkStartOfReception>。RTE 在这里锁定接收缓冲区,并把缓冲区能装多少通过bufferSizePtr写回去。 - 每个分片到达,TP 层调
LdCom_CopyRxData;LdCom 转成<LdComUser_LdComCbkCopyRxData>,分片数据直接写进 RTE 的接收缓冲区。 - 所有分片到齐,TP 层回调
LdCom_TpRxIndication。LdCom 通知 RTE,解锁缓冲区,并按配置触发DataReceivedEvent。
如果第 2 步里 RTE 发现缓冲区装不下TpSduLength,应该直接返回BUFREQ_E_OVFL拒收,而不是硬写。这是防内存越界最关键的一步。
3.4 TriggerTransmit:让底层决定何时取数据
FlexRay、LIN 这类轮询式总线上,什么时候发不由上层决定,而是控制器来问。这时走的是另一条路:
- 下层调
LdCom_TriggerTransmit(TxPduId, PduInfoPtr)。 - LdCom 转给
<LdComUser_LdComCbkTriggerTransmit>。 - RTE 把准备好的数据写进
PduInfoPtr->SduDataPtr,并把实际长度回填。
4. 配置上的硬性约束
不是随便一个信号都能配成 LdCom,System Template 对此有硬性规定(规则 ID 见TPS_SYST_02016等)。
数据类型:处理的ISignal必须满足下面三条之一。
- 类型是
UINT8_N(定长字节数组) - 类型是
UINT8_DYN(变长字节数组) - 关联了 DataTransformation,也就是 Transformer 链的输出
打包与位置:
packingByteOrder必须是Opaque。LdCom 不做字节序转换,数据只能当不透明字节流处理。startPosition必须是 0,不支持在 PDU 里偏移插入信号。- 一个
ISignalIPdu里有且仅有 1 个ISignal。1:1,不允许把多个小信号打包进同一个 LdCom PDU。
传输属性与定时:
transferProperty只能是triggered或triggeredWithoutRepetition。不支持pending,也不支持自动重复发送。- 不允许定义更新位(
updateIndicationBitPosition)。 - 定时只能用事件控制(
EventControlledTiming),且numberOfRepetitions必须为 0。 - 不允许配置
minimumDelay(最小间隔定时器)。
这些约束背后的逻辑是一致的:LdCom 不维护本地缓冲区,也不做状态管理。所以配置里任何"需要记住上一次状态"的能力,它都用不了。
5. 和 RTE 与 Transformer 的配合
5.1 缓冲区锁与 RTE_E_COM_BUSY
TP-API 模式下传输跨越多个通信周期,必须防止中途被改。
- 发送方向:RTE 调完
LdCom_Transmit就锁住发送缓冲区。如果TpTxConfirmation还没回来,SWC 又去Rte_Write同一个信号,RTE 会返回RTE_E_COM_BUSY,拒绝覆盖。 - 接收方向:RTE 在
StartOfReception环节锁住接收缓冲区。TpRxIndication结束之前,SWC 去Rte_Read同样会拿到RTE_E_COM_BUSY。
这个返回值不是故障,而是正常的状态反馈。应用层该做的是先判断再决定,而不是当成错误来处理。
5.2 复杂结构体怎么走 Transformer
SWC 之间要传结构体时,通常配一条 Transformer 链:
- SWC 调
Rte_Write_<p>_<a>(&structData)。 - RTE 调 Transformer(比如
SomeIpXf_Serialize)把结构体序列化成字节数组,存进 RTE 申请的临时缓冲区。 - RTE 把这个字节数组当成
UINT8_N/UINT8_DYN信号,调LdCom_Transmit交给 LdCom。 - 接收端反过来:LdCom 收到字节数组,RTE 调
SomeIpXf_Deserialize还原成结构体,交给接收方 SWC。
Transformer 是 RTE 层的事情,LdCom 只看到一坨字节。这也解释了为什么它的配置约束里要求Opaque——它确实不关心里面装的是什么。
6. 配置容器与关键参数
LdCom 的行为完全由 ECUC 配置决定,容器层次如下:
| 容器 / 参数 | 类型与取值 | 说明 |
|---|---|---|
LdCom | 模块根容器 | |
└LdComGeneral | 全局设置 | |
└LdComDevErrorDetect | Boolean | 开发错误检测开关 |
└LdComVersionInfoApi | Boolean | 是否生成版本查询接口 |
└LdComConfig | 配置主体 | |
└LdComIPdu | 0…* | 每个 PDU 一项 |
├LdComHandleId | Integer 0…65535 | RTE / PduR 调用时用的句柄 ID,ECU 范围内唯一 |
├LdComApiType | LDCOM_IF/LDCOM_TP | 决定生成 IF 接口还是 TP 接口 |
├LdComIPduDirection | LDCOM_SEND/LDCOM_RECEIVE | 收发方向 |
└LdComPduRef | Reference | 指向 PduR 里的 IPdu |
└LdComUserModule | 0…* | 上层用户模块,一般是 RTE |
└LdComUserModuleCnf | 用户模块配置 | |
├LdComUserIPdu→LdComUserCbkHandleId | Integer | 用户侧回调句柄 |
└LdComUserCallback | 回调定义 | |
├LdComUserCallbackName | String | 回调函数名 |
└LdComUserCallbackType | 枚举 | 回调类型,见下 |
LdComUserCallbackType的常见取值有LDCOM_RX_INDICATION、LDCOM_TP_COPY_RX_DATA、LDCOM_TP_COPY_TX_DATA、LDCOM_TP_RX_INDICATION等,和前面讲过的各个回调一一对应。
这里最关键的一项是LdComApiType:配成LDCOM_TP才会生成LdCom_CopyTxData/LdCom_CopyRxData这些接口,配成LDCOM_IF就只生成LdCom_Transmit/LdCom_RxIndication。
7. 排障:三个常见问题
7.1 调用发送返回RTE_E_COM_BUSY
原因:这个信号配的是 TP-API。上一次发送还没结束(TP 层还在分片,RTE 发送缓冲区处于锁定状态),应用又调了一次发送。
怎么查:
- 看底层 TP 的传输速率和总线负载,确认有没有发生卡包或超时。
- 检查
LdCom_TpTxConfirmation是否正常触发、是否完成了解锁。 - 应用侧加一层发送状态判断,别在 BUSY 期间硬写。
7.2 配置工具报 “Invalid Signal Mapping”
原因:把一个普通标量信号(比如uint16)映射给了 LdCom,或者往同一个 LdCom IPdu 里塞了多个信号。
怎么查:
- 信号类型是不是
UINT8_N或UINT8_DYN。 startPosition是不是 0,packingByteOrder是不是Opaque。- 这个 IPdu 里是不是只有 1 个信号。
7.3 大数据传输时数据错乱或内存越界
原因:没按流式拷贝的规矩来,或者 RTE 给的接收缓冲区比发送方的总长度小。
怎么查:
- 在
LdCom_StartOfReception里核对TpSduLength和bufferSizePtr的大小关系。 - 装不下就返回
BUFREQ_E_OVFL,别硬写。
8. 小结
LdCom 做的事情其实很少:不缓冲、不转换、不过滤,只把上层的 SignalId 换成 PduId,然后把数据透传下去。省下来的这些开销,换来的正是大数据传输需要的效率。
判断一个信号该用哪个模块,看数据形态就够了:
- 要按位打包、要做字节序转换、要周期性发送、要监控超时 → Standard COM。
- 是一整块连续字节、要分片、由事件触发 → LdCom。
两者不是替代关系。同一个 ECU 上经常同时存在,各管一摊。