ARTICLE DETAIL

资讯详情

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

UDS 0x36服务NRC码详解:ECU刷写故障排查与实战经验

UDS 0x36服务NRC码详解:ECU刷写故障排查与实战经验 搞ECU刷写的兄弟十有八九都跟0x36服务打过交道。这玩意儿在UDS诊断协议里头看着不起眼就是个“传数据”的动作可真到了产线或者售后刷写现场一堆奇奇怪怪的NRC码全是从它身上冒出来的。0x73、0x72、0x31、0x24这些东西光看名字是记不住的你得真踩过坑才知道它们背后到底意味着什么。这篇东西我就围绕0x36服务展开把刷写流程里最常见的NRC、触发条件、排查思路全部掰开揉碎讲一遍。今天讲的不只是“0x73是块序列计数器错误”这种表面定义我会结合uds协议栈源码层的判断逻辑、上位机实际发送行为、ECU端bootloader的处理流程把这些NRC码出现的前因后果讲清楚。适合正在做UDS刷写上位机、在调bootloader、或者天天跟产线刷写故障死磕的工程师看新手也能顺着这篇建立一套排查框架。1. 先搞明白0x36服务在整条刷写链路里的位置1.1 一次完整的UDS刷写0x36才是真正的主角很多人一说刷写就想到0x34请求下载实际上0x34只是“开了个头”真正把固件数据搬进ECU内部存储器的是0x36诊断服务TransferData。一次完整的UDS刷写序列大概是这样的10 02 - 进入扩展会话Programming Session 27 01 / 27 02 - 安全访问Security Access请求种子/发送密钥 34 00 44 xx xx xx - 请求下载RequestDownload告诉ECU要写哪里、写多少 36 01 data... - 传输数据块1TransferData块序列号从0x01开始 36 02 data... - 传输数据块2 36 03 data... - 传输数据块3 ... - 循环传输直到全部数据发完 37 - 请求退出传输RequestTransferExit 31 01 02 xx xx - 例程控制触发Flash编程完成后的校验/有效性检查 11 01 - ECU复位回到应用模式这里可以看到0x36在整个刷写事务里是执行次数最多的服务数据量越大的固件0x36的次数就越多。想象一下一个2MB的固件如果每次能传1KB那一轮刷写就要执行2000多次0x36服务任何一次出错都会导致刷写流程中断或数据不完整。0x36服务在整个刷写流程里的角色有点像物流运输里的“干线司机”。0x34相当于签了运输合同告诉收货方我要送多少货、送到哪个仓库0x36就是那一车一车拉过去的货0x37则是签收确认。如果司机送错了仓库、送错了数量、送货顺序乱了仓库那边肯定要拒收这个“拒收”反馈就是NRC。1.2 0x36服务的请求格式与ECU状态机为什么它这么“矫情”0x36服务请求报文格式如下字节0: 0x36SID服务标识符 字节1: BlockSequenceCounter块序列计数器 字节2~N: 数据块Block Data看起来很简单对吧但ECU侧对这条服务的处理是有严格状态机约束的。如果你没先进入扩展会话、没通过安全访问、没先发0x34直接甩一个0x36过去ECU基本都会回NRC。这就是为什么很多人第一次写刷写脚本时单发0x36总是被拒绝因为ECU不认“无状态”的0x36。块序列计数器BlockSequenceCounter是0x36服务最核心的机制ECU端会期望完整的块序列号从1开始按1、2、3……递增当计数到0xFF时下一个是0x00。这本质上是ECU用来检测“数据帧是否丢失或乱序”的一个手段。注意这里的块序列号是按块数递增不是按字节数递增跟CAN报文的DLC大小没有直接关系。另外还有一个特别容易忽略的点刷写会话下0x36的数据长度和0x34里声明的memorySize要能对得上。ECU内部会累计收到的数据长度如果你发的数据总长度超过或少于0x34声明的大小ECU会在某个时刻拒绝你。这个逻辑不是所有ECU都完全一样但大多数bootloader都会做这个校验因为这是防止“刷入不完整固件”的最后一道防线之一。理解了这些底层逻辑再回头看NRC很多问题就清晰了。NRC不是ECU随便抛出来的错误码它是ECU的状态机在特定条件下给出的“拒绝理由”而我们要做的就是通过这个理由反推ECU内部到底卡在哪一步。2. NRC码逐条拆解0x73和0x72背后对应着哪类问题2.1 0x73 wrongBlockSequenceCounter最容易踩的计数器坑0x73在UDS协议里叫wrongBlockSequenceCounter翻译过来就是“块序列计数器错误”。ECU返回这个NRC意味着ECU识别到你当前发送的块序列号不是它期望的那个值。触发0x73的场景其实有好几个第一种序列号不连续。比如你上一帧发的是36 01开头这一帧突然变成了36 03ECU期望的是02于是直接抛0x73。这种情况常见于多线程上位机发送线程和确认线程没有做好同步导致数据块乱序发出。第二种序列号没有从1开始。有些bootloader会比较严格0x34完成后的第一个0x36必须带块序列号0x01如果你从0x00开始或者从某个中间值开始ECU直接拒收。第三种重发机制处理不当。这是个非常隐蔽的坑很多上位机在发送超时后会重发但重发时没有把块序列号回退到超时那一帧的值。看起来重发了实际上ECU拿到的序列号是乱的。这个问题我在后面实战案例里会详细展开。第四种块序列号回绕错误。块序列号是单字节到0xFF后下一个应该是0x00。一旦你的上位机在计数器实现上用了普通int却忘了处理回绕就会在固件较大、块数超过255时出现问题。而CAN FD时代块大小更大但大固件的块数依然可能超过255回绕逻辑一定要测到位。0x73这个NRC本质上是ECU在跟你说“兄弟你发送的顺序不对我这边按状态机等你呢。”排查的时候先别急着怀疑ECU优先检查上位机发送逻辑里的块序列号生成方式。2.2 0x72 generalProgrammingFailureECU内部拒绝的“沉默信号”0x72的NRC全称是generalProgrammingFailure翻译成大白话就是“编程常规失败”。这个NRC比0x73难搞多了因为0x73至少还明确告诉你是“顺序不对”而0x72是ECU告诉你“你传的数据我没法接受或者写不进去”但具体原因可能五花八门。从我实际经验来看0x72最常见的几个触发原因第一Flash擦写失败。ECU要把数据写入内部Flash之前一般会先擦除相关扇区。如果擦除操作本身失败比如电压不稳、Flash驱动出错、扇区地址越界ECU就会返回0x72。第二写入数据的校验失败。有些bootloader在接收完整包后会做CRC校验如果计算出的校验值跟0x34或0x37部分携带的校验信息对不上也会报0x72。第三写入地址超出允许范围。0x34里声明的地址是逻辑地址ECU内部可能要做逻辑地址到物理地址的映射。如果映射结果超出了Flash的物理地址范围或者落到了保留区域写入动作会失败并反馈0x72。第四非易失存储器编程条件不满足。部分ECU在编程前会检查一些硬件条件比如电压是否在编程允许范围内、温度是否过高、安全访问是否真的通过条件不满足同样会报0x72。这里要特别提醒一点0x72出现时不要只盯着0x36这一帧看因为0x36只是把数据交到ECU的接收缓冲区真正的“写入Flash”动作可能是在后台异步执行的。也就是说0x36返回正响应不代表数据已经写进Flash了可能只是“数据收下了”真正的失败发生在后面的某个时刻。这个异步特性是很多新手理解不了0x72的根源。2.3 还有一组NRC也经常在刷写场景里出现除了0x73和0x72这两个“主角”刷写过程中还有几个NRC出镜频率相当高0x31 requestOutOfRange请求超范围。一般在0x34请求下载时出现比如memorySize超出了允许刷写的最大长度、地址不属于可编程区域、addressAndLengthFormatIdentifier不合法等。0x24 requestSequenceError请求顺序错误。ECU发现你跳过了某个必须的前置步骤。比如没发0x34就直接发0x36、没发27 01就发27 02这类。0x22 conditionsNotCorrect条件不正确。这个跟0x24有点像但含义更宽泛经常用在“当前条件下不能执行该服务”比如刷写过程中电瓶电压异常、整车处于某种安全状态不允许刷写。0x13 incorrectMessageLengthOrInvalidFormat消息长度或格式不对。0x36的数据长度超过了ECU能接收的最大块大小或者帧里少字节、多字节都可能被回0x13。0x33 securityAccessDenied安全访问被拒绝。如果种子和密钥不匹配或者密钥算法不对在27服务那一步就会收到0x33根本轮不到0x36出场。这组NRC虽然不像0x73/0x72那么“致命”但它们的出现往往意味着前面某个环节就没走对。记住一个原则NRC是状态机给出的结果排查时要从刷写状态机的起点开始捋而不是只看出问题的那一帧。3. 当0x36返回NRC时正确的排查路径是什么3.1 先看会话、解锁和0x34再查0x36本身的数据很多工程师一看到0x36被拒就疯狂检查0x36的数据内容实际上这是走了弯路。正确的排查顺序应该是从外到内、从前到后第一步确认会话模式。刷写前必须进入扩展会话10 02。有些bootloader在默认会话下根本不会响应0x36直接回0x7F 36 7F或0x7F 36 22之类的。如果你用诊断仪抓到的log里没有10 02或者10 02之后又切回了默认会话那0x36肯定会被拒。第二步确认安全访问是否通过。0x36属于“需要安全访问才能执行”的服务范畴没解锁就发0x36ECU大概率回0x33或0x24。这里有个常见问题某些ECU的安全解锁状态在会话切换后会失效如果刷写工具在0x34前后又重置了会话那解锁状态就丢了。第三步核对0x34的参数。0x34请求下载时带了一个关键参数addressAndLengthFormatIdentifier它决定了地址和长度字段的字节数。如果这个格式标识符和ECU后端解析逻辑不匹配后面对地址的判断全部会错位。另外0x34里声明的memorySize必须能容纳你后面通过0x36发送的全部数据。第四步检查0x36的块序列号和数据长度。块序列号是否从1开始连续递增数据块大小是否超过ECU声明或协商的最大值。超长、超短或者数据块大小与0x34声明的总长度不匹配都会触发NRC。这个排查顺序我在不同项目里验证过很多次命中率最高的永远是前两步会话丢了、解锁丢了。真正走到第四步才发现问题的情况反而是少数。3.2 用完整日志把一次刷写过程“定格”下来遇到0x36的NRC手里有一份高质量的完整日志比什么都强。这个日志不只是记录几条报文而是要把一次刷写过程完整“定格”下来。我建议的最小日志记录内容至少包括时间戳。精确到毫秒甚至微秒级排查CAN总线时序问题时没有时间戳基本寸步难行。方向。区分是诊断仪发送还是ECU回复双向都要记录。CAN ID。无论是物理寻址还是功能寻址总线上的ID要能区分清。数据场。完整记录从第一个字节到最后一个字节不允许截断。连续帧与流控帧。如果是ISO-TP分包传输连续帧和流控帧也必须记录下来很多0x36请求本身是跨多帧CAN报文传输的光记第一个帧代表不了完整服务。有一个技巧每次刷写成功后保存一份“黄金日志”。以后出了NRC把失败日志跟黄金日志做逐帧对比。这个方法看起来笨但效率极高因为大部分刷写NRC都是流程性错误跟黄金日志比对后往往一眼就能看出哪里多了、哪里少了、哪里时序不对。如果你的工具链支持自动比对那更好。CANoe、Tmaster这些工具都有脚本能力可以自动对比两组log文件把差异帧直接标出来。3.3 对照uds协议栈源码理解NRC是怎么一步步产生的搞UDS刷写不能只停留在“收到NRC就百度一下”的层面。想真正搞懂为什么ECU会返回某个NRC最好的方式是去读ECU端uds协议栈源码或者至少理解协议栈里NRC判断的执行顺序。典型的UDS协议栈处理一个诊断请求时NRC判断顺序大致是这样的1. 检查SID是否被支持 - 不支持回0x11serviceNotSupported 2. 检查当前会话是否允许 - 不允许回0x7FserviceNotSupportedInActiveSession 3. 检查安全访问状态 - 未解锁回0x33securityAccessDenied 4. 检查报文长度/格式 - 不对回0x13incorrectMessageLengthOrInvalidFormat 5. 检查子功能/参数合法性 - 不合法回0x31requestOutOfRange 6. 检查请求顺序/前置条件 - 不满足回0x24requestSequenceError或0x22conditionsNotCorrect 7. 执行服务内部逻辑 - 内部失败回0x72或0x73等这段顺序非常关键。它意味着ECU返回哪个NRC直接反映了“检查卡在哪一层”。比如你收到0x33说明前面格式检查、参数检查都过了只是卡在安全访问收到0x13说明SID和会话都合法但是报文长度不对收到0x72说明前面所有检查都通过了是执行阶段出了问题。理解了这段逻辑再去读协议栈源码你就能非常清楚地看到自己发的请求是在哪个检查点被拦下来的。这个排查能力是NRC码含义本身给不了的也是区分“会用”和“精通”的核心分水岭。4. 实战案例三个让我印象深刻的刷写翻车现场4.1 案例一0x73被“照单全收”背锅的是上位机重发机制这个案例是我早年间做产线刷写工具时遇到的。现场反馈是某工位刷写失败率突然飙升报错清一色是0x73。按常规思路0x73就是块序列号不对查ECU端逻辑查了半天没发现问题后来把上位机日志拉出来一看真相立刻浮出水面。那款上位机用的是阻塞式CAN发送接口但工程师在超时重发逻辑里偷了个懒连续帧超时后直接把整包0x36请求原封不动重发一遍。问题就出在这里原封不动意味着块序列号跟超时那一帧完全一样。ECU端呢在收到一个带完整数据的0x36后已经更新了内部期望的块序列号此时又收到一个序号不变的帧自然判定为wrong block sequence counter。这个案例给我的教训非常深刻上位机重发0x36时块序列号必须回退到超时那一帧的序列号不能原样照发。如果ECU端已经把超时帧当成有效帧处理过了上位机的重发逻辑必须基于对端状态做调整。更稳妥的做法是在重发前先发送一个ECU能识别的“同步请求”或重新走一遍小范围的0x340x36流程让ECU状态回到已知点。4.2 案例二0x72的假象根因在Flash地址映射另一个案例来自售后刷写场景ECU在接收到特定数量的0x36数据块之后稳定地回复0x72而且每次都固定在那个位置失败前后数据内容怎么改都无济于事。一开始我怀疑是数据块太大导致Flash写入失败把块大小调小了一倍问题依旧。后来翻了ECU的bootloader源码才发现根因压根不在0x36通道上而在0x34给出的逻辑地址到物理Flash地址的映射表里。这张映射表把应用区的某个逻辑地址段映射到了保留扇区而ECU在擦除时发现扇区号非法直接返回0x72。因为擦除动作是异步的表面上看0x36都收到了正响应但后台擦写失败后ECU在某个检查点统一上报0x72。这类问题的排查关键是0x72这种“编程失败”类NRC一定要结合0x34里的地址和长度信息一起看对照ECU内部的地址映射表和Flash扇区分布图确认每个逻辑地址都有合法的物理落点。不要被“固定位置失败”迷惑那个位置不一定是字节偏移也可能是某个扇区边界。4.3 案例三0x31和0x24联袂出场问题出在0x34的参数上这个案例发生在一次OTA远程升级联调中现象是偶尔出现0x31偶尔出现0x24而且都是在刷写流程的开始阶段。日志看起来0x34发送正常ECU也回了正响应然后0x36第一块就被拒有时候是0x31有时候是0x24。排查到最后问题出在0x34的addressAndLengthFormatIdentifier字段。这个字段的值为0x44时表示后面地址占4个字节、内存大小占4个字节。但服务端在组包时偶尔会把内存大小字段少填一个字节导致整个0x34的长度不对ECU解析出的“期望数据长度”是一个错乱值。等到0x36数据开始到达时ECU按照错乱的期望长度计算接收状态于是出现各种奇怪的NRC。这个案例给我们的经验是0x34返回正响应不代表0x34的参数完全合法。有些ECU对0x34的参数校验并不严格先把请求接收下来等到0x36传输过程中才暴露问题。所以排查0x36的NRC时一定要回头验证0x34的每一个字节特别是地址长度格式标识符和它后面的实际数据长度是否匹配。5. 刷写NRC速查表与排查技巧沉淀5.1 常见NRC速查表NRC名称含义常见触发动作排查方向0x13incorrectMessageLengthOrInvalidFormat报文长度或格式不正确0x36数据块过大/过小或总长度与0x34不符核对报文DLC与ECU支持的最大块大小0x22conditionsNotCorrect条件不正确刷写条件不满足如电压异常、整车状态不允许检查硬件条件和外部状态0x24requestSequenceError请求顺序错误跳过0x34直接发0x36或未解锁就发0x36检查刷写流程状态机确认前置步骤完成0x31requestOutOfRange请求超范围0x34地址非法或内存长度超出允许范围核对地址映射表和允许刷写范围0x33securityAccessDenied安全访问被拒绝种子/密钥错误或未解锁就执行受保护服务核对解锁算法和密钥计算逻辑0x72generalProgrammingFailure编程常规失败Flash擦写失败、数据校验失败、物理地址越界查ECU存储驱动、地址映射、硬件条件0x73wrongBlockSequenceCounter块序列计数器错误块序列号不连续、不从1开始、重发未回退检查上位机块序列号生成与重发逻辑这张表建议直接存一份在工位上每次刷写出问题先对着表把NRC归类再决定排查方向绝大部分NRC都能在5分钟内定位到大致范围。5.2 几个让我少加班的排查技巧第一最小复现。遇到刷写NRC不要急着在完整固件上反复试。把固件裁到最小比如只刷一个几百字节的测试数据块看还能不能复现。如果最小数据块能成功、大固件失败那基本可以断定是流程、计数或边界类问题如果最小数据块都失败那就是状态机或参数问题。第二录制回放。用CANoe、Tmaster虚拟通道这类工具把一次成功的刷写过程录制下来然后在故障环境里回放。回放时逐帧比对ECU响应很容易找到“从哪一帧开始ECU的响应跟成功记录不一致了”。第三对比黄金日志。黄金日志就是某次完整的成功刷写日志。出问题时把失败日志和黄金日志导到对比工具里做逐字段对比时间戳差异、报文差异一目了然。这个方法在产线复现类问题上尤其好用。第四关注ECU数据处理时序。CAN收发是毫秒级ECU内部Flash擦写可能是几十毫秒甚至上百毫秒。如果你的上位机发送节奏太快ECU接收缓冲区可能溢出或者后台擦写还没完成就来了一堆新数据这时候NRC往往是0x72或0x7F。遇到这类情况给0x36之间加一点延时或者用ISO-TP流控帧来限制发送速率问题通常能缓解。5.3 工具链选择与日志规范直接影响排查效率刷写类问题的排查效率很大程度上取决于工具链。当前业内用的比较多的包括CANoe、PCAN、Tmaster虚拟通道上位机等。CANoe功能全面但价格不菲适合整车厂和大型供应商。Tmaster虚拟通道上位机在ECU刷写场景里也有不少人用它能把上位机和服务端模拟整合在一个环境里方便复现问题。我的建议是不管用什么工具日志规范一定要统一。工程团队内部应该约定一套标准的日志格式包含时间戳、收发方向、CAN ID、数据内容、服务名解析等字段。这样一旦现场出现问题任何人都能拿着日志快速进入排查状态而不是先花半小时整理数据。另外如果你的团队以开源方案为主可以考虑基于开源的CAN/CANFD上位机方案二次开发。这几年开源社区里已经有不少全开源的CAN/CANFD上位机刷写工具支持收发报文、记录日志、脚本控制适合做产线或实验室的刷写验证。这类工具的好处是源码在手遇到问题可以改不用等厂商支持。5.4 避坑清单刷写0x36服务最常见的12个坑最后我把自己踩过和帮别人排查过的坑汇总成一份清单每一条都是实战中真金白银换来的0x34的memorySize比实际固件大小小导致0x36传不完。0x34的memorySize比实际固件大小大ECU等待更多数据最后因长度不足报错。addressAndLengthFormatIdentifier填错导致地址解析错位。块序列号没有从1开始而是从0开始。块序列号超过0xFF后回绕处理有Bug。超时重发时块序列号没有回退到超时帧的值。0x36的数据块大小超过ECU最大接收能力触发0x13。没有在0x36之间处理流控帧高速发送导致缓冲区溢出。发送完0x34后ECU返回正响应但实际没有进入传输状态马上发0x36被0x24拒绝。安全解锁状态在刷写过程中意外失效0x36中途被0x33拒绝。应用区和Bootloader区地址重叠0x34写入时擦除了关键区域。0x36的请求全部是正响应但ECU复位后应用起不来说明写入校验其实失败问题在数据本身或校验算法。对照这份清单去查问题能覆盖掉80%以上的0x36相关NRC故障。剩下那20%就得靠日志、源码和黄金比对来兜底了。我个人在实际排查中还有一个习惯每次刷写类问题定位到根因后我都会在协议栈源码或上位机代码的对应位置加一行注释记录当时的现象和最终根因。几个月下来这套“问题字典”比任何外部文档都管用。以后再遇到类似的NRC翻一翻注释就能想起当年的排查路径效率提升非常明显。0x73、0x72这些NRC码本身并不复杂复杂的是它们背后牵连的ECU状态机、地址映射、上位机逻辑、工具链配置这些周边环节。把0x36服务吃透等于把整个UDS刷写链路的大半条命脉握在了手里。希望这篇内容能帮你在下次遇到0x36报错时少走几步弯路。
返回列表