资讯详情

资讯详情

AUTOSAR Dcm模块服务列表与UDS诊断内部实现逻辑详解

搞AUTOSAR的朋友十有八九都绕不过Dcm这个模块。每次点开配置工具左边一栏全是DcmDsl、DcmDsd、DcmDsp开头的子模块里面又塞了几十个函数、几百个配置项第一次接触基本是懵的。其实诊断通信这块没那么玄乎Dcm就干三件事把诊断请求收进来、按规则分发到对应服务、再把响应发出去。这篇我就把Dcm模块的服务列表和内部实现逻辑拆开聊一聊适合正在做或者准备做诊断功能开发的工程师也适合想搞懂UDS在AUTOSAR里怎么走通的小白。1. 先搞懂Dcm在AUTOSAR里到底扮演什么角色1.1 从一条诊断报文说起一条UDS诊断请求从外部诊断仪发出来要经过CAN物理层、CAN收发器、CAN控制器、CanIf、CanTp最后才到Dcm。Dcm之前的链路都是运输工只负责把数据搬到地方Dcm才是真正坐柜台的它得判断客户来办什么业务是查DID0x22、写数据0x2E、解锁安全等级0x27还是切会话0x10然后把这个业务转给对应的后台处理。所以我一直跟同事说Dcm不是独立的业务系统它是整个诊断服务的中枢。它自己不做具体业务DID的读写往往要调用NvM或者Com例程控制要调用Rte或者BSW底层接口Dcm只负责把请求拆开、判断合法性和权限、把业务分发下去再把结果打包成响应发出去。AUTOSAR把Dcm拆成三个子模块分别是DSLDiagnostic Session Layer、DSDDiagnostic Service Dispatcher和DSPDiagnostic Service Processing。这三个词你理解成DSL管会话和定时器DSD管分发和路由DSP管具体的诊断服务逻辑。很多新手配置的时候只盯着DSP里的服务列表结果响应链路总是跑不通原因往往是DSL和DSD两层的参数没配对。1.2 我们常说的Dcm服务列表到底指什么这里要先澄清一个容易混淆的概念。平时大家嘴里说的Dcm服务列表通常指的是两类东西一类是Dcm对外提供的API函数服务列表也就是AUTOSAR标准接口比如Dcm_Init、Dcm_MainFunction、Dcm_StartOfReception、Dcm_CopyRxData、Dcm_CopyTxData这些。这类接口是给其他模块或者集成代码用的它们是Dcm模块的门面。另一类是Dcm支持的UDS诊断服务列表比如0x10、0x11、0x22、0x27、0x2E、0x31、0x3E、0x85等等每个服务在Dcm配置里都对应一个使能开关和一组参数。这类服务是给外部诊断仪用的它们是Dcm模块的菜单。这两类列表我都讲而且会串起来讲。因为做AUTOSAR集成的时候两边都对不上号后面排查问题会非常痛苦。2. 拆解Dcm内部的服务DSL、DSD、DSP三层各管什么2.1 DSL层传输协议的收包端点DSL是Dcm最底层的一层直接对接PduR或者CanTp。这一层主要管理的是报文的到达和发送不关心报文内容是什么诊断服务。它要做的事包括接收诊断请求并启动接收流程、发送诊断响应、管理P2/P2*服务器定时器、管理会话状态和状态持续时间。常见的DSL层服务API大致有这么几类类别典型API作用初始化与周期运行Dcm_Init、Dcm_MainFunction模块初始化、周期任务调度接收相关Dcm_StartOfReception、Dcm_CopyRxData、Dcm_RxIndication告诉Dcm开始收包、拷贝数据、收包完成发送相关Dcm_ProvideTxBuffer、Dcm_CopyTxData、Dcm_TxConfirmation获取发送缓冲区、拷贝响应数据、确认发送完成会话管理Dcm_GetSesCtrlType、Dcm_SetSesCtrlType查询和切换诊断会话类型安全等级管理Dcm_GetSecurityLevel、Dcm_SetSecurityLevel查询和设置安全等级内部信号Dcm_DslInternal_...唤醒、复位、故障注入等内部处理注意DSL并不解析诊断服务IDSID它只把收到的字节流完整地交给上层。而且DSL层最重要的工作之一是处理P2定时器这是我后边要重点讲的坑点。2.2 DSD层路由分发中心请求收进来之后DSL把完整的诊断报文交给DSD。DSD做的事情是解析诊断报文的格式提取SID、Sub-function、数据段然后根据SID表决定应该调用哪个DSP服务处理函数。DSD层的核心函数通常是Dcm_DsdDecodeMessage当然不同版本具体函数名会有差异但逻辑都一样先判断报文长度是否合法、SID是否被支持再判断当前会话状态下是否允许执行该服务然后检查安全等级和权限最后分发到具体的服务处理函数。这个设计思路很像银行大厅里的叫号机。DSD不办业务只负责检查你排没排上号、你的业务类型属于哪个窗口然后把号牌给对应窗口。DSD在分发前会判断几件事物理寻址还是功能寻址、当前会话是否合法、当前安全等级是否满足、是否在Dcm的SuppressRespBit模式下需要抑制响应。如果某一步失败它会在DSD层直接生成NRC负响应码比如0x7F、0x11服务不支持、0x22条件不满足等而不会进入到DSP层。2.3 DSP层真正干活的业务逻辑DSP层是Dcm模块里最庞大的部分因为每一个支持的UDS服务都对应一组处理函数和配置项。这一层的核心任务是把DSD分发过来的合法请求转换成对上层软件组件或底层驱动模块的实际调用。常见的DSP子服务包括会话控制服务0x10处理会话切换、会话持续时间对应Dcm_DspSesCtrl_Service安全访问服务0x27处理种子请求、密钥校验对应Dcm_DspSecurity_ServiceDID服务0x22 ReadDataByIdentifier、0x2E WriteDataByIdentifier对应Dcm_DspDID_Service例程控制服务0x31对应Dcm_DspRoutine_Service输入输出控制服务0x2F对应Dcm_DspIOControl_Service通信控制服务0x28对应Dcm_DspComControl_Service请求下载/上传服务0x34、0x36、0x37对应Dcm_DspRequestDownload_Service等测试仪在线服务0x3E用于刷新定时器控制DTC设置服务0x85对应Dcm_DspControlDTCSetting_Service读取DTC信息服务0x19对应Dcm_DspDTCRegion_Service清除DTC服务0x14对应Dcm_DspClearDTC_ServiceDSP层还有一个重要任务就是配置DID和RID的访问权限表。一个DID可以配置它允许在哪个会话下读取、在哪个会话下写入、需要多高的安全等级、是否允许功能寻址访问。这些配置在工具里看起来只是一张张表格但漏配置一个Session或者SecurityLevel就会导致诊断服务返回NRC 0x7F 0x22或者0x7F 0x33。2.4 一张表看懂Dcm对外服务接口为了方便集成和自测我把Dcm最常用的对外API整理成一张速查表。这里以AUTOSAR 4.x常见接口为例接口名称调用方向主要用途Dcm_Init系统启动时调用初始化Dcm内部状态和配置Dcm_MainFunction周期任务调用处理周期定时器、内部状态机Dcm_StartOfReceptionPduR/CANIf模块调用通知Dcm有新的诊断请求到达Dcm_CopyRxDataCANIf/PduR调用将接收缓冲区的数据拷贝到Dcm内部Dcm_RxIndicationCANIf/PduR调用通知Dcm整个诊断请求已接收完成Dcm_ProvideTxBuffer发送响应前Dcm内部调用从底层申请发送缓冲区Dcm_CopyTxDataDcm回写响应时调用将响应数据拷贝到底层发送缓冲区Dcm_TxConfirmation底层调用Dcm通知Dcm诊断响应已发送完成Dcm_GetSesCtrlType上层模块调用查询当前诊断会话类型Dcm_SetSesCtrlType上层模块调用请求切换诊断会话Dcm_GetSecurityLevel上层模块调用获取当前安全等级Dcm_SetSecurityLevel上层模块调用设置当前安全等级这十几个函数是Dcm模块和外部通信的主要握手点。实际项目里你可能还会看到很多带Dcm_Dsp_前缀的函数那些是配置工具根据你使能的服务自动生成的内部服务处理函数也建议都保留在服务列表里不要随意删除。3. 服务列表配置背后的三道关卡设计3.1 配置服务使能和DID权限的具体步骤用Vector达芬奇或者EB tresos配置Dcm时最关键的是下面几件事第一步在DcmDspService使能需要的UDS服务。找到Service Table把0x10、0x22、0x2E、0x27这些服务全部使能同时把对应的Session和Security配置填好。第二步配置DcmDspDID。每一个DID要单独配置数据长度、可读权限、可写权限、所属的会话范围以及安全等级要求。比如0xF190这个DID用来存放软件版本号它可能允许任何会话读取但只有BootLoader或者解锁后的App会话可以写。第三步配置安全访问。0x27服务要配置Seed和Key的长度、密钥计算算法ID、允许的失败重试次数还要配置Level 1和Level 2分别对应上什么安全等级。第四步配置完所有参数后一定要重新生成RTE和BSW代码。很多人改完配置后忘了生成代码或者生成代码后忘记把生成的Dcm_Cfg.c编译进工程导致实际运行的服务和配置表完全对不上。这里我想特别强调一个经验DID的读写权限不仅受Dcm内部配置影响还受上层软件组件影响。如果DID在配置工具里允许读但上层代码在读取时挂起了、返回了错误状态Dcm依然会抛NRC。所以在排查DID读写问题时不要把锅全甩给Dcm。3.2 0x2E写DID时必须过的三道关卡很多新人在调试0x2E写DID的时候会遇到明明配置没问题但写进去的数据就是不对。这里要提一下实际应用中Dcm服务的三道把关逻辑。第一关是Dcm本体的服务级校验也就是DSD层做的事SID支持吗会话支持吗安全等级够不够报文长度够吗如果这关没过Dcm直接返回NRC 0x7F 0x11、0x7F 0x22、0x7F 0x33、0x7F 0x13之类。第二关是DID的权限校验也就是DSP层基于DID配置做的检查当前会话允许写这个DID吗安全等级允许写吗DID长度和请求长度匹配吗如果这关没过Dcm会返回0x7F 0x31请求超出范围或者0x7F 0x72一般编程失败等NRC。第三关是应用层校验也就是DID写操作真正执行时的业务逻辑判断比如数据格式不对、校验和错误、写NvM失败、写入的标定值超出合理范围这些都要由上层代码返回错误。Dcm只是负责把DID值交给某个函数去执行写入执行结果由上层反馈。所以排查0x2E问题的时候不要只盯着Dcm配置要顺着调用链往下看。我遇到过好几次工具配置完全正确但是应用层的DID Write函数内部没有做数据范围的合法性检查导致写入非法值后ECU行为异常。这不是Dcm的问题但调试时你往往最先怀疑Dcm。3.3 诊断响应路径与P2定时器在服务中的角色诊断响应不是Dcm说发就能发的得看底层发送资源是否就绪还得看处理时间是否满足P2/P2定时器要求。P2是服务器在收到请求后到开始发送响应之前允许的最大时间一般配置为25ms到50ms之间P2是服务器在发送了NRC 0x78响应待定之后允许继续处理的最大时间一般配置为5000ms。如果Dcm在P2时间内无法完成处理并返回响应它必须先发一个NRC 0x78告诉诊断仪我还在处理你等等。然后启动P2定时器在P2时间内完成真正的响应。如果你没有配置好NRC 0x78的自动发送功能或者发送NRC 0x78后没有正确切换定时器就会导致诊断仪端的超时误报。这个逻辑在Dcm里是由DSL层管理的。Dcm在一个诊断请求到达后首先启动P2定时器然后交给DSD和DSP处理。正常情况下服务处理函数会在P2超时之前调用Dcm_ProvideTxBuffer和Dcm_CopyTxData把响应数据准备好。如果处理时间超过了P2Dcm会自动发送0x7F SID 0x78的响应并切换到P2*定时器等待业务完成。我在实际项目里经常发现一个问题就是Dcm_MainFunction的调用周期太长导致P2/P2*定时器计算不准。比如有的项目把Dcm_MainFunction放到10ms周期任务里那P2定时器精度最多也就到10ms如果你把P2配成25ms实际表现的超时阈值可能忽大忽小。对时间敏感的UDS服务我建议Dcm_MainFunction至少放到5ms周期里跑。4. 用一次0x22请求走通整个Dcm链路4.1 环境假设这里我以一个典型的CAN诊断环境为例物理寻址ID是0x7E0功能寻址ID是0x7DFDcm挂在PduR下面底层是CanTp CanIf CanDriver。我们想读取DID 0xF190的软件版本号。这个例子很简单但走完一次完整请求你会发现Dcm服务列表里至少涉及Dcm_StartOfReception、Dcm_CopyRxData、Dcm_RxIndication、Dcm_DsdDecodeMessage、Dcm_DspDID_Service、Dcm_ProvideTxBuffer、Dcm_CopyTxData、Dcm_TxConfirmation这些服务每一环都必须正常返回诊断链路才算通。4.2 一步步看请求如何被处理第一步诊断仪发送CAN诊断请求02 22 F1 90。CAN ID是0x7E0。这一帧先到CanDriverCanDriver把数据交给CanIf。第二步CanIf根据CAN ID匹配到CanTp通道。CanTp识别到这是一个单帧长度是2字节0x02于是解析出有效数据是22 F1 90然后通过PduR调用Dcm_StartOfReception把诊断报文长度和地址信息告诉Dcm。第三步Dcm收到StartOfReception后会对请求做一个长度预检查。长度合法它就会准备好接收缓冲然后调用底层获取数据。此时底层通过Dcm_CopyRxData把数据拷贝到Dcm内部缓冲区。第四步当完整请求接收完成底层调用Dcm_RxIndication通知Dcm可以开始处理了。Dcm的DSL层收到这个信号后会启动P2定时器并把数据交给DSD。第五步DSD调用Dcm_DsdDecodeMessage解析出SID0x22、DID0xF190。它先查服务表发现0x22服务在Default Session下使能了再查传输类型当前是物理寻址请求不需要抑制响应然后判断当前会话和安全等级是否满足服务要求。全部通过DSD把请求分发给DSP。第六步DSP调用Dcm_DspDID_Service进入DID读取流程。它根据0xF190查DID配置表发现该DID允许在Default Session读取、安全等级要求为0于是调用上层提供的DID读函数拿到软件版本号数据。第七步Dcm根据响应格式拼装响应字节流这里可能是06 62 F1 90 01 02 03表示成功响应。然后Dcm通过Dcm_ProvideTxBuffer申请发送缓冲区用Dcm_CopyTxData把响应数据拷贝到底层发送缓冲交给PduR和CanIf发送。第八步底层发送完成后调用Dcm_TxConfirmationDcm确认响应已发出整个服务处理结束P2定时器被重置。整个流程如果从外部看就是诊断仪发一帧请求ECU回一帧响应中间所有环节都被Dcm服务列表管理得井井有条。任何一个服务接口没有配置好或者代码生成有问题都会导致链路断掉。4.3 配置层面需要改哪几个ECUC参数基于上面的流程在配置工具里你需要确认并检查这几个关键参数PduR里的Dcm接收端口和发送端口配置是否正确Dcm模块接收的Pdu要和CanTp的payload长度匹配。DcmGeneral里的MainFunction周期参数建议5ms或者10ms匹配你实际的任务调度。DcmDsl里的P2 Server Delay和P2* Server Delay要根据ECU实际处理能力和诊断需求设置。DcmDspService里的SID使能表确认0x22被使能确认当前会话状态下允许使用。DcmDspDID里的DID表格确认0xF190存在、长度配置正确、读权限允许、Session要求合理。这些参数在生成代码后会落到Dcm_Cfg.c和Dcm_Cfg.h里如果你想检查实际配置对不对直接打开这两个文件看宏定义值就行。比如DID长度配置错了你会在Dcm_Cfg.c里看到对应DID表项的长度字段不是预期值。5. 常见问题与排查技巧实录5.1 UDS开发中Dcm相关问题的速查表我把自己做项目时经常遇到的Dcm问题整理成了表格你们可以直接对照排查现象可能原因排查建议诊断仪发送请求后ECU无任何响应Dcm_StartOfReception没被调用说明报文没到Dcm检查CanIf、CanTp、PduR路由配置请求进入Dcm但完全没有响应Dcm接收了请求但DSD没通过校验发送了NRC但你忽略了加大CAN日志过滤确认是否收到0x7F响应返回0x7F 0x11服务不支持SID使能表没配或者当前会话权限不足检查DcmDspService里SID使能和Session配置返回0x7F 0x22条件不满足当前会话不满足、安全等级不够、功能寻址不支持检查DSD层的服务权限判断返回0x7F 0x31请求超出范围DID没配置、长度不匹配、DID的读写属性不对检查DID长度和读写权限表返回0x7F 0x78但没有后续响应业务处理超过P2时间但在P2*时间内没有完成检查业务处理函数是否挂起确认P2*定时器配置读取DID数据多一位或少一位DID长度配置和实际数据长度不一致检查DID长度参数0x27一直校验失败Seed和Key长度不匹配、密钥计算算法不一致、失败次数未清零检查安全访问算法ID、种子来源、重试计数器5.2 NRC 0x78响应待定机制的实际使用场景NRC 0x78这个机制是Dcm设计里最容易理解错的地方。它并不是诊断服务失败而是我还在处理稍等的意思。有些工程同学会把0x78当成错误处理实际上它只是延长约定时间的一种握手信号。举个实际例子0x31例程控制里有一个刷写例程例程执行时间可能要2秒钟但P2只有50ms。如果Dcm不先发0x78诊断仪等不到响应就会判定超时。所以Dcm在例程处理函数还在执行时会自动发送NRC 0x78然后反复利用P2定时器继续等待。只要在P2时间内发出最终响应整个交互就是正常的。这里的关键是Dcm_DspRoutine_Service内部实现时要记得及时告诉DSL还没有处理完否则Dcm会以为服务处理已经结束就可能直接发送一个错误的响应或者不发响应。不同工具生成的服务函数里通常有对应的内部API做这件事建议仔细阅读生成代码里的注释。5.3 Dcm_MainFunction调度和全局中断屏蔽的坑最后说一个比较隐晦的问题。Dcm_MainFunction负责定时器管理和很多状态机的推进。如果你的工程把全局中断屏蔽时间做得太长比如在某个中断服务函数里关了中断做了一大堆事情那么Dcm_MainFunction的周期就会抖动P2/P2*定时器的时间基准就会漂。我见过一个案例ECU在特定工况下会进入一个长临界区导致Dcm_MainFunction整整200ms没有跑诊断仪侧发送了一个0x27请求后ECU已经完成了解锁但响应迟迟没有发出。结果就是诊断仪报超时但ECU里安全等级已经被提高了。这种问题排查起来特别费劲因为功能逻辑上没问题纯粹是调度时序被干扰了。所以做诊断功能一定要留个心眼尤其是涉及多模块咬合的时候别只查Dcm本身要看看跑Dcm_MainFunction的任务是不是被其他高优先级任务饿死或者打断了。我在实际项目里还有一个习惯就是把Dcm相关的发送确认日志和接收指示日志全部打印出来利用CANoe的日志时间戳对齐看一次诊断交互的时间线。只要日志里能看到Dcm_StartOfReception到Dcm_TxConfirmation的间隔基本就能判断问题出在接收链路、处理逻辑还是发送链路。诊断开发做到后面拼的就是这种时间线上的敏感度。Dcm的服务列表再多本质上都是在管理这条时间线把每一步该发生的动作按时做完。如果你能带着这个视角去看文档和配置很多原来觉得绕的东西都会一顺百顺。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →