ARTICLE DETAIL

资讯详情

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

UDS诊断入门:从0x10会话控制吃透CAN总线报文与刷写流程

UDS诊断入门:从0x10会话控制吃透CAN总线报文与刷写流程 CAN总线上做诊断这一块经常有人问我“UDS 到底从哪开始学”我的答案一直是同一句先把 0x10 会话控制服务弄透。别看它只有两个数据字节却是整个诊断体系的第一道门槛——ECU 处在哪种会话下决定了你能不能做安全解锁、能不能擦写 Flash、能不能读 DTC。网上讲 UDS 的教程不少但大多上来就甩一串 ISO 14229 的表格看完还是不会抓包。这篇文章我就按实际排查的思路走一遍从 0x10 的报文格式、正负响应、会话超时机制到刷写流程里它负责的“开场”最后再把我自己踩过的 NRC 坑列出来。目标是让看完的人拿着报文能看懂拿起工具敢上手。1. UDS 到底在解决什么问题0x10 为什么是入门第一课1.1 用两分钟理解 ISO 14229 和 UDS 的关系先说个基本概念。UDS 全称 Unified Diagnostic Services统一诊断服务它的核心规范是 ISO 14229-1。你可以把它理解成一套“车厂和零件厂约好的暗号表”规定诊断仪Tester和 ECUServer之间要怎么说话、说什么、怎么回应。这套暗号跑在整车通信网络上最常见的是 CAN 总线。CAN 本身只负责把数据从一个节点搬到另一个节点它不管这段数据是“读电压”还是“刷软件”。于是 ISO 14229 在 CAN 之上定义了一层诊断协议把“服务 IDSID 子功能 参数”的结构定下来再配合 ISO 15765-2也就是我们常说的 ISO-TP做报文分段传输。换句话说你平时抓到的诊断报文本质上是三层东西叠在一起最底层是 CAN 帧中间是 ISO-TP 的传输层控制单帧、多帧、流控最上层才是 UDS 真正的服务内容。这里有一个很多新手容易混淆的点UDS 不只存在于 CAN 上它也可以通过 CAN FD、以太网DoIP、LIN 等传输。但绝大多数入门场景都在 CAN/CAN FD 上所以这篇文章的所有报文拆解也都基于 CAN 来展示。ISO 14229 之所以重要是因为它把所有诊断服务统一编号了比如 0x10 就是会话控制、0x22 是读数据、0x2E 是写数据、0x19 是读 DTC、0x31 是例程控制。只要遵循这套标准通用的诊断仪就能跟不同品牌的 ECU 对话这也是它成为汽车诊断事实标准的原因。1.2 为什么入门一定从 0x10 开始0x10 服务叫 Diagnostic Session Control诊断会话控制。它干的事很简单让 ECU 切换到不同的“工作状态”。但这个简单动作是整个诊断逻辑的开关。举个例子。ECU 上电后默认在默认会话Default Session这时候很多服务是受限的——比如你想写 EEPROM、想执行某个内部例程ECU 会直接拒绝。你必须先通过 0x10 把会话切到扩展诊断会话Extended Session或编程会话Programming Session权限才放开。然后再做安全访问0x27、写数据0x2E、例程控制0x31这些操作。如果一开始不切会话后面全是 0x7F 负响应。说得直白一点0x10 就像大楼的门禁。你按了楼层按钮请求进入某个会话门禁系统才会放你进对应区域。没有这一步你在电梯里按什么按钮都没用。更进一步刷写软件、标定数据、读取故障码前设置状态几乎都要先走一次会话切换。我见过不少刚入行的工程师拿着 CANoe 一顿发 0x27 解锁结果 ECU 一直不回正响应排查半天才发现人家还在默认会话里压根没做 0x10 02。所以说0x10 不是“一个简单的服务”它是所有后续诊断操作的前置条件。把它的请求格式、响应格式、超时机制吃透后面的安全访问、读写数据、DTC 服务学起来都会顺畅很多。2. 0x10 服务的报文格式从请求到响应逐字节拆解2.1 请求报文服务 ID 子功能0x10 请求报文的格式非常固定就是两个字节第一个字节SID固定为 0x10表示“这是一个会话控制服务”第二个字节子功能Sub-function表示“你要切到哪个会话”ISO 14229-1 里定义了几个标准子功能值实际开发中你至少会接触三四个子功能值会话名称用途0x01默认会话Default Session上电后的初始状态权限最低作为安全兜底0x02编程会话Programming Session用于 Flash 刷写、Bootloader 交互权限最高0x03扩展诊断会话Extended Diagnostic Session用于标定、测试、特殊功能比默认会话权限高0x04安全系统诊断会话Safety System Diagnostic SessionISO 14229-1:2020 新增用于安全相关系统诊断需要注意0x10 请求没有额外参数至少在标准规定里就是 SID 子功能两个字节。如果有些 OEM 自定义加了补充字节那是私有的扩展不在本文讨论范围。判断请求是否合法的要点很简单把 0x10 当成服务 ID看第二个字节是不是 ECU 支持的子功能再看请求长度是否符合 ECU 定义的 DLC。从 CAN 报文的角度看0x10 请求如果通过单帧发送CAN 数据域长这样02 10 02 00 00 00 00 00第一个字节 0x02 是 ISO-TP 单帧协议控制信息PCI表示后面跟了 2 个数据字节紧接着才是 0x10SID和 0x02子功能。很多新手用 CAN 工具看裸报文第一反应都是“为什么数据里多了个 02 开头”其实就是 ISO-TP 的封装。这一点特别容易忽略但它决定了你解析报文时要把第一个字节单独划出来。2.2 正响应格式0x50 子功能 P2/P2*当 ECU 同意切换会话时会回一个正响应。0x10 的正响应有特点不单单是 SID 加 0x40 再回显子功能那么简单它还会上报两个超时参数用来告诉诊断仪“你接下来每一次请求最长可以等多久”。正响应结构如下第一个字节0x50即 0x10 0x40表示正响应第二个字节请求时发过来的子功能值原样回显第三个字节P2_server_max高字节第四个字节P2_server_max低字节第五个字节P2*_server_max高字节第六个字节P2*_server_max低字节P2 和 P2* 这两个概念很多文章一笔带过但实际调试中特别关键。P2_server_max 表示 ECU 处理普通诊断请求能接受的最大等待时间单位是毫秒P2*_server_max 则表示 ECU 处理那些比较耗时、需要额外延长的请求时能接受的最大等待时间也是毫秒。诊断仪收到这个值以后会把它作为发送请求后的超时阈值。如果诊断仪以自己的固定超时时间去等ECU 换了编程会话后响应变慢很容易误判成超时。举一个真实的抓包例子。诊断仪发送02 10 02 00 00 00 00 00ECU 回正响应06 50 02 00 32 01 F4 00拆开看0x06ISO-TP 首帧表示共 6 个数据字节0x50正响应 SID0x02子功能回显确认切到了编程会话0x00 0x32P2_server_max 50 ms0x01 0xF4P2*_server_max 500 ms这个例子是我故意选的常见配置。实际项目里P2 可能被配成 50ms 或 100msP2* 可能被配成 5000ms不同 ECU 差别很大。所以千万不要拿着固定超时去测所有 ECU一定要先读它 0x10 正响应里的数值。2.3 负响应格式0x7F 0x10 NRC如果 ECU 不答应它不会沉默而是会回一帧负响应。格式只有三个字节第一个字节0x7F固定负响应 SID第二个字节0x10表示“对哪个服务发出的拒绝”第三个字节NRCNegative Response Code负响应码负响应码是整个诊断调试里最实用的信息。看到 0x7F 不要慌关键是读最后一位。比如收到03 7F 10 12意思就是“0x10 服务被拒绝原因见 0x12”。0x12 表示子功能不支持说明你请求里带的会话值这个 ECU 压根不吃。在实际项目里0x10 相关的常见 NRC 有NRC含义触发场景处理方向0x10通用拒绝ECU 内部条件不满足没有更细的码可用检查整车状态、电源状态0x11服务不支持ECU 没有实现 0x10 服务多见于老旧节点确认诊断规范换其他方式进入0x12子功能不支持请求了 ECU 不支持的会话值查看该 ECU 支持的会话列表0x13请求长度错误请求字节数不对比如多发了参数检查报文 DLC、ISO-TP 长度0x22条件不满足如车速过高时禁止切编程会话满足前置条件后重试0x78请求已收到正在处理ECU 正在做耗时操作需要诊断仪等待配合后 0x7F 0x10 0x78 后再收正响应NRC 的出现不代表设备坏了它只是一种协商方式。很多测试人员一看负响应就报 bug其实要先判断是谁的问题是测试请求本身不合法还是 ECU 当前状态不允许还是真的实现缺陷。这个判断能力基本决定了你在诊断岗位上能走多深。3. 会话状态机与超时机制为什么 ECU 会“自己跑回默认会话”3.1 ECU 的会话切换状态机其实很简单ECU 里的会话状态可以理解成一个小型状态机。上电初始所有 ECU 都落在默认会话。收到 0x10 请求后根据子功能跳到对应会话。正常情况下状态迁移关系是这样的默认会话可以切到编程会话、扩展会话、安全系统会话扩展会话可以切到编程会话、默认会话编程会话也可以回到默认会话任何会话下只要收到切回默认会话的请求都会立刻回到默认会话注意标准里允许的迁移方向是“任意会话切到任意会话”只要子功能合法。但实际工程中不少 ECU 会限制某些跳转比如从编程会话直接切扩展会话被拒绝必须先把控制权交回应用层。遇到这种限制最直接的排查办法就是试并读取 ECU 的 NRC。不要凭直觉断定 ECU“坏了”先确认它是不是故意在拒绝你。从安全角度想这个状态机设计得很有讲究。默认会话是零权限的“安全房”编程会话是权限最高的“重地”。ECU 设计者通常会限制编程会话的进入条件比如要求车速为 0、点火开关处于特定位置、整车处于安全状态。这样即使诊断仪被误操作也不会在行驶过程中把刹车控制器的 Flash 给擦了。3.2 P2 和 P2*ECU 用来“踢你”的定时器先提到一个细节ECU 进入非默认会话后并不会永远待在里面。如果诊断仪长时间不发请求ECU 会自动退回默认会话。这个时间不是一个固定常量而是和 0x10 正响应里的 P2*_server_max 强相关。简单说ECU 内部有一个会话保持定时器。在非默认会话下每次成功收到一个有效诊断请求定时器就被重置一次。如果超过一定时间没有收到任何请求定时器超时ECU 自动切回默认会话。这个“一定时间”很多实现里直接取 S3 定时器配置值但 ISO 14229 里同时也用 P2/P2* 来描述响应延迟的上限两者配合使用保证诊断仪和 ECU 的预期一致。举个例子。某 ECU 的 P2*_server_max 是 500msS3 超时是 5000ms。如果你发完一个请求后等了 600ms 还没收到响应那你不能简单归结为“ECU 无响应”——因为单个请求的响应等待时间已经超过了 P2*但这不代表会话已经掉回默认。会话掉回默认是在“持续没有请求”的情况下发生的所以抓包时一定要区分两种超时一个是单次请求响应的超时一个是会话保持的超时。实际工作里我见过最典型的坑是用诊断仪切换到了编程会话然后 UI 界面卡住了几分钟等用户点下一步发现 ECU 早就回到默认会话了。这时候如果直接发擦除请求等来的必然是负响应。正确做法是在长时间操作前先重新发一次 0x10确认还在目标会话里再继续后续服务。3.3 0x10 在完整刷写流程里扮演的角色既然这篇教程面向入门我就把刷写流程串一遍让你看清 0x10 在每个环节的位置。一条典型的 UDS 刷写流程大概是这样的0x10 02切入编程会话0x27 01/02请求安全访问种子并发送密钥解锁 Flash 擦写权限0x31 01 FF 00执行擦除例程根据 OEM 定义不同地址不同0x34 00 44 xx xx请求下载告诉 ECU 要写多少数据0x36 01 xx xx ...循环传输数据注意每次数据块长度限制0x37请求退出传输0x31 01 FF 01校验编程完整性可选视规范而定0x10 01切回默认会话0x11 01ECU 复位让新程序生效你会发现0x10 不仅出现在开头结束前也往往要切回默认会话。这正是 0x10 的另一个作用——安全“退出”。一旦退出非默认会话所有解锁状态、写入权限通常都会被清掉防止诊断仪遗漏状态导致后续误操作。理解了这一层你会发现 0x10 不是一个“发一次就完事”的服务它伴随整个刷写生命周期。4. 动手实操从总线抓包到完成一次 0x10 会话切换4.1 实操环境和工具选择先把三样东西备齐想真刀真枪地验证 0x10你需要一个能发 CAN 报文的工具和可以观测报文的环境。我用得最多的是这三种组合软件方案PCAN-USB PCAN-View 或者 CANoe。CANoe 功能强大但价格高适合项目开发PCAN-View 轻量适合快速验证。低成本方案树莓派 / 开发板 CAN 收发器模块 can-utils。适合在家自学成本几十块钱就能搞定。生产环境常用诊断仪、刷写工具、自动化测试台架。这类工具封装得比较好但反而不容易看清底层报文。如果你只是入门验证我最推荐 can-utils 方案因为所有字节都是透明的。先用ip link set can0 up type can bitrate 500000把接口拉起来然后candump can0开一个窗口监听再用cansend can0 7E0#0210020000000000发送请求整个流程完全透明非常直观。4.2 一步步操作发送 0x10 请求并解读响应接下来跟着我走一遍。假设我们通过 0x7E0诊断物理请求 ID发送请求ECU 通过 0x7E8 回复CAN 波特率设置为 500kbps这是最常见的组合。第一步打开终端窗口输入candump can0 -n 20把 listener 起来抓 20 帧就停。看到输出后先确认总线上确实有数据再发送请求。第二步发送切到编程会话的请求cansend can0 7E0#0210020000000000021002拆开就是刚才讲的ISO-TP 单帧头 SID 0x10 子功能 0x02。这时候看candump输出正常情况下会抓到一帧从 0x7E8 回来的数据比如7E8 [8] 06 50 02 00 32 01 F4 00对照 2.2 节的拆法0x066 字节首帧0x50正响应0x02回显子功能告诉我们切到了编程会话00 32P2 50ms01 F4P2* 500ms到这里0x10 流程就走通了。第三步再做一次切回默认会话的验证。发送cansend can0 7E0#0210010000000000正常会收到7E8 [8] 06 50 01 00 32 01 F4 00注意第二字节变成 0x01确认已经回到默认会话。这一步很关键因为它能帮你确认“会话切换成功”到底是 ECU 真的切了还是诊断仪自己以为切了。实际项目里会话切换成功的唯一硬证据就是正响应报文而正响应里的子功能回显又进一步确认了 ECU 当前进入的到底是哪个会话。所以抓包时不要只看有没有响应一定要看响应里的第二字节。4.3 抓不到响应怎么办从头排查总线、ID 和 PCI新手最容易卡死的一个环节是cansend发出去了但candump里什么都没有。这时候别急着怀疑 ECU 坏了按下面顺序排查。先确认物理层。CAN 总线两端需要 120 欧终端电阻如果你只是在桌面上把两块开发板对接至少要在其中一端接入终端电阻。没有终端电阻时报文偶发丢失、波特率协商失败、甚至完全通信不上都正常。再确认 ID。0x7E0 是大多数车厂遵循的物理请求 ID但并不是所有 ECU 都用。有些车型用 0x7E2、0x7E4有些诊断仪通过功能寻址 ID 0x7DF 去广播。功能寻址能同时唤醒多个 ECU但 0x10 这类会话控制平时尽量用物理寻址否则一堆 ECU 同时切换会话会乱套。这个原则在 ISO 14229 相关文档和设备规范里是反复强调的。接着确认 ISO-TP 封装。cansend发送的原始 CAN 数据是02 10 02这个02不是服务 ID 的一部分而是协议控制信息。如果你在发请求时把第一个字节写成10 02ECU 根本不会理你因为它会认为这是长度超标的单帧或者格式非法。最后确认波特率。CAN 总线上所有节点必须使用相同波特率。CANFD 和经典 CAN 混用还有更多坑特别是在“CANFD 支持”开关没同步时报文会以错误格式上总线导致另一端收不到。这个在我实际调试中碰到过不止一次排查到最后往往只是配置界面里一个勾选的问题。5. 0x10 相关的典型故障案例与排查技巧5.1 从头到尾排查一个 NRC 0x12 案例我在某个项目中遇到过一台控制器诊断仪发送0x10 03请求扩展诊断会话结果 ECU 回了03 7F 10 12也就是子功能不支持。第一反应是奇怪因为 0x03 是最常用的扩展会话怎么会不支持。后来查看该控制器诊断规范才发现这台 ECU 为了简化逻辑只实现了默认会话和编程会话。所有标定工作都需要先切到编程会话再用安全访问解锁。也就是说这个 ECU 的设计者放弃了扩展会话这条路。这个案例说明千万不要把“标准定义的功能”当成“每台 ECU 都应该支持的功能”。ECU 支持的会话列表永远以该车型的诊断规范为准。排查这类问题我的经验是先做一次“扫描”把 0x01、0x02、0x03、0x04 依次发一遍把每个子功能的 NRC 记下来。这样一个矩阵表就能快速看出这台 ECU 支持哪些会话、不支持哪些比翻文档快得多。5.2 案例切到编程会话后0x78 出现的处理流程还有一次我在做刷写自动化时发送0x10 02后收到了03 7F 10 78。0x78 表示请求已收到但 ECU 需要额外时间处理。很多刚入门的工程师一看到 0x7F 就说失败其实 0x78 是“软失败”它告诉诊断仪“你别急我还在弄”。收到 0x78 后正确做法是保持会话挺住继续发送后续请求。但要注意0x78 之后 ECU 究竟多久会给出结果并没有强制规定。诊断仪侧需要配合一个足够长的等待窗口否则会在等待期间误触发超时。像这种情况我一般会在测试脚本里把“0x78 重试等待”配置成至少 1 到 2 秒同时观察 ECU 后续是否主动发出正响应。这里还要提醒一点诊断仪在收到 0x78 后别立刻重发同样的 0x10 请求。因为 ECU 刚才的 0x10 正在处理中你重复发请求反而可能让状态机混乱甚至引发二次 NRC。正确姿势是记录 0x78等待然后观察 ECU 是否继续完成原请求。5.3 会话保持的坑状态丢失与误操作最后一个要说的坑是 0x10 切换成功后状态“掉”回去。这个坑在刷写和标定环节特别常见。比如你发完 0x10 02然后去做数据准备准备了几分钟再回来发安全访问却收到了 0x7F 0x27 0x33这大概率不是你密钥算错了而是会话已经掉回默认权限已经失效了。我之前调试一个自动刷写脚本时就在“大文件分包发送”这步踩过这个坑。0x36 每帧之间的间隔如果超过了 ECU 的会话保持时间ECU 就会认为诊断仪已经“离场”自动回到默认会话。后续数据帧再发过去全部变成负响应。解决方案有两个方向一个是把 0x36 分包发送的时间间隔压缩在 ECU 允许的窗口内另一个是做一个“心跳机制”在长时间等待场景里定期发送一个无害的诊断请求比如 0x3E 保持活跃或周期性读一个 ID来维持会话不超时。顺带说一句0x10 01 这个子功能并不鸡肋。很多情况下你主动切回默认会话比等 ECU 超时自动切回更安全。因为在自动超时之前解锁状态和临时配置可能还在主动退出等于彻底清场。尤其在做完刷写、需要整车进入正常模式的场景流程末尾主动发一次 0x10 01是既标准又稳妥的习惯。说到这我想起自己第一次抓 0x10 报文时的场景——盯着06 50 02 00 32 01 F4这串字节翻规范翻了半天才明白每一个数是什么意思。其实诊断报文没有多玄乎它就是把状态、权限、时间约定这些后台逻辑压缩成几个十六进制字节摆在总线上。你把 0x10 的格式、状态机、超时机制和 NRC 对照表搞清楚了后面的 0x19、0x27、0x31 学起来会发现底层思路一脉相承。如果你正准备踩进车载诊断这个门不用急着背一大堆服务先把 0x10 这条线走通后续真刀真枪调试时会少走很多弯路。
返回列表