ARTICLE DETAIL

资讯详情

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

AUTOSAR诊断开发:用“DTC的一生”讲透DEM模块核心机制

AUTOSAR诊断开发:用“DTC的一生”讲透DEM模块核心机制 干了多年AUTOSAR诊断开发要说哪个模块最磨人我会毫不犹豫投DEM一票。别误会不是因为DEM本身多难而是它牵扯的环节实在太多上层应用怎么报故障、底层怎么存储、UDS服务怎么读、NvM怎么写、BswM怎么切状态一个DTC从产生到老化几乎跟半个ECU都有关系。网上关于DEM的资料不少但大多停留在PPT层面翻来覆去就那几张状态机图。今天这篇文章我想换个角度用“DTC的一生”这条时间线把AUTOSAR的DEM模块从头到尾讲透。不管你是刚接触AutoSAR配置还是已经被PMC排查折磨到失眠这篇都值得你花二十分钟读完。先说清楚DEM能解决什么问题让一个诊断事件比如“传感器对地短路”从发生、确认、存储、上报到被诊断仪读取、清除整个过程有一套标准化机制来管理不靠应用层自己记标志位。适合谁来参考正在用Davinci Configurator或EB tresos做AUTOSAR集成的软件工程师、搞UDS/OBD诊断的测试工程师以及想从“会点灯”进阶到“会诊断”的嵌入式新人。1. 先搞懂DEM在整个AUTOSAR里到底站在哪个位置1.1 DEM不是一个人单打独斗DCM、NvM、BswM的职责分工很多人一开始会混淆DCM和DEM。DCMDiagnostic Communication Manager管的是“通信”解析诊断仪发过来的UDS报文比如0x19、0x14、0x22、0x2E这些服务它只管“车对外的话术”。DEMDiagnostic Event Manager管的是“事件记账”到底当前哪些故障是发生了的、哪些是被确认过的、哪些是历史存储的、它们应该显示成什么状态位这些都是DEM自己的活。我给你打个比方。DCM是前台接待负责接电话、记订单DEM是财务和仓库负责记录这批货到底有没有入库、库存是多少、还能不能查旧账。真正干脏活累活的是DEM但客户诊断仪只跟DCM对话。所以DEM本身就带了一堆对外的接口它需要跟周围的模块打交道DCM诊断仪请求服务时DCM调用DEM的接口比如Dem_GetStatusOfDTC、Dem_ClearDTC。NvM非易失存储管理DTC是否确认、老化计数器、冻结帧数据、扩展数据这些都是要掉电保存的。DEM不直接操作Flash而是通过NvM的接口来做。BswM模式管理DEM能驱动BswM做故障响应比如某个DTC确认后BswM可以降级控制策略或者进入跛行模式。反过来BswM也可以在ECU唤醒时通知DEM做初始化。应用层SWCSoftware Component它是故障信号的来源通过RTE接口调用Dem_SetEventStatus或者直接通过Port读写的方式告诉DEM“我现在检测到哪路电压异常了”。EcuMECU启动、唤醒时DEM需要跟着初始化NvM这个时序是EcuM在管。整个模块握手关系一多问题就来了集成的时候经常出现“DEM没起来、NvM还不可读、DCM已经开始记录诊断请求”这种时序冲突。这是后面排查的重灾区。1.2 “事件”才是DEM的心脏为什么AUTOSAR不直接叫故障码管理AUTOSAR里有一组容易绕晕的概念DemEvent诊断事件、DTC诊断故障码、DTCOrigin故障码来源、DiagStatus。你如果直接看SWS_DEM文档很可能被这些术语搞疯。我说一个基本结论DEM管理的最小单位是Event而不是直接管理DTC。一个Event可以对应一个DTC多个Event也可能映射到同一个DTC。打个比方DTC P0123是“节气门位置传感器电路故障”它可以由“对电源短路”“对地短路”“信号合理性错误”三个Event映射而来。诊断仪读到DTC P0123时它是被至少一个Event触发的具体触发的是哪个Event可以用扩展数据区分。作为配置者我们至少要建立三种映射关系Event → DTC编号定义这个事件最终对外显示成什么DTC码。Event → 状态位集合当前这个事件该置哪些状态位见2.2节。Event → 存储槽位当它确认故障时数据存到NvM哪个区域、冻结帧存多长。这个概念一旦想通后面读配置工具就有方向了不是配“DTC”而是配“Event”然后把两个概念绑定起来。2. 拆解DTC的一生一个故障从发生到被人遗忘2.1 故障探测应用层拿什么信号判断“坏了”故障不会凭空出现在DEM里一定是应用层SWC先检测到某个物理量异常然后把结果告诉DEM。怎么告诉最常见的是通过RTE在SWC里有一个Port连接到DemEvent应用层用Dem_SetEventStatus(EventId, EventStatus)这个函数直接更新事件状态。举个例子检测发动机水温传感器对地短路/* 应用层SWC周期任务每10ms执行 */ FUNC(void, App_10msTask)(void) { uint16_t adcValue Adc_ReadValue(SENSOR_CH); if (adcValue 5000) { /* 电压超过5V认为对电源短路 */ Dem_SetEventStatus(DemConf_DemEventParameter_EvtSensorShortToPower, DEM_EVENT_STATUS_PREFAIL); Dem_SetEventStatus(DemConf_DemEventParameter_EvtSensorShortToPower, DEM_EVENT_STATUS_FAILED); } else { Dem_SetEventStatus(DemConf_DemEventParameter_EvtSensorShortToPower, DEM_EVENT_STATUS_PASS); } }这里有几个点容易被新手误解。首先DEM_EVENT_STATUS_PREFAIL和DEM_EVENT_STATUS_FAILED的区别PREFAIL表示“进入故障的预备阈值还没到但信号已经异常”FAILED表示“达到故障判定阈值基本算确诊”。如果你自己实现了防抖逻辑可以直接传DEM_EVENT_STATUS_FAILED如果你想让DEM内部的防抖功能参与判定那就先给PREFAIL由DEM自己翻转状态。其次事件状态的更新频率很关键。一般建议跟传感器的采样周期同步异常信号你半天才扫一次故障确认时间会被拉长OBD的IURIn-Use Rate统计会受影响。实测下来10ms周期是大多数项目的“甜点”100ms也能用但诊断体验会明显迟钝。2.2 防抖与状态管理状态掩码的真正含义DTC状态掩码StatusOfDTC是UDS 0x19服务的核心也是DEM数据在诊断仪上最直观的体现。它是个8位字节每位代表一种诊断状态。标准ISO 14229-1定义如下位掩码含义什么情况下置1bit00x01testFailed当前测试判定失败bit10x02testFailedThisOperationCycle本次上电循环内测试失败过bit20x04pendingDTC待确认状态故障出现但还没满足确认条件bit30x08confirmedDTC已确认故障这是存储和显示的关键位bit40x10testNotCompletedSinceLastClear自上次清除后测试还没完成过bit50x20testFailedSinceLastClear自上次清除后测试失败过bit60x40testNotCompletedThisOperationCycle本次上电循环内测试未完成bit70x80保留/厂商自定义一般不用假如诊断仪发一个0x19 02按状态掩码读DTC请求掩码是0x20testFailedSinceLastClear那DEM就需要把所有testFailedSinceLastClear为1的DTC报出去。这就是为什么你在实车上读到的DTC状态是类似0x28这样的值——0x28等于confirmedDTC0x08加上testFailedSinceLastClear0x20意思是“这个故障确实发生过并且一直没有被清除掉”。这非常符合直觉一个故障一旦被记录为已确认在清除之前它的confirmedDTC位就该一直保持为1。那么pendingDTC呢这个位专门用来“提前预告”。防抖机制里当故障信号第一次出现但还没满足最终确认条件时可以把pendingDTC置位。比如温度超过了阈值的一次采样但防抖需要连续3次超阈值才能确诊第1次时可以先标pending。等第3次到了再置confirmed。这么做的目的是让诊断仪能够及时发现“有苗头”的故障而不必等完整防抖周期结束这对法规诊断如OBD尤其重要。状态位之间的翻转不是乱翻的DEM内部有一套标准流程一个Event从FAILED状态被Dem_SetEventStatus写入之后DEM会去检查它的防抖条件把testFailed、confirmedDTC、pendingDTC的状态位更新当故障消失状态会变成PASS然后进行老化处理。配置防抖时常用的策略有三个计数器法Counter、时间法Time、直接映射None/Immediate。计数器法每次故障信号计数器加1每次通过信号计数器减1。计数器超过阈值确认故障。典型配置阈值10单周期加1。时间法连续故障持续时间超过阈值比如150ms。适合转速、电压这类连续模拟量。直接映射应用层已自行判断DEM不需要再防抖Event一收到FAILED就置confirmed。省事但前提是应用层逻辑足够可靠。注意一点同一个Event如果既配了计数法又配了老化周期它的故障退出条件不只是故障信号消失还要老化计数器走完。这个“时间差”在实车测试中非常容易让人误判“为什么故障清不掉”。2.3 故障存储与老化为什么DTC不会永远亮着DTC一旦确认它总不能只存在RAM里吧下电就没了那还聊什么。DEM会把确认状态、老化计数器、冻结帧数据等按配置写入NvM。这里有一个概念叫“Fault Memory”故障存储器它通常被划分为Primary Memory和Secondary Memory。Primary Memory存真正的故障信息比如状态位、事件ID、老化计数器。容量小但频繁读写。Secondary Memory存扩展内容包括冻结帧Freeze Frame、扩展数据记录Extended Data。容量大但写入频率低。NvM的写入也有讲究。DEM支持直接写入Immediate和延迟写入Deferred。直接写入适用于关键DTC比如安全相关故障每次状态变化立即写Flash。延迟写入则为了减少Flash擦写次数先记在RAM等ECU下电前统一把NvM刷下去。很多项目默认是延迟写入于是你会遇到一个经典问题ECU在故障刚发生时就瞬间掉电状态还没来得及存盘DTC就丢了。解决方法有两个一是把关键事件改成Immediate存储二是确保EcuM的下电时序足够长让NvM有充分时间完成写操作。再说“老化Aging”这是个被低估的功能。已确认的DTC不是永远钉在故障码表里的只要故障不再出现经过若干个驾驶循环后它会被自动清除。这就是ISO 14229里说的“Aging”当DTC处于confirmed状态但连续多个操作循环都没有再次检测到故障老化计数器就会累加达到设定的循环阈值后confirmedDTC位清零这个DTC就不再对外报出。配置里有几个参数要格外留意DemAgingCycleThreshold老化阈值按照OBD法规通常设为40次驾驶循环DemAgingIterationCount已经老化过的循环次数。如果这两个参数在NvM里没有一个合适的初始值会出现“明明跑了足够多的循环老是不老化”的怪象。原因多半是计数器的初值被NvM恢复成了0而不是从1开始。2.4 故障清除UDS 0x14清除诊断信息的完整链路诊断仪发0x14ClearDiagnosticInformation时DCM会检查安全等级和会话模式。注意很多ECU要求0x14必须在扩展会话或编程会话下并且通过了27服务的安全解锁才能执行。DCM验证通过后才会调用到DEM的Dem_ClearDTC。Dem_ClearDTC会做三件事把指定的DTC或者全部DTC的confirmedDTC、pendingDTC、testFailedSinceLastClear这些状态位清干净清除老化计数器删除对应的冻结帧和扩展数据记录。清完之后之前存的NvM数据也会被重置。所以如果你测试时发现0x14清不掉先别怀疑DEM先查DCM那边的SecurityLevel和Session配置对不对这个坑我踩过不止一次。3. 从零配置DEM以Davinci Configurator为例的实操全过程3.1 配置前的准备SWC接口先把好别在RTE上栽跟头用Vector的Davinci Configurator的话DEM的配置无非两大块一是ECU层面的Dem模块参数二是SWC和RTE的接口关联。别一上来就闷头在Dem界面里点先把SWC想清楚。我建议的顺序是先在Davinci Developer里定义好应用层的Port明确哪个Port输出故障状态哪个Port接收DEM反馈然后再到Configurator里把DemEvent关联到这些Port上。常见的接口方案有两种一是应用层调用Dem_SetEventStatus这种服务接口二是定义专用的SenderReceiverPort周期性地把状态写进PortDEM通过RTE去读。后者在工具链上更“AUTOSAR味”但会引入RTE生成时端口方向、DataElement类型不一致的风险。避坑指南里最实用的一条如果你用Port方式注意选择正确的Runnable。我遇到过一个问题——SWC里明明写了Rte_Write_xxx生成的代码里却怎么也找不到这个函数。最后发现是Runnable没映射到对应的Port。换句话说Port只是“数据通路”真正触发写入的Runnable必须和Port绑定而且要挂到正确的周期Task上。建议每个周期Runnable里只写本周期使用的Port不要图方便把好几个逻辑塞到一个Runnable里后期调试RTE满天飞的时候会崩溃。3.2 DemEvent核心参数逐一过一遍打开Dem模块创建Event。我个人习惯把Event的命名直接写成“Evt_信号名_故障类型”比如Evt_WaterTempSensor_ShortToGround这样DTC映射、冻结帧、故障码表导出来之后一眼能看懂。每个Event需要关注的关键参数我列一下DTC编号3字节标准按ISO 14229的格式填写比如P0123对应0x0123厂商部分可能定义成0xC123。注意DTC的字节序配置工具里显示的是32位值但实际UDS报文里发的是高字节在前还是低字节在前各厂商习惯不同一定要跟诊断调查表对齐。Debounce策略选择Counter或Time并给参数。计数器法就填DebounceCounterThreshold、DebounceCounterIncrementStep、DebounceCounterDecrementStep。时间法就填DebounceTimeThreshold单位通常是毫秒。Event存储位置Primary还是Secondary是否允许用Deferred模式写入NvM。老化相关是否启用Aging阈值填多少单位是“个操作循环”还是“个时间周期”。Port关联这个Event通过哪个RTE端口跟SWC连接。OBD相关是否属于OBD监控需要给到0x19 01的服务里。这里有一个很重要的细节一个DTC可能由多个Event共享那么你在配置每个Event的DTC编号时工具会默认生成一个DTC实体。此时要留意“DemDtcId”的唯一性。曾经有项目配了两个Event引用同一个DTC号但DemDtcId被工具生成了两个直接导致0x19 02查状态时同一个DTC出现两次测试报告直接被客户打回。3.3 生成代码与集成检查配置完成后生成代码是关键一步。生成后的文件主要包括Dem_Cfg.h、Dem_Cfg.c、Dem_PBcfg.c含配置描述有时在GeneratedArtifacts目录下。先别急着编译花十分钟检查这几个点检查Dem_Cfg.c里有没有生成你需要的EventId宏比如DemConf_DemEventParameter_EvtSensorShortToPower不出意外它已经是一个枚举枚举值。检查生成的Dem_Cfg.c中DTC状态掩码相关的映射表是否正确。检查NvM的Block是否已经与DEM的存储需求关联。DEM会生成一块或多个NvM Block如果NvM里没配启动时DEM读取NvM会失败DTC状态会变成“未初始化”状态。然后编译烧录用CANoe/CDS或PCAN连上ECU发UDS 0x19 02去读。第一次能读出预期DTC基本就说明DEM的“接收-存储-上报”链路通了。但别高兴太早这只是开始后面有更多坑等着。3.4 与0x19、0x85、0x28这些“邻居”服务怎么配合DEM不是孤立存在的诊断仪最终是通过DCM的服务来操作DEM的数据。除了0x14清除我们还要看三个常见服务0x19 01/02/04按DTC状态或分组读取核心就是DEM的Dem_GetStatusOfDTC0x85ControlDTCSetting当诊断仪发送“DTCOn/Off”控制时DEM要暂时停止DTC记录。这个功能在产线标定时很有用可以避免测试过程中产生一堆干扰故障码。配置时注意0x85的On/Off状态要让DEM和DCM都知道否则会出现“关掉了还在记录”的问题。0x28CommunicationControl这个不直接归DEM管但它会影响ECU的通信和网络唤醒如果通信关掉了后续的诊断请求就发不进来了。在配置时要留意模块间的交互0x28把通信关掉后如果ECU还处于诊断会话DEM和NvM之间依然可以正常工作因为NvM不依赖通信但网络管理报文不发了也会影响外部诊断仪发现ECU的能力。AUTOSAR网络管理是另一条线。网络管理状态Network Mode、Prepare Bus-Sleep Mode决定了ECU是否保持唤醒。如果你的ECU在做DTC老化测试时网络管理状态切换太快直接进入Bus-Sleep模式那么DTC状态的更新和NvM存储都没有足够时间完成。实测中很多“DTC丢了”“老化没执行”的案例归根到底不是DEM配置错了而是网络没“撑住”。建议在EcuM和NvM的时序里做一次确认在下电前NvM先写完再切报文唤醒网络再进睡眠。4. 实测中最常见的几个DEM坑与排查4.1 DTC状态位不更新、状态掩码错误这是测试日报里最常出现的问题典型的“明明清了故障状态掩码还是0x28”“明明是新的故障confirmedDTC位居然一直是0”。排查思路可以按这几步来用调试器确认应用层是否真的调用了Dem_SetEventStatus断点打在调用处看看传入的EventId和status值。查看EventId是否被宏定义正确。工具版本升级后旧的DemConf_DemEventParameter_xxx枚举名可能变化如果你代码里用的是旧名称编译能过就怪了。检查防抖计数器的阈值。如果阈值设置得很大比如100而你的故障信号只持续了3个周期那pendingDTC肯定置不了更容易直接PASS。确认NvM有没有正确读到旧的DTC状态。如果NvM数据全是0xFF未初始化DEM会认为没有DTC存储记录。另外还有一类“状态掩码是乱值”的问题比如0x51这种不合逻辑的组合。多数情况下是DTC状态字节在配置工具里被手动修改过和DEM生成的逻辑码表不一致。解决办法是重新生成代码并做一遍出厂默认值设置。4.2 NvM存储/老化异常掉电后DTC丢失我们项目里就遇到过故障确认后诊断仪一读状态位完全正常但ECU断电重启再读DTC变成了“曾经有过”的pending状态或者干脆什么都没了。排查下来问题出在NvM Block的“Deferred”机制上配置中是延迟写入ECU掉电太快NvM根本来不及把RAM里的状态刷进Flash。还有一类老化和NvM相关的坑老化条件已经满足但DTC就是“清不掉”。看实现代码会发现老化计数器是在NvM里保存的如果NvM写失败DEM永远认为还差一次循环。所以遇到“老化卡住”的Bug去翻NvM的读写状态往往比改DEM参数更有效。4.3 事件ID冲突与DTC编号重复多个Event共享一个DTC编号是允许的但工具上可能会有两种配置方式一种是在Event里直接填DTC编号另一种是先定义DTC实体再在Event里引用。这俩方式看起来差不多但生成出来的DTC排查表会不一样。如果你采用“DTC实体Event引用”的方式在生成DTC列表时多个Event会被合并成一个DTC实体不会出现重复项如果你在每个Event里都填一遍DTC编号有的工具就会生成两个DTC实体到时候0x19 01列表里就会看到两个一模一样的DTC。排查方法很简单打开生成的Dem_Cfg.c对比一下每个DemDtcId对应的DTC值再用UDS测试仪发0x19 01扫一遍响应列表应该不会出现同一个DTC码出现两次的情况。4.4 多核与中断Port访问/RTE通信问题现在ECU跨核越来越多DEM如果放在Core0而应用层SWC在Core1RTE跨核通信就会引入延迟和一致性风险。典型问题是Core1把故障状态写到PortCore0的DEM要等下一个周期才读到导致状态位响应慢了一拍。如果PLC或HIL测试里对“故障确认时间”有精确要求这就会成为问题。另一个更具杀伤力的问题是在中断服务里调用Dem_SetEventStatus。DEM内部有临界区保护但在中断上下文里调用可能会导致优先级反转或者死锁。建议的做法是中断里只置普通变量由周期任务读取并喂给DEM。如果非得在中断里调用一定要仔细审查DEM的Dem_SetEventStatus实现是否有OS级别的中断保护以及调用的OS优先级是否匹配。4.5 排查速查表现象优先排查点常见解决方向DTC状态掩码一直不变应用层EventId、防抖阈值检查SWC是否真的调用了Dem_SetEventStatus检查Debounce配置是否过于苛刻掉电后DTC丢失NvM写时序、Block关联改为Immediate写入或延长下电时序老化不执行AgingCycleThreshold、NvM恢复值检查老化计数器初值确认NvM读出的值不是0同一个DTC出现两次DTC实体重复检查Event的DTC引用方式合并实体0x14清不掉DCM的Session/Security、NvM写失败先读DCM的日志确认27解锁是否成功故障确认慢Runnable周期、降级跨核缩短SWC周期或改为直接映射诊断请求无响应网络管理状态、0x28配置让ECU保持网络模式延长Bus-Sleep时间最后再分享一点个人习惯。每完成一个新项目的DEM配置我都会做一张完整的“DTC生命周期测试矩阵”覆盖首次故障确认、掉电重启、老化循环、0x14清除、0x85关闭记录、0x28关闭通信这6个维度。在ECU上跑一遍再交付给测试组能省掉后面一大半扯皮。DEM这东西说难不难但细节是真的多能把每个DTC从出生到消亡的路径都捋清楚你就算真正入门AUTOSAR诊断了。如果你在配置过程中遇到特别奇葩的DEM问题欢迎在评论区发出来我可能在下期专门挑几个有代表性的案例拆一拆。
返回列表