AUTOSAR 中的“通信管家”与“诊断门卫”:COM 与 DCM 是如何配合工作的?
你有没有想过,当维修技师把一个 OBD 诊断仪插进你的车里,几秒钟就能读出发动机转速、电池电压、故障码时,这些数据到底是从哪儿来的?是谁在背后调度这一切?
在现代汽车的 ECU(电子控制单元)中,这背后有一套精密协作的软件体系在运行。其中两个关键角色就是COM 模块和DCM 模块—— 它们就像一对默契搭档:一个管“日常通信”,另一个管“对外接待”。理解它们怎么合作,是掌握 AUTOSAR 软件架构的核心钥匙。
为什么需要 COM 和 DCM?先说清楚背景
随着汽车越来越智能,一辆车里的 ECU 数量动辄几十个,每个都在处理大量信号:车速、油温、刹车状态……如果让应用软件直接去操作 CAN 报文、编码字节,那代码会变得极其臃肿且难以维护。
于是,AUTOSAR 提出了分层设计思想——把复杂问题拆解。
在这个架构下:
- COM 模块成了“通信调度员”:它负责统一管理所有应用层的数据发送和接收,屏蔽底层总线细节;
- DCM 模块则是“诊断门卫”:专门应对来自外部设备的诊断请求,比如读数据、写参数、刷程序。
两者分工明确,但又必须紧密协作。因为很多诊断数据,其实就来源于正常通信过程中产生的信号。
COM 模块:应用数据的“交通指挥中心”
它到底做什么?
想象一下早高峰的城市道路,成千上万辆车要上路。如果没有红绿灯和交警,交通就会瘫痪。COM 模块干的就是这个活儿 —— 给每一个信号安排何时发、怎么发、发到哪。
它位于应用软件组件(SWC)和底层通信栈(如 CanIf、PduR)之间,作用如下:
- 接收应用层传来的信号(例如
vehicleSpeed = 60.5 km/h) - 根据配置决定是否发送(比如只在变化超过 1km/h 时才发)
- 把多个小信号打包进一个 CAN 报文(提高总线利用率)
- 在接收端拆包并通知对应的应用
整个过程对开发者透明,你只需要调用一句Com_SendSignal(),剩下的都由配置工具自动生成代码完成。
关键能力一览
| 特性 | 说明 |
|---|---|
| 信号抽象化 | 支持布尔、整型、浮点等类型,无需手动位操作 |
| 多路复用(Multiplexing) | 多个信号共享同一 PDU,节省带宽 |
| 灵活触发机制 | 可设为周期发送、事件驱动或混合模式 |
| 过滤与阈值控制 | 避免无意义更新,降低总线负载 |
| 信号组同步 | 确保相关信号(如 XYZ 三轴加速度)同时更新 |
举个例子:发车速信号有多简单?
Std_ReturnType sendVehicleSpeed(float speed) { uint16 encodedValue = (uint16)(speed * 10); // 单位转换:0.1km/h return Com_SendSignal(COM_SIGNAL_ID_VehicleSpeed, &encodedValue); }就这么一行函数调用,背后可能涉及:
- 查找该信号属于哪个 CAN 报文(I-PDU)
- 判断是否满足发送条件(如周期到了 or 值变了)
- 将其写入缓冲区,等待 PduR 路由到底层驱动
好处显而易见:应用逻辑不再关心 CAN ID、字节序、信号位置,移植性和可维护性大大增强。
DCM 模块:诊断世界的“前台接待员”
它的角色定位
当你用诊断仪扫描车辆时,发出的是标准 UDS 协议命令(如22 F1 90表示“读取电池电压”)。谁来听?谁来回应?答案就是DCM 模块。
它是 ECU 的“诊断入口”,依据 ISO 14229 标准工作,主要职责包括:
- 接收诊断请求 PDU
- 解析服务 ID(如 0x22 读数据,0x2E 写数据)
- 执行具体操作(可能是读传感器、改参数、启动例程)
- 构造响应报文返回给客户端
但它本身不干活,更像是一个“项目经理”——自己不动手,而是协调其他模块完成任务。
典型工作流程
- 诊断仪发送
02 22 F1 90 - CanTp 层重组后交给 PduR
- PduR 转发给 DCM
- DCM 解析出 DID = F190
- 查表找到对应的处理函数
GetBatteryVoltage() - 调用函数获取实时数据
- 编码成两字节,构造响应
06 62 F1 90 XX XX - 通过 CanTp 发送回去
整个过程毫秒级完成。
核心特性亮点
- ✅ 支持标准 UDS 服务(0x10~0x3E)
- ✅ 多种会话模式(默认、扩展、编程)
- ✅ 安全访问机制(Seed-Key 认证防篡改)
- ✅ 动态数据生成(支持回调函数)
- ✅ 多通道支持(CAN/LIN/Ethernet)
实战代码:注册一个诊断读取函数
static Std_ReturnType GetBatteryVoltage(uint8* data) { float voltage = get_analog_battery_voltage(); uint16 encoded = (uint16)(voltage * 100); // 转换为 mV×100 data[0] = (encoded >> 8) & 0xFF; data[1] = encoded & 0xFF; return E_OK; } // 配置项(通常由配置工具生成) const Dcm_DidInfoType Dcm_DidInfo[] = { [DCM_DID_BATTERY_VOLTAGE] = { .DidReadFnc = GetBatteryVoltage, .DidSize = 2, .SessionMask = DCM_DEFAULT_SESSION | DCM_EXTENDED_DIAGNOSTIC_SESSION, }, };你看,DCM 并不存储数据,而是“按需拉取”。这种设计既节省内存,又能保证返回最新值。
COM 和 DCM 如何“握手”?四种典型协作场景
虽然 COM 和 DCM 各自独立,但在实际运行中频繁互动。它们的关系不是上下级,而是通过 RTE 和 PduR 实现松耦合协同。
下面是四个最常见也最重要的交互场景:
场景一:诊断读取应用信号(DCM → COM)
“我想知道现在发动机转速是多少?”
—— 这是最常见的诊断需求。
流程如下:
- DCM 收到
22 F1 0C请求 - 查配置表发现
F10C映射到信号EngineSpeed_rpm - 调用
Com_ReceiveSignal(COM_SIGNAL_ID_EngineSpeed, &value)获取当前值 - 编码后返回响应
📌关键点:
DCM 自身并不采集转速,它依赖 COM 提供“最新鲜”的数据副本。因此必须确保:
- COM 正常接收并更新了该信号
- 信号映射关系正确(DID ↔ Signal ID)
- 使用的是同一个信号实例,避免双份缓存导致不一致
场景二:诊断写入运行参数(DCM → COM)
“我要临时修改某个 PID 控制器的比例增益。”
这类操作常见于标定调试阶段。
流程如下:
- DCM 接收
2E F1 AA 新值请求 - 验证当前是否处于扩展会话 + 已通过安全等级 3 认证
- 解码新值
- 调用
Com_SendSignal(COM_SIGNAL_ID_P_Gain, &newValue) - COM 将新值广播到总线,并通知相关 SWC
⚠️安全警告:
此类写操作必须设置权限!否则黑客可通过诊断口随意篡改关键参数,造成安全隐患。
✅ 最佳实践建议使用 FIM(Function Inhibition Manager)进行统一权限管理。
场景三:COM 触发故障上报(COM → DEM → DCM)
“检测到机油压力过低!”
—— 这类异常需要能被诊断仪读到。
虽然 COM 不直接联系 DCM,但它可以通过 DEM(Diagnostic Event Manager)间接影响诊断行为:
- COM 检测到
EngineOilPressure < threshold - 设置对应 DTC 的状态为
PRE_FAILED - DEM 上报该事件
- DCM 在后续
19 xx xx请求中可返回此故障码
💡 这种机制实现了“软故障监测”——无需单独任务轮询,降低了 CPU 开销。
场景四:DCM 控制通信行为(DCM → COM)
“我现在要刷写程序,请暂停所有非必要报文发送。”
在 OTA 或产线刷写时,为了释放总线资源,DCM 可以要求 COM 暂停部分通信:
- DCM 调用
Com_SetGlobalTxMode(COM_TXMODE_DISABLED) - COM 停止自动发送某些非关键 I-PDU
- 刷写完成后恢复
COM_TXMODE_ENABLED
🔧 更高级的做法是结合 FIM 实现功能抑制策略,实现精细化控制。
实际案例:OBD-II 发动机转速读取全过程
我们来看一个真实法规场景:OBD-II 要求支持 PID $0C(发动机转速)。
当诊断仪发送02 22 F1 0C时,发生了什么?
- 物理层接收:CAN 控制器收到帧,交由 CanIf → CanTp 重组完整 PDU
- 路由到 DCM:PduR 根据 DstAddr 判断这是诊断请求,转发给 DCM
- 解析服务:DCM 识别为
ReadDataByIdentifier,提取 DID = F10C - 查找映射:查表得知 F10C 对应
EngineSpeed_rpm信号 - 调用 COM:执行
Com_ReceiveSignal(...)获取最新值 - 格式转换:将 rpm 值除以 4,转为两个字节(UDS 规范要求)
- 构造响应:生成
06 62 F1 0C XX XX并通过 CanTp 返回
🎯价值体现:
整个过程中,DCM 完全不需要知道发动机转速是怎么来的、在哪条 CAN 上、用了几个字节。它只关心“我要读什么”,其余交给 COM 和配置系统搞定。
这就是 AUTOSAR “关注点分离”理念的最佳诠释。
设计时必须注意的五个坑
别以为只要配好就能跑通。以下是项目中最容易踩的雷区:
| 注意事项 | 说明 |
|---|---|
| 信号一致性 | DCM 读取的信号必须与应用使用的是同一份数据,否则会出现“诊断读的是旧值”的问题 |
| 时序匹配 | 若 COM 信号每 10ms 更新一次,而 DCM 请求瞬间到达,应启用 last-value 缓存机制 |
| 内存优化 | 对于极少使用的诊断专用信号(如出厂测试标志),可考虑绕过 COM,由 DCM 直接访问变量 |
| 安全性 | 所有WriteDataByIdentifier必须经过 Security Access 校验,防止非法写入 |
| 性能瓶颈 | 高频诊断请求可能导致 COM 缓冲区溢出,需做压力测试评估 |
📌 特别提醒:在量产项目中,务必对诊断路径进行端到端延迟测试,确保在最差情况下也能及时响应。
总结:它们不是对手,而是搭档
回到最初的问题:COM 和 DCM 有什么关系?
一句话总结:
COM 是数据的生产者和搬运工,DCM 是数据的消费者和服务提供者;二者通过标准化接口协作,共同构建起 ECU 的“内外双循环”通信体系。
- 正常运行时,COM 处理应用通信;
- 诊断介入时,DCM 借力 COM 获取数据;
- 异常发生时,COM 通过 DEM 影响 DCM 的输出;
- 特殊模式下,DCM 反过来调控 COM 的行为。
这种双向、松耦合的设计,正是 AUTOSAR 架构强大之处。
随着智能网联汽车的发展,远程诊断、OTA 升级、云端数据分析等新需求不断涌现。未来的 COM 与 DCM 协同还将延伸到以太网传输、DoIP 协议、UDPNM 网络管理等领域。
但万变不离其宗:搞懂 COM 与 DCM 的交互逻辑,你就掌握了打开 AUTOSAR 黑盒的第一把钥匙。
如果你正在做 autosar 软件开发,不妨问自己一句:
我写的那个Com_SendSignal,将来会不会被诊断仪读走?它的值准不准、安不安全、能不能及时更新?
想清楚这些问题,你的架构思维就已经迈上了新台阶。