1. 为什么要搞懂 Transmission Mode 和 Transfer Property
做 AUTOSAR 通信栈的朋友应该都有这种感觉:COM 模块表面上就是“收收发发”,真正把协议栈配置跑通、报文按预期发出去,往往要卡在 Transmission Mode 和 Transfer Property 这两个概念上。不少刚接触 AUTOSAR 的工程师,配了信号、建了 PDU,结果一上板发现报文要么不发、要么乱发、要么发得太勤把总线占满,来回查了半天,最后定位到是这两个参数没配对。
这篇文章不是把规范翻译一遍,而是站在实际做项目的角度,把 Transmission Mode(传输模式)和 Transfer Property(传输属性)到底是什么、怎么配、容易踩什么坑,一次说清楚。我尽量用实际控制器开发中常见的场景来讲。内容主要面向正在做 BMS、VCU、MCU 等 ECU 通信开发的软件工程师,也适合刚从标准协议栈入门、被各种配置项绕晕的新手。
先说结论:Transmission Mode 解决的是“什么时候允许发”的问题,Transfer Property 解决的是“允许发的时候具体怎么发”的问题。这两者必须结合起来看,单独搞懂任何一个,配置的时候照样会踩坑。
2. Transmission Mode:控制信号发送的允许机制
2.1 四种模式的含义与定位
Transmission Mode 的中文叫法很多,有人叫传输模式,有人叫发送模式,但表达的都是同一件事:这个信号(或者信号组)在什么条件下被允许触发发送。规范里定义了四种模式:TRUE、FALSE、MIXED、NONE。我分别说一下在实际工程里的理解。
- TRUE:无条件允许发送。信号只要来了,就可以立即触发报文的发送,不受任何模式的限制。
- FALSE:无条件禁止发送。这个信号永远不会主动触发发送,除非你在代码里通过接口强行把 Transmission Mode 切到 TRUE。
- MIXED:混合模式。这种模式下,信号的发送同时受“TMS(Transmission Mode Selector)”和“TME(Transmission Mode Enable)”两个条件的控制。TMS 决定“方向”,TME 决定“闸门”,两者配合起来,可以实现类似“平时周期发,紧急时立即发”的效果。
- NONE:不附带任何传输模式条件。通常用于那些不需要走模式控制的信号,直接由上层决定发不发。这个模式配置相对少,但存在。
不少人看到这里会想:“我把所有信号都配成 TRUE 不就行了?反正都能发。” 能发是能发,问题是总线负载和发送时刻完全不可控。尤其像 CAN 这类带优先级仲裁的总线,如果所有报文都一窝蜂抢总线,高优先级报文倒是无所谓,低优先级报文会一直被“压制”,延迟越积越大,最后出现超时故障。Transmission Mode 的核心价值就是让开发者明确控制“什么时候允许发”,从而让总线行为可预期。
2.2 TMS 和 TME:两个容易混淆的控制条件
在 MIXED 模式下,TMS 和 TME 是两个正交的控制条件,很多人第一次看就被绕进去了。我打个比方:TMS 像红绿灯的“方向灯”,决定哪条道可以走;TME 像“栏杆”,决定这条道现在能不能过车。只有“方向对”且“栏杆抬起”,车(信号)才能出去。
TMS 是一个信号,取值只有 0 和 1。它由上层软件写入,COM 模块读取这个值来判断当前该用哪种“子方式”。当 TMS 为 0 时,对应“低路(Low Path)”逻辑;当 TMS 为 1 时,对应“高路(High Path)”逻辑。这里的“低路”和“高路”只是名字,不代表优先级高低,你可以理解为两套发送策略。比如低路策略是“20ms 周期发一次”,高路策略是“10ms 周期发一次”,或者高路策略是“有事件就立刻发一次”。
而 TME 也是一个信号,同样取值 0 和 1,它表示“允许”条件是否成立。只有当 TME 为 1 时,发送才被真正使能。
两个条件合起来,就构成了 MIXED 模式下经典的真值表关系:
| TMS | TME | 发送行为 |
|---|---|---|
| 0 | 0 | 不发送 |
| 0 | 1 | 按低路策略发送 |
| 1 | 0 | 不发送 |
| 1 | 1 | 按高路策略发送 |
注意一个细节:TMS 为 1 但 TME 为 0 时,也不发送。我在实际调试中见过不少同事只把 TMS 拉高、忘记拉 TME,结果报文死活不发,查了半天才发现是 TME 还卡在 0。TMS 和 TME 的初始值都需要在配置阶段提前设置好,不同的项目要求不同,有的要求上电后立刻周期发送,那就得把 TME 初始值配成 1。
2.3 模式和信号组(Signal Group)的关系
Transmission Mode 不直接作用在单个信号上,而是作用在**信号组(Signal Group)**上。这是另一个高频踩坑点。
AUTOSAR COM 里,信号组是若干个信号打包在一起的集合,所有组内信号共享同一套发送模式控制逻辑。也就是说,你配置 Transmission Mode 时,实际是配在 Signal Group 上的;单个信号本身只有 Transfer Property,没有 Transmission Mode。从工具配置界面来看,一般先创建 Signal Group,然后把需要的信号都挂进组里,再为这个组配置 Transmission Mode。如果某个信号没有归属到任何信号组,那它只能配置 Transfer Property,没有 Transmission Mode 的概念。
为什么要有信号组?因为实际工程中很多信号必须“同生共死”。举个很常见的例子:仪表上要显示车速、转速、油量,这三个信号分开单独发没意义,必须放到同一个报文里一起发。如果车速变化触发发送、油量没变化就不发,那报文就会变成“半新半旧”的数据,对接收端来说很难处理。放进同一个信号组后,只要组内任何一个信号满足触发条件,整组信号就会一起被打包发出,保证数据一致性。
这块配置的时候还有一个隐藏逻辑:一个信号组只能配置一种 Transmission Mode,但一个信号组里的不同信号可以配置不同的 Transfer Property。这两种机制叠加起来,才构成了完整的发送策略。
3. Transfer Property:数据发送的具体行为策略
3.1 四种属性的定义和触发条件
如果说 Transmission Mode 是“门卫”,那 Transfer Property 就是“门卫放行后,人怎么走出去”的规则——是排队一个个走、还是一窝蜂冲出去、还是只有变化时才出去。AUTOSAR 标准里定义了四种 Transfer Property:PENDING、TRIGGERED、TRIGGERED_ON_CHANGE、TRIGGERED_WITHOUT_REPETITION。
- PENDING(等待):表示信号永远处于“等待”状态,不触发任何发送。这个属性通常用于占位符信号,或者接收方向的数据预留。注意,PENDING 不是“不发”,而是“不负责触发”,它仍然可以被打包在其他信号的发送事务里。
- TRIGGERED(触发):只要信号被更新(即上层调用 Com_SendSignal 写入新值),就触发一次发送。这个属性最常用,适合那些“变化即发送”的控制类信号。但要注意,TRIGGERED 也有一个隐性限制:它在每个周期内最多触发一次发送,具体周期由发送模式的时间参数控制。
- TRIGGERED_ON_CHANGE(变化触发):信号的值相对于上一次发送的值发生变化时,才触发发送;如果值没变,即使反复写入,也不会触发。适合传感器数据、状态机信号这类“变化才有意义”的数据。我刚做项目时容易把 TRIGGERED 和 TRIGGERED_ON_CHANGE 搞混,实际区别就在“值变没变”这一层。
- TRIGGERED_WITHOUT_REPETITION(无重复触发):配置在支持 TMS 切换的信号组里。当 TMS 从 0 变为 1、或者从 1 变为 0 时,触发一次发送,但之后不再重复触发,直到 TMS 再次翻转。这个属性适合那些只关心状态切换瞬间的报文,比如“进入故障模式”“退出故障模式”这类事件。
3.2 属性与模式的组合:常见的工程配置
把 Transmission Mode 和 Transfer Property 组合起来看,才是真正能直接抄作业的配置方式。整理几个我实际工程里用过的组合:
| 应用场景 | Transmission Mode | Transfer Property | 说明 |
|---|---|---|---|
| 周期性状态报文 | MIXED | TRIGGERED | TMS 为 0 时按低路周期发,适合常规状态信息 |
| 周期+事件混合报文 | MIXED | TRIGGERED_ON_CHANGE | TMS 为 1 时按高路事件触发,平时按低路周期保底 |
| 碰撞/下电等紧急信号 | TRUE | TRIGGERED | 无条件允许,变化即发,保证最快响应 |
| 故障标志位上报 | MIXED | TRIGGERED_WITHOUT_REPETITION | TMS 翻转时发一次,避免状态重复刷屏 |
| 预留/填充字节 | FALSE 或 NONE | PENDING | 不使用发送能力,只占位置 |
这里要特别强调一下 MIXED 模式下低路和高路的周期参数。很多工具里配置的是TransmissionModeTrueTiming 和 TransmissionModeFalseTiming,分别对应 TMS 为 1(高路)和 TMS 为 0(低路)时的发送周期。如果你把高路配成 10ms、低路配成 100ms,那当 TMS 拉高后,报文发送频率会突然从 100ms 跳到 10ms,这个变化接收端是能直接感受到的——比如接收端超时监控用的超时阈值必须能适应最大周期,否则就会误报超时。
3.3 周期性发送中的时间基准:Repetition Period
Transfer Property 本身不直接定义周期,真正的周期参数是Repetition Period(重复周期),它在 COM 信号组的发送模式下配置,以秒为单位。底层实现中,COM 模块会维护一个软件定时器,每个周期检查一遍是否有信号满足触发条件,如果有,就生成对应的发送请求。
以 20ms 周期发送为例:COM 内部每 20ms 产生一次“心跳”,此时检查该信号组内所有信号的 Transfer Property。如果某个信号配置的是 TRIGGERED,那么不带额外条件,直接触发发送;如果配置的是 TRIGGERED_ON_CHANGE,则还要比较当前值与上一周期发送值是否不同,不同才触发;如果配置的是 PENDING,则无条件跳过。
所以你会看到一种现象:一个周期 20ms 的信号组,如果里面只有 TRIGGERED_ON_CHANGE 信号且值一直没变,那么报文实际发送间隔可能远超 20ms。接收端如果按照 20ms 去监控超时,就很容易误报。解决方法是:要么在信号组里额外加一个 TRIGGERED 属性的心跳计数器信号,保证至少每周期发一次;要么把接收端超时阈值放宽到几倍周期长度。
4. 实际项目中的配置流程与实操建议
4.1 一个典型通信矩阵的配置全过程
假设我们现在要做一块 VCU(整车控制器),需要向外发送一个状态报文,包含车速、电机转速、整车状态三个信号。整包报文周期要求 20ms,整车状态只在状态切换时立即发送。这个需求怎么落地?
第一步,确认通信矩阵。在 DBC 或 ARXML 里,所有信号都已经分配好起始位、长度、精度和偏移量。映射到 AUTOSAR 工具中,就是创建三个 ISignal:VehicleSpeed、MotorSpeed、VehicleStatus。这一步没有太多玄学,关键是起始位和长度别对错。
第二步,创建 PDU。把三个信号挂到同一个 PDU 下,配置好 DLC(数据长度)和报文 ID。PDU 的 DLC 必须能容纳所有信号,比如三个信号都占 8 bit,那 DLC 至少是 3,一般实际工程会按 8 字节对齐,多出来的字节补 0 或者填充。
第三步,创建 Signal Group。把三个信号放进同一个组,为什么要这样做?因为车速和电机转速是周期性发送的,而整车状态是事件触发,我们希望“整车状态变了就立刻把整包发出去”,而不是单独发一个只含状态字节的半截报文。信号组保证了“要么整包一起发,要么整包都不发”。
第四步,配置 Transmission Mode。选择 MIXED 模式,设置两个时间参数:TMS 为 0 时,低路周期 20ms;TMS 为 1 时,高路周期也设 20ms,但额外允许事件触发。TME 初始值设 1,确保上电后报文能正常周期发出。
第五步,配置 Transfer Property。VehicleSpeed 用 TRIGGERED,MotorSpeed 用 TRIGGERED,VehicleStatus 用 TRIGGERED_WITHOUT_REPETITION。TMS 信号在上层代码中用一个 Com_SendSignal 接口写入,业务逻辑里当状态机切换时把 TMS 从 0 写成 1,COM 检测到 TMS 翻转后,就会触发一次整包发送(因为 VehicleStatus 配置了 TRIGGERED_WITHOUT_REPETITION)。发送完成后,TMS 即使保持在 1,也不会再重复触发,直到下次从 1 切回 0 再切回 1。
这里有一个容易忽略的细节:TRIGGERED_WITHOUT_REPETITION 只在 TMS 翻转的边沿触发,而车规级状态机通常还会周期性地上报当前状态作为心跳。所以实际工程里,VehicleStatus 往往会同时存在另一个周期性状态位,或者 TMS 翻转触发和周期触发配合工作,避免接收端长时间收不到状态变化帧。
4.2 工具配置中的关键参数对照
不同厂商的工具界面不同,但底层参数名大同小异。我列几个常用参数和它们对应的含义,方便大家在配置界面里快速找到:
| 参数名 | 配置位置 | 含义 |
|---|---|---|
| TransmissionMode | Signal Group 属性 | 选择 TRUE/FALSE/MIXED/NONE |
| TransmissionModeSelect | Signal Group 属性 | 指定用作 TMS 的信号 |
| TransmissionModeEnable | Signal Group 属性 | 指定用作 TME 的信号 |
| TransmissionModeTrueTiming | Signal Group 属性 | TMS 为 1 时的发送周期 |
| TransmissionModeFalseTiming | Signal Group 属性 | TMS 为 0 时的发送周期 |
| TransferProperty | ISignal 属性 | 选择 PENDING/TRIGGERED/TRIGGERED_ON_CHANGE/TRIGGERED_WITHOUT_REPETITION |
| RepetitionPeriod | Signal Group 属性 | 低路/高路下的重复发送周期 |
工具界面里这些参数可能分布在多个子页面中,尤其是 TMS 和 TME 信号的选择,要在“Signal Group”页面里专门指定。选 TMS 信号时要注意,它本身必须是这个信号组里的成员信号,而且通常是 1 bit 的布尔信号。如果 TMS 信号没在组里,工具会直接报错或者生成代码时忽略这个配置,导致运行时行为完全不符合预期。
4.3 初始化顺序对发送行为的影响
COM 模块的初始化顺序也会影响发送行为。系统上电后,COM 模块先调用 Com_Init,然后上层应用才开始调用 Com_SendSignal 写入信号值。在这之间,TMS 和 TME 的初始值从配置中加载。如果你把 TME 初始值配成 0,那么上电后即使 TMS 已经为 1,报文也不会发送。很多“上电后第一帧报文丢失”的故障,就是 TME 初始化时序晚于上层应用第一次写入导致的。
解决思路有两种:一种是把 TME 初始值配成 1,让发送使能条件从一开始就满足;另一种是在上层应用初始化流程里,先调用 COM 的使能接口,再开始写入信号数据。具体选哪种,取决于项目对“首帧报文是否必须在特定时刻发出”的要求。如果是整车控制器这种对第一帧报文有严格时序要求的节点,我建议采用第一种方案,并把 COM 初始化放在任务调度的最早期。
5. 不同 Transfer Property 的代码表现与底层行为
5.1 从 Com_SendSignal 到 PDU 发送的路径
在 AUTOSAR COM 架构中,上层应用调用Com_SendSignal接口写入信号值,COM 模块把这个值暂存到内部缓冲区,然后根据信号组的发送模式判断是否触发发送。如果是 TRIGGERED 属性,写入动作本身就会触发“发送请求”;如果是 TRIGGERED_ON_CHANGE,还需要比较新旧值。
这个过程中有一个重要的中间角色叫COM 发送指示(ComTxModeTrue/Fasle)。它决定发送请求如何传递给下层 PDU Router。比如在 CAN 通信栈中,COM 会调用 PduR_ComTransmit,最终由 CanIf 和 Can 驱动把报文发到总线上。这一整条路径中,任何一个环节阻塞都会导致报文发不出去。实际调试中,如果 COM 配置看起来没问题但报文仍然不发,建议用 CAN 上位机抓一下底层 Can_Write 是否被调用,逐层定位。
5.2 一个典型的调试案例
我调试过一辆纯电平台的 VCU,出现了一个很奇怪的现象:BMS 的 SOC 信号在仪表上显示“跳变”,一会 80%、一会 0%,完全不符合实际电量变化。
排查过程是这样的:先看 DBC 配置,SOC 信号从 BMS CAN 报文里解出来,报文的周期是 100ms,按理说不会出现跳变。再用 CAN 分析仪抓取 BMS 原始报文,发现 SOC 信号在物理总线上确实存在跳变,说明问题出在 BMS 发送端。打开 BMS 的 COM 配置,发现 SOC 信号给了 TRIGGERED_ON_CHANGE 属性,而 BMS 上层软件每个循环周期都在调用 Com_SendSignal 写入 SOC——即使值没变。按道理说值没变不会触发发送,但问题出在 BMS 里 SOC 的物理值到信号值之间有一个换算过程,浮点转整型后,低位反复在“0”和“1”之间抖振,导致每次写入的值其实都在变,所以触发了大量发送。总线上的报文多了,数据采样窗口互相叠加,接收端看起来就像跳变。
这个案例说明两件事:一是 TRIGGERED_ON_CHANGE 对“值变化”的判断是看物理信号发送缓冲区的内容,而不是看应用层逻辑值;二是浮点转定标过程中的舍入误差会让信号值频繁抖动,配置 TRIGGERED_ON_CHANGE 之前最好先做“死区”处理,比如变化量超过某个阈值才更新信号。
5.3 周期信号和事件信号共存的注意事项
实际控制器里,一个 PDU 往往同时包含周期信号和事件信号。比如整车状态报文,既包含固定周期刷新的母线电压,又包含故障等级这种一旦变化就要立刻上报的信号。这种场景下,信号组设计要特别注意一点:信号组的发送周期以最慢信号为准,还是以最快信号为准,取决于你怎么配置。
如果以事件信号为主,把整个信号组配成 TRIGGERED_ON_CHANGE,那周期性信号的周期特性就消失了,母线电压只能跟着故障等级的变化来发送,接收端长时间收不到电压数据,很容易触发超时报警。如果以周期信号为主,把信号组配成 TRIGGERED,那事件信号的“立即发送”要求可能被周期节拍拉长到下一个周期,延迟变大。
工程上的折中做法是:把周期信号和事件信号分成两个 PDU,各自独立配置。周期 PDU 保持固定周期发送,事件 PDU 保持事件触发。如果硬件资源紧张、PDU 数量有限,再考虑把事件信号合并到周期 PDU 里,但必须接受事件响应延迟等于发送周期的现实。没有十全十美的方案,只有适合项目需求的取舍。
6. 常见问题排查与避坑经验
6.1 报文完全不发的几类原因
根据我这些年踩过的坑,报文完全不发,通常集中在以下几个地方:
- TME 信号初始值为 0,发送使能被关闭。排查方法:在调试器里直接查看 COM 模块的 TME 信号值,如果一直是 0,改成 1 试试。
- 信号没有挂入信号组,导致 Transmission Mode 根本没有生效。工具上配置了也是白配,COM 生成代码时不会为该信号生成任何发送路径。
- TMS 信号选择错误,或者 TMS 信号未包含在信号组内,工具生成代码时把 TMS 相关逻辑删掉了,运行时 TMS 条件恒为默认值。
- 上层应用没有调用 Com_SendSignal,信号值从未被写入,即使底层配置再正确也不会触发发送。这种问题在刚联调时很常见——应用层以为 COM 模块会自动把信号发送出去,实际上 AUTOSAR 里信号发送必须由应用层调接口触发。
排查建议:先确认 TME、TMS 的状态位,再确认应用层是否有写入动作,最后确认信号组的周期是否真的在运行。这三个点串起来一查,绝大多数“完全不发”的问题都能定位。
6.2 报文发送频率过高或过低怎么调
发送频率过高,通常是因为把事件信号和周期信号混在同一个信号组里,而事件信号被频繁触发。一个隐蔽的场景是:应用层每个循环周期都在调用 Com_SendSignal,但是信号值其实没有变化。如果配置的是 TRIGGERED,那么每次调用都会触发一次发送,总线负载直接爆表。这时候要么改成 TRIGGERED_ON_CHANGE,要么在应用层加变化检测。
发送频率过低,最常见的原因是信号组周期配得太长。比如你想要 20ms 发送,结果在工具里把周期配成了 0.2,少写一个小数点,总线表现就是发送频率骤降十倍。这类问题靠肉眼很难发现,最好在实车测试前用静态检查工具对比通信矩阵里的周期要求和 ARXML 配置,值不一致就报警。
6.3 不同 OEM 项目中的常见配置差异
不同整车厂对 COM 配置的规范要求差异很大。有些 OEM 要求所有发送信号必须挂在信号组里,不允许裸信号;有些 OEM 要求周期信号统一用 TRIGGERED,事件信号统一用 TRIGGERED_ON_CHANGE,不允许自定义 MIXED 模式;还有些 OEM 对 TMS 信号命名有固定前缀,方便做自动化检查。
这些差异意味着,换一个项目时最好的做法是先拿到该项目的通信规范和配置模板,别直接照搬上一个项目的经验。尤其是 TME 信号的初始值、TMS 信号的分配方式,这两个参数几乎每个 OEM 都有自己的习惯。如果你负责的模块要同时适配多个 OEM 平台,建议在设计阶段就把这些差异参数全部做成可配置项,不要硬编码在代码里,否则每次适配都要动代码,风险很高。
6.4 一个值得养成的配置自检习惯
我每次完成 COM 配置后,都会做三件事:
第一,核对通信矩阵,逐信号确认起始位、长度、周期、发送属性是否和矩阵一致。这一步能防住 60% 以上的低级错误。
第二,生成代码后,人工检查一次 Com_Cfg.c 里信号组相关的配置结构体,确认 TMS 信号、TME 信号、周期参数的数值和配置界面一致。
第三,在 HIL 台架上做一个最小测试用例:用脚本周期写入信号值,同时抓取 CAN 总线上对应报文的实际发送周期和内容,和预期做比对。这一步能暴露前两步查不出的行为级问题,比如 TME 初始值导致的首帧延迟。
这三件事看起来简单,但坚持做,能省下后面联调和路试阶段的大量排障时间。
7. 写在最后的个人体会
做 AUTOSAR 通信配置这件事,很多时候问题不是“不懂规范”,而是“规范太多、细节太碎”。Transmission Mode 和 Transfer Property 只是 COM 模块里的两个知识点,但把这两个点真正吃透,整个发送逻辑的骨架就立起来了。我个人在带新人时,通常会让他们先自己做一个小报文的手动配置,从无到有走一遍流程,再故意埋两个雷让他去查——比如把 TME 初始值改成 0,或者把 TRIGGERED_ON_CHANGE 和 TRIGGERED 混用。查过一次之后,对这两个概念的理解深度完全不一样。
如果你的项目还处在早期选型阶段,我建议多花一点时间把通信矩阵和 COM 配置的映射关系整理清楚,形成一份项目内部的自查清单。这份清单越细越好,最好细化到“每个信号属于哪个信号组、TMS 用哪个信号、TME 初始值是多少、Transfer Property 是什么”。等做到第二个、第三个项目时,这份清单的价值会越来越大。技术文档不会帮你避开所有坑,但一份来自实际项目经验的清单可以。