尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

UDS协议0x19服务0x06子功能详解:按记录号读取DTC扩展数据

UDS协议0x19服务0x06子功能详解:按记录号读取DTC扩展数据 搞OBD诊断或者ECU开发的朋友对UDS协议肯定不陌生。但说实话0x19服务ReadDTCInformation读取诊断信息里的子功能特别多平时用的最多的可能就是0x02按状态掩码读DTC和0x04读快照/扩展数据。我今天想重点聊聊0x19服务里一个容易被忽略却又非常实用的子功能——0x06也就是按记录号读取DTC扩展数据记录ReportDTCExtendedDataRecordByRecordNumber。这个子功能什么意思呢简单说每个DTC除了故障码本身还可以挂一堆“环境数据”比如故障发生时的车速、水温、电压、累计里程等。这些数据按记录号Record Number一条条存着0x06就是让你指定DTC和记录号精准地把某一条捞出来。它比0x04一次读全部更省带宽、更灵活。我在做售后诊断仪和产线EOL检测工具时0x06基本是必调的。这篇文章我会直接切入报文级解析把请求响应格式、参数计算、NRC处理讲透再把实战中踩过的坑一条条列出来希望对正在做协议栈开发或者诊断功能测试的朋友有帮助。1. 0x19服务的整体图景与0x06的定位1.1 0x19服务的子功能家族0x19服务在ISO 14229-1里是“读取诊断信息”服务它的子功能非常多从0x01到0x0E后面还有扩展每个子功能有不同的用途。我先把常用的几个列出来大家感受一下0x06在整个家族里的位置。0x01报告DTC数量按状态掩码统计有多少个DTC。0x02报告DTC列表按状态掩码返回符合条件的DTC。0x03报告DTC快照数据SnapShot Data也就是故障发生瞬间的“冻结帧”。0x04报告DTC扩展数据记录Extended Data Record把一个DTC关联的全部扩展数据记录都读出来。0x06报告DTC扩展数据记录按记录号也就是指定DTC指定记录号读单条扩展数据。0x0A报告支持的DTC列表以及每个DTC支持哪些快照、扩展数据记录号。0x0B报告第一个DTC快照用于快速确认当前故障状态。0x0C报告第一个DTC扩展数据记录同样用于快速访问。你会发现0x06和0x04很像都是读扩展数据记录差别就在“按记录号”这4个字。0x04是把某个DTC下所有扩展数据一股脑返回而0x06是指定某一条返回。很多ECU的DTC会挂多条扩展数据比如记录1是故障发生时的环境数据记录2是故障消失时的环境数据记录3可能是ECU内部状态。要是你用0x04每次都得把这几条全读完数据量上去了协议栈处理时间也变长用0x06就可以按需读取效率高很多。1.2 0x06子功能为什么值得单独拿出来讲我在早期的项目里一直用0x04读扩展数据觉得够用了。后来做售后诊断工具客户反馈某车型网络信号不好诊断仪读DTC扩展数据经常超时。排查下来问题就出在0x04一次性返回的数据量太大CAN帧得分包发送网络拥堵时会丢帧。后来改成用0x06按记录号读取每次只拉一条报文短了稳定性明显好很多。还有一个场景是OTA刷写之后做功能验证。刷写完需要确认某些DTC的状态和扩展数据是否符合预期如果全量用0x04读不仅慢还得逐个解析用0x06按记录号精准读取脚本写起来也简单。更关键的是0x06的请求格式很“干净”就一行报文特别适合设备端快速实现和调试。所以虽然0x04用得多但从工程实践角度0x06的“精准读取”能力是无可替代的。2. 报文格式逐字节拆解2.1 请求报文“你说你要什么”0x06子功能的请求报文格式是固定的一共6个字节。我直接给出字节排列请求19 06 DTC_High DTC_Middle DTC_Low RecordNumber其中19是服务IDSID表示这是读DTC信息的服务。06是子功能表示按记录号读取扩展数据。DTC_High、DTC_Middle、DTC_Low是目标DTC的三字节编码。RecordNumber是你要读取的记录号1个字节。这里最容易犯错的地方是DTC的字节序。ISO 14229里DTC定义是三字节的比如P0101对应的编码是0x01 0x01 0x01。很多工程师第一次看到0x01 0x01 0x01会以为这是三个独立的字节搞不清哪个是高、哪个是低。实际上DTC编码的高字节High Byte在最前面中间字节Middle Byte在中间低字节Low Byte在最后。你从DTC码本里查到P0101查表是0x010101那么发送时就是0x01 0x01 0x01这刚好是三字节顺序天然一致。但遇到P0204这种就得查对应编码不要想当然按ASCII拆。RecordNumber的范围是0x00~0xFF。0x00通常是无效值因为扩展数据记录号从1开始定义如果ECU返回NRC 0x31请求超出范围先查你填的记录号是不是从1开始的。给个实际例子我要读取DTC P01010x010101的记录号为1的扩展数据记录那请求就是19 06 01 01 01 01就这么简单。很多OBD工具里显示的“Read DTC Extended Data Record by Record Number”底层就是这条报文。2.2 正响应报文“我给你你要的”正响应的格式比请求要复杂一些因为返回的数据长度不固定。ISO 14229-1规定的0x06正响应格式如下正响应59 06 DTC_High DTC_Middle DTC_Low RecordNumber StatusOfDTC ExtendedDataRecord其中59是响应SID等于请求SID0x19 0x40。06是子功能原样返回。DTC_High、DTC_Middle、DTC_Low跟请求里的DTC相同用于确认。RecordNumber跟请求里的记录号相同。StatusOfDTC这个字节表示DTC当前的状态比如是否确认、是否历史故障、是否测试失败等每一位定义见ISO 14229-1的DTC状态掩码。ExtendedDataRecord长度不固定内容由ECU厂商定义通常包含故障发生时的各种环境数据。这里有一个重点响应里的ExtendedDataRecord到底有多长协议栈不会主动告诉你这个字段的长度而是通过响应报文的整体长度来隐式表示。比如你收到一帧CAN诊断报文通常最多8字节或者多帧ISO-TP传输的连续帧最后解析出来59 06 01 01 01 01 DTC状态 后面一堆数据字节剩下的全是ExtendedDataRecord。所以你在做解析时不能只按固定长度去截而是要先解析出DTC、记录号和状态字节后面剩下多少字节就是多少。这一点在我做CAPL测试脚本的时候踩过坑原因就是提前写死了长度。再补充一点DTC状态字节StatusOfDTC是0x04、0x06子功能响应中都带有的它是故障码当前状态的实时体现。比如它是0x20已确认当前存在故障还是0x08测试通过但历史存在能直接辅助判断故障是否真的存在这个比单纯看故障码本身要可靠得多。2.3 负响应NRC码背后的含义如果诊断仪发出的请求ECU没法正常处理ECU会回负响应。负响应报文格式是7F SID NRC其中SID是0x19NRCNegative Response Code负响应码告诉你“为什么不行”。0x06子功能常见的NRC有下面几种NRC含义常见触发原因0x11服务不支持ECU压根没实现0x19服务或者此服务只在某些诊断会话下可用0x12子功能不支持ECU实现了0x19服务但没实现0x06子功能比如有些老ECU只有0x01~0x040x13报文长度错误请求报文字节数不对少于或多于6字节0x22条件不满足当前ECU状态不允许读取扩展数据比如需要进入编程会话或安全解锁状态0x31请求超出范围DTC不存在或RecordNumber超过了该DTC拥有的记录数量或RecordNumber为00x7F服务未在活动会话中支持在默认会话Default Session下请求但ECU限制该功能仅在扩展会话中可用我调试过程中遇到最多的就是0x31。原因五花八门有的是DTC编码查错了有的是记录号没从1开始有的是ECU把0号保留用于“全部记录”。还有一种情况是DTC本身存在但这个DTC当前没有生成扩展数据记录所以ECU认为“你要的记录根本不存在”也会回0x31。这个时候你最好先用0x0A子功能去查一下该DTC支持哪些记录号再发起0x06请求。3. 0x06与0x04的对比别用错子功能3.1 功能边界差异0x04和0x06都能读DTC扩展数据记录。很多新手容易混淆我干脆把两者的差异摊开了讲。0x04的请求格式是19 04 DTC_High DTC_Middle DTC_Low响应格式是59 04 DTC_High DTC_Middle DTC_Low StatusOfDTC ExtendedDataRecord_1 ExtendedDataRecord_2 ...也就是说0x04会把这个DTC下面所有记录号从1到N的扩展数据记录全部返回。如果ECU定义了3条记录那响应里就会有3段数据每段包含记录号、数据长度和数据内容但具体每段的内部结构不同ECU不同。0x06则只返回你指定的那一条。这两个功能在ISO 14229-1的设计里其实是互补的0x04适合“全量审计”比如下线检测时需要把故障关联的所有环境数据都拉取归档。0x06适合“定向查询”比如远程诊断或售后排查时你明确知道要看记录2那直接读记录2。3.2 实际选型建议实际工程选型我给你几点建议第一如果你在用诊断仪做常规读码优先用0x02读DTC列表再用0x06按需读扩展数据不要一上来就0x04全量拉取。这样报文少、效率高对CAN总线带宽压力也小。第二如果你的ECU定义了特别多的扩展数据记录0x04一次响应可能会超过ISO-TP的连续帧能力比如超过4095字节导致某些工具解析异常。用0x06拆分请求可以规避单帧响应过长的问题。第三0x04响应里的扩展数据记录具体怎么组织在不同ECU平台上可能会有差异。有些ECU习惯在每段记录前面加一个记录号和长度有些则直接拼接。相比之下0x06响应里记录号是单独字段返回的解析起来更明确、更不容易出错。从协议栈开发角度0x06更好实现因为不用自己去维护记录号的隐式偏移。所以我的结论是新项目做诊断功能设计时优先保证0x06可用并把记录号的定义整理成一份清晰的DBC或Excel表格0x04可以作为辅助工具给产线或售后用但不要依赖它做精确解析。4. 实战中的关键参数与配置细节4.1 DTC格式和字节序的确认前面提到DTC是三字节编码但实际项目中有几个细节容易被坑到。第一DTC编码表不是“P0101直接就是0x010101”这么简单的。ISO 15031-6定义了DTC的标准编码方式每个DTC对应一个三字节数值。比如P0101对应0x010101这个没错但P1234对应的编码可能是0x0234具体要查表。千万不要把ASCII码“P0101”的字符值直接填进报文里那是错的。做协议栈时建议把所有DTC和扩展记录号的映射关系做成配置文件启动时一次性加载避免每次请求时查表出错。第二字节序在不同工具上显示方法不同。CANoe的Diagnostic Console里可能直接显示“DTC with 3 bytes”而有的工具显示“0x010101”有的显示“01 01 01”。本质是一样的。我习惯统一用十六进制字节序列表示因为写CAPL脚本或Python脚本时直接按字节组包不容易错。第三DTC里还有一类“pending DTC”待定DTC和“permanent DTC”永久DTC它们在三字节编码上没有区别区别在状态字节。0x06读扩展数据时DTC编码一样但状态字节不同返回的扩展数据记录内容可能也不同。比如同一个P0301第一次发生时记录1存储的是第一次故障的环境数据第二次发生时记录5存储的是第二次的。所以不要以为“DTC相同记录号相同数据就永远一样”。4.2 记录号的定义一致性记录号RecordNumber是0x06的核心参数但它很“个性”因为ISO 14229-1并没有规定记录号的具体含义只规定“每个DTC可以关联一个或多个扩展数据记录每条记录有唯一记录号”。具体记录号代表什么完全由ECU厂商自己定义。我在某个项目里就遇到这种情况厂商定义的记录号如下记录号1故障发生时的环境数据车速、转速、冷却液温度、进气温度、燃油修正值等记录号2故障发生时的ECU内部电压值5V、12V、参考电压等记录号3故障发生时的行车里程记录号4故障消失时的环境数据记录号5~8保留给后续扩展所以我在测试用例里必须严格按这个定义去验证。如果测试人员不知道记录号含义随手写了个记录号3结果返回的是“行车里程”而不是“电压”就会误判产品问题。这里我强烈建议项目启动时就要推动OEM或ECU供应商提供一份“DTC扩展数据记录号定义表”并把它纳入软件开发配置库而不是放在某个工程师的本地Excel里不然换个人就找不到了。另外记录号是1个字节范围0~255。0通常不可用。255有时候会被厂商定义为“所有记录”但这不是ISO标准的强制要求所以用之前一定要确认别想当然。4.3 扩展数据记录的内容组织方式0x06正响应的ExtendedDataRecord字段内容完全由厂商自定义ISO 14229-1只建议了一些常用数据参数比如ISO 14229-1 Annex D里列出的DID、DTC severity等。有的ECU会把数据按DID形式组织即每两个字节一个参数ID后跟数据值有的则直接按固定顺序拼接一个字节一个参数。举个例子某ECU的扩展数据记录1内容如下0x01 0x02 0x03 0x04 0x05 ...厂商定义的参数顺序可能是车速2字节、发动机转速2字节、冷却液温度1字节。那解析时就得按这个固定模板去拆。还有一种方式是带长度字段比如0x01 0x02 0x04 0x1A 0x00 0x00 ...第一个字节表示参数ID或数据字段编号第二个字节表示数据长度后面跟数据。这种带自描述的结构解析起来更安全但占用的字节会多一些。我的建议是如果由你来设计这个扩展数据的格式尽量采用“ID Length Data”的自描述格式因为后续车厂需求变更多了增加新参数不用改整个数据结构解析端也能通过Length字段跳过未知字段向前兼容性会好很多。如果你只是解析别人的ECU定义那就老老实实按照对方提供的模板来不要自创解析规则。5. 踩坑实录与排查建议5.1 常见问题速查表我把在0x06调试过程中真实遇到的问题列成一个速查表方便大家对照排查。问题现象可能原因解决思路请求后无响应物理层没通、CAN ID不对、ECU处于busy状态抓总线报文确认请求发出去了检查诊断ID和功能寻址/物理寻址配置负响应0x12ECU没实现0x06子功能用0x0A查支持的子功能列表或更新ECU软件负响应0x13请求报文长度不对少于或多于6字节检查报文长度尤其注意ISO-TP发送时不能多带填充字节负响应0x31DTC或记录号非法用0x0A确认DTC支持的记录号确认DTC编码查表正确记录号不能为0负响应0x22当前会话/状态不允许读取先发10 03进入扩展会话或完成安全解锁流程响应解析出来的数据完全不对扩展数据记录解析模板不对字节序或参数顺序搞错找供应商要最新的解析模板按模板逐字段比对用已知数据反推验证数据总长度超出一帧CAN范围使用了ISO-TP多帧传输确认工具/驱动支持连续帧接收和重组加大接收超时时间5.2 排查思路和工具建议真正的实战中排查问题不能只靠肉眼看报文。我强烈建议手头准备一个CANoe或者PCAN加Wireshark的抓包环境。第一步抓CAN总线日志确认请求报文有没有发出去。很多“无响应”问题其实是物理链路或者ID配置错了。你去看总线日志干净的报文在总线上一定清清楚楚。第二步核对请求报文的每个字节。把请求报文逐字节和标准格式比对重点看DTC顺序和RecordNumber。这个步骤能排除至少一半的0x31。第三步查ECU的软件需求文档。0x06的响应内容格式、每个记录号的含义、DTC编码表这些在需求文档里一定有。如果文档缺失就去找ECU供应商要或者用0x0A子功能主动试探看ECU自己怎么描述。第四步如果0x06读出来的数据和你预期不符可以用0x04做交叉验证。0x04会返回所有记录通过对比确认是不是单条记录解析的问题。不过要注意0x04可能因为数据量太大导致抓包时间较长别在总线负载高的场景下测试。第五步别忽视“时序”问题。有些ECU在DTC清除之后扩展数据记录会被重置如果刚清完DTC紧跟着读0x06很可能读到0xFF或空数据。所以测试流程里建议先注入故障、触发DTC再读扩展数据确定数据稳定后再清DTC。在上手0x06子功能的过程中我个人的体会是它的逻辑比很多看似复杂的服务更“小而美”报文结构固定、响应明确难点全在对DTC编码、记录号和扩展数据格式的约定理解上。做诊断开发工具链顺手与否很重要但真正决定进度的还是对自己负责的DTC定义和数据字典熟不熟。如果你刚开始接触0x06最有效的做法就是先把ISO 14229-1里的0x19服务章节通读一遍再拿着诊断仪在一台真实ECU上把所有子功能都试一遍尤其是用0x0A去对比0x06能读到的记录号列表。踩过一次坑后面就会顺手很多。
返回列表