拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AUTOSAR诊断:从DTC到DEM,解析故障码生命周期与配置实践

AUTOSAR诊断:从DTC到DEM,解析故障码生命周期与配置实践

那天售后反馈过来说客户那台车“发动机故障灯”亮着但不抖动也不跛行,诊断仪一读,出现一个历史故障码P0171。我盯着那个confirmed bit和failed since last clear bit同时存在的状态发了会儿呆——这个场景我见过太多次了。很多刚接触AUTOSAR诊断的同学会把DTC理解成“一个故障码”,但实际上在AUTOSAR的软件架构里,DTC只是诊断事件管理(Diagnostic Event Management,DEM)对外呈现的一张“名片”,它背后的Event(监控事件)、状态位、去抖策略、老化机制才是你真正需要关心的东西。这篇文章基于我参与过的几款域控制器和动力总成项目的DEM开发经验,把DTC从监控到上报再到被清除的完整生命周期拆开讲一遍,适合刚接手诊断开发、正准备用DaVinci Configurator配置DEM或者苦于搞不清DTC状态字节的工程师参考。

1. 写在故障发生之前:DEM到底管的是哪一段“人生”

1.1 事件(Event)与 DTC:两套容易被混为一谈的概念

我接触过的不少开发者都有一个思维惯性:看到诊断需求文档里写“电压过低报U0100”,就立刻去找工具里怎么配一个叫“U0100”的DTC。这个出发点没错,但AUTOSAR的DEM工作方式并不是“DTC数据库”,而是“事件处理器”。一个完整的诊断监控项通常由三样东西组成:监控逻辑、Event ID、DTC编号。监控逻辑你写在SWC或者CDD里,它负责判断“当前这个物理量是否处于故障状态”;判断结果会被调用接口通知给DEM,这个接口就是Dem_SetEventStatus;而你通知DEM的时候,带过去的是一个Event ID,DEM再根据内部配置找到与之绑定的DTC。

也就是说,DEM本身不关心你的故障是怎么被判定出来的,它只负责接“结果”,然后根据你配置好的状态位迁移逻辑去维护DTC状态字节。Event和DTC的关系是多对一还是一对一,完全取决于设计。大部分OEM习惯一对一,一个含义明确的DTC只对应一条监控事件,查起来省事。但如果你做的是UDS 19服务按状态掩码过滤读取,多个Event映射到同一个DTC的情况也能用,只是后续老化计数和确认逻辑都会互相牵扯,不推荐新手上来就这么干。

1.2 DEM 管不到的部分:从检测到上报的职责边界

还有一层分工经常被忽略:DEM负责DTC状态的记录与更新,但“DTC用什么格式发给诊断仪”“按哪个子功能响应0x19请求”这些事情归DCM(Diagnostic Communication Manager,诊断通信管理)管。UDS报文进来之后,DCM解析、调DEM查询结果、组装响应报文。换句话说,DEM是一套状态机加存储机制,DCM是它面向诊断仪的门面。

如果你做过实际项目就会发现,当诊断仪读到一个DTC的时候,画面上出现的状态字节其实是DEM通过DCM转发出去的。这也解释了为什么你在配置工具里既能看到Dem模块的参数,也会看到Dcm模块里关于19、14服务的服务配置,两者必须配合使用。

2. 实时监控阶段:故障从出现到“转正”的甄别过程

2.1 一次非法值从何而来:监控器的触发路径

先看一个最常规的场景:你监控的是电池电压,SWC周期性读ADC,发现电压掉到了3.0V,低于你设定的阈值3.2V。这是不是就要立刻把DTC状态字节写成failed?如果真这么干,诊断仪上会多出一堆因为电瓶拆装、冷启动瞬间压降产生的“幽灵故障码”。所以DEM的前置——或者说你的监控逻辑里——必须有一套确认机制。

最常见的设计是:监控结果只在“监控条件满足”的前提下才有效。比如车速高于某值才允许做某个DTC的测试,发动机运行时间超过多少秒才开始监控,或者某个使能条件为真时才能上报。这个“前提”在AUTOSAR里通常用FIM/FID来管(后面第4节细说),而在SWC侧也可以写成纯逻辑判断。我的经验是:使能条件不要全堆在SWC里,尽量把“该不该测”的判断留给FID,把“是不是故障”的判断留给监控器,这样以后OEM要求调整某个DTC的测试前提时,你只需要动配置,不用改代码。

2.2 去抖(Debouncing)策略:把瞬态故障挡在门外

当监控器确认了一次“本次测量失败”后,怎么决定它到底是不是一个需要记录的故障呢?这就是DEM的去抖逻辑。

AUTOSAR DEM的去抖方法在配置里有两类常见思路:直接去抖和时间去抖。直接去抖其实就是一个计数器,每次事件状态为FAILED时加一,每次为PASSED时减一,直到计数达到阈值才确认故障。时间去抖则是要求故障状态持续一定时间,比如连续2秒内始终处于failed状态才确认。我见过最常用的工程做法是:计数器式的快速确认加一次“稳定窗口”。比如电池电压这个监控项,Fail阈值设为-2、Pass阈值设为+2、计数上下限在配置里写清楚,连续两次采集都低于门槛就确认故障。

DEM的配置参数里会用Debouncing相关字段表示,例如DemEventDebouncing...或者生成后出现在DemEventParameter里。这里需要注意:去抖计数和老化计数是两套东西。去抖决定“这个DTC当前是不是active”,老化决定“之前确认过的故障可不可以被清除”。很多刚入行的同事会在试车时发现故障码明明已经不报了但还留在confirmed状态里,就是因为去抖已经翻回去了,但老化计数还没跑完。

2.3 事件状态与快照/扩展数据记录的联动

确认故障之后,DEM还会干一件重要的事:记录快照数据(Snapshot)和扩展数据(Extended Data)。快照就是某个时刻的环境值,比如发生故障那一刻的电压、水温、里程。扩展数据往往是DTC的处理次数、老化计数器之类的内部信息。

这两个数据不是自动全部记录的,需要你在配置里把Event所关联的Snapshot编号、位数、数据源都定义好。最常见的坑是:你配了Snapshot,但对应的DID(数据标识)没在Dcm里映射好,或者SWC没把快照值写进端口,结果19 02子功能读快照时全是0。我每次集成时都会把快照的读取单独做成一条自测用例,保证“故障触发那一刻的数据留存”真能跑通,而不是只在配置界面里看上去很完整。

3. DTC 状态字节:八个位决定诊断仪的显示结果

3.1 状态位表:每一位都对应一个明确语义

UDS诊断规范里,每个DTC后跟一个字节的状态掩码,这8个bit就是DEM向诊断仪呈现的“最终判决”。它们的定义如下:

Bit术语含义
0testFailed当前测试认为该DTC处于失败状态
1testFailedThisOperationCycle本操作周期内测试失败过
2pendingDTC待确认故障(本次失败,但还不够“确认”条件)
3confirmedDTC已确认故障(满足确认条件,需要被记录/显示)
4testNotCompletedSinceLastClear自上次清除以来测试尚未完成过
5testFailedSinceLastClear自上次清除以来测试失败过
6testNotCompletedThisOperationCycle本操作周期测试未完成
7warningIndicatorRequested请求点亮故障指示灯的位(部分场景用)

这个表看起来枯燥,但你只要记住一条经验就能理清:bit0、bit2、bit3是“时序上的三兄弟”。bit0代表刚刚发生了一次失败;bit2是介于“刚失败”和“已确认”之间的状态;bit3一旦置位,就意味着这个故障在存储里已经“定居”了,需要走清除或老化流程才能去掉。

3.2 状态翻转流程:pendingDTC 与 confirmedDTC 的分工

用一段串行逻辑描述DTC的“转正”过程:监控器上报FAILED后,DEM先把bit0和bit1置1,同时bit2(pending)也会置位;如果故障条件持续存在并达到了你配置的确认计数(Debouncing到达确认阈值),bit3(confirmed)置1。到此为止,诊断仪用19 02按状态掩码过滤时就能看到这个DTC了。

反过来,当监控器上报PASSED,bit0清零,去抖计数逐渐衰减;如果这个操作周期内一直没再失败,bit1、bit6这些周期相关位也会被清除。但bit3不会立即消失,它会一直保留,直到完成规定的老化循环。所谓老化(Aging),通常是这样:为一个已确认的DTC配置老化循环次数,比如“连续10个驾驶循环测试未失败则清除DTC记录”。每完成一个合格的操作循环,老化计数减1,减到0时DEM清除该DTC的存储记录。

我在实际项目里经常发现一个现象:开发阶段大家都盯着bit0和bit3看,容易忽略bit5(自上次清除以来测试失败过)。有些OEM验收时会要求“清除DTC后再复测,如果故障再现,bit5和bit3要一起亮”,这其实是用来确认“清除动作真的生效过”的关键证据。

4. 该不该报、何时报,FIM 和 FID 说了算

4.1 功能抑制(FIM)解决什么问题

DEM的监控逻辑本身很“单纯”,你喂给它什么状态它就记录什么状态。但整车环境下,同一个物理量在不同的条件下,其故障判定要求是完全不同的。比如车速信号在ABS/ESP没有完成初始化之前出现异常,可能只是因为软件启动时序;再比如发动机在拖车启动工况下,转速和油压数值都不是常规意义上的“正常值”,如果监控器照常判定故障,就会报出一堆假DTC。

这时候就需要一个前置的“权限判断”机制:FIM(Function Inhibition Management)和FID(Function Identifier)。FID本质上是一组全局的许可标志,每个FID代表一个具体的功能条件,比如“整车动力模式正常”“传感器供电稳定”“诊断仪请求了DTC设置控制关闭”。FIM则拿这些FID的当前值去判断:当前能不能让某个监控事件去调Dem_SetEventStatus。

4.2 FID 的配置逻辑:故障事件也讲“前提条件”

举个实际配置例子:你要抑制“网络通信故障”这个DTC在车辆下线前的报出,那就可以定义一个FID,名字叫“允许通信安全监控”,只有在PDI模式或下线检测通过后才置TRUE。FIM的配置会把这个FID关联到具体Event上,并且在DEM事件使能条件中指明“该事件只有在FID为TRUE时才能被更新”。

因为FIM/FID在AUTOSAR的ECUC参数里并没有一套绝对统一的模板,很多OEM都会在SWC里实现一套自己的抑制逻辑,再通过RTE调用DEM接口。我的建议是:不要为了“实现简单”而把抑制逻辑全部硬编码到监控SWC里,尽量把每个FID的定义、使用范围、置位时机整理成一张配置表,因为量产之后最常发生的诊断改动,就是“某个DTC在某个特殊工况下不该报,但之前没做抑制条件”。

5. 配置和集成的落地细节:基于 DaVinci Configurator 的实操笔记

5.1 EcucDem 参数的核心项

到了具体配置阶段,以DaVinci Configurator为例,你首先要在BSW模块列表里确认Dem模块被启用,并挂到正确的EcucPartition上。打开ECUC参数后,至少要关注这几个块:DemGeneral、DemDtcTable、DemEventTable、DemOperationCycleStorage。

DemDtcTable里是DTC编号与DTC状态位映射的定义,比如DemDtcNumber、DemDtcStatusBitMask、DemDtcStatusConfirmedBitPosition等。如果你在配置里把确认位设成bit3,那么所有该DTC的状态字节都会按bit3来解析。这里有个很坑的细节:不同OEM给的状态字节宏定义可能包含整个字节的掩码,你千万别直接用0xFF去更新状态字节,不然会把该保留的历史信息全部覆盖掉。正确做法是先用掩码读出,再在对应位上做 | 和 & 操作。

5.2 事件 ID 与 RTE 的接线方式

SWC侧惯用的集成方式是:监控SWC的OutputPort发送一个布尔量或枚举量,通过RTE映射到DEM模块的接口;也可以直接在代码里调用Dem_SetEventStatus。我个人偏好后一种,因为可读性好,而且方便在调试的时候打断点。

#include "Dem.h" void Monitor_BatteryVoltage(float voltage) { if (voltage < 3.2f) { Dem_SetEventStatus(DemConf_DemEvent_BatteryVoltageBelowThreshold, DEM_EVENT_STATUS_FAILED); } else { Dem_SetEventStatus(DemConf_DemEvent_BatteryVoltageBelowThreshold, DEM_EVENT_STATUS_PASSED); } }

如果还要存储快照数据,那就用带快照参数的变体接口,比如Dem_SetEventStatusWithSnapshotData(DemConf_DemEvent_BatteryVoltageBelowThreshold, DEM_EVENT_STATUS_FAILED, DemConf_DemEvent_BatteryVoltageBelowThreshold_SnapshotId, &snapshotData)。快照的数据格式必须在配置里提前指定,否则接口会拒绝写入。

5.3 集成时的常见坑与验证手法

下面这些“坑”我基本每次新项目都能遇到,列出来给大家排雷:

  • Event ID和DTC编号没对应上,导致A事件把B的DTC状态刷新了。这类问题排查起来比较费劲,最好在配置生成后就做一次“EventId到DTC映射表”的核对,把工具生成的Dem_EventId文件抓出来对照诊断矩阵表过一遍。
  • 配置了DTC但NVRAM块没分配好。DEM要把confirmed的DTC和老化计数器存储下来,如果NVRAM的Block大小不够或者地址重叠,下电后再上电故障码就丢了。
  • 忘在周期任务里调用Dem_MainFunction。DEM不是纯事件触发的,它的循环处理和老化计数推进依赖主函数周期调用。如果你是手写代码集成,很容易漏掉这一条。
  • 操作循环类型没定义准确。Operation Cycle是DEM判断“本操作周期”和“老化”的时间基准,必须和整车定义的电气循环、驾驶循环对齐。例如你定义了“点火循环”,但ECU在动力模式下不断电,导致老化计数一直不动,故障码永远清不掉。
  • 19服务的子功能和状态掩码不匹配。比如DCM侧只实现了19 01,没实现19 02,但DEM里的快照配置齐全,那么诊断仪查快照时就会得到不支持响应。

我的自测手法很简单:在台架上把实际监控条件制造出来,按顺序记录DTC状态字节能从0x00翻转到0x05(bit0+bit2)再到0x09(bit0+bit3),然后用14服务清除,确认状态字节恢复初始值。跑通这样一条链路,基本可以确定DEM核心逻辑没大问题。

6. 诊断仪侧的“一生”:19/14/85/28 服务如何配合 DEM

6.1 19 服务读 DTC 的子功能怎么选

UDS的0x19服务有若干子功能,最常用的几个和DEM的关系是这样:

  • 19 01:按状态掩码报告匹配的DTC。客户端可以传一个状态掩码,比如只查bit3(confirmed),那么DEM会返回所有已确认故障。如果你的诊断仪界面显示“当前故障”和“历史故障”,大部分后端就是19 01配合不同掩码实现的。
  • 19 02:按状态掩码报告DTC快照。适合故障发生瞬间的环境数据回放,需要DEM已经配置好Snapshot并且数据来源完整。
  • 19 04:报告已确认的DTC,相当于19 01的简化版。
  • 19 06:报告支持的DTC,严格说这更多是DCM的配置,但数据来源仍然是DEM里的DTC表。

做一个项目时,千万要先确认诊断仪究竟用哪个子功能。不少国产诊断仪为了通用性会直接用19 02去翻快照,结果你只测了19 01,C客户现场报“读不到快照”,返工成本很高。

6.2 清除与冻结:14 服务、85 服务的真实作用

0x14服务是清除DTC,它的触发逻辑是:DCM收到14服务请求后,调用DEM的数据清除接口,把状态字节清零、清除快照和扩展数据、重置老化计数。注意,14服务不是“关闭监控”,只是清空已存记录,如果故障还存在,新一次监控照样会把状态位重新置上。

0x85服务(DTC设置控制)作用正好是“控制记录动作”。它把DEM置于“记录开启”或“记录关闭”的状态。下线检测时经常用85服务把驱动相关的DTC记录关掉,防止误报污染诊断结果。我在配置时特别提醒:85是作用于“DTC设置”的开关,跟0x28(通信控制)不是一回事,别把两个服务的作用搞混。

下线测试还有一个常见顺序:先发85关记录,再做该工况的测试,结束后先发85开记录,再发14清一下可能产生的新状态,保证车辆交付时诊断记录干净。

6.3 28 服务与诊断事件上报的联动

0x28服务是用来控制通信行为的,比如抑制特定报文的发送、禁止接收、允许收发等。很多人困惑“28服务会不会影响DTC监控”,我的理解是:28服务配置的是网络通信通道,不是DEM的记录状态。如果你用28服务把某条CAN报文抑制了,而DTC的确认条件恰好依赖这条报文的有效数据,那确实会间接影响监控结果,但那是因为数据没传到,不是DEM被关闭了。

所以在做DTC上报联调时,我一般会同时打开三处观察:DEM事件状态、DCM响应的状态字节、总线上对应的诊断报文ID。如果DTC明明在DEM里是confirmed,但诊断仪读不到,问题大概率出在DCM的19服务路由或者28服务的通信抑制上,而不是DEM模块本身。

我在这个项目里最大的感受是:诊断开发看上去是在配置工具,实际上是在不断地“翻译”——把客户的现象翻译成状态字节,把状态字节翻译成配置项,再把配置项翻译成代码逻辑。DTC的一生从需求文档里的一个编号开始,在DEM的状态机里经历失败、确认、老化、清除。把这套流程想透了,之后不管是用DaVinci Configurator还是手写配置,你都不会再被“故障码读不出来”这种问题卡太久。

返回列表