Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.18 负响应码(NRC)——当ECU说“不

被拒之门外的诊断仪

你第一次连上一台陌生的ECU。你发了条0x2E——想用DID 0xF40C写入一个新的发动机转速限制。ECU回你三个字节:0x7F 0x2E 0x33

如果你不知道NRC编码体系,你看到的只是一串十六进制数字。如果你知道——你看到的是一个完整的句子:“我收到了你的0x2E写入请求——但拒绝执行——因为安全访问被拒绝了——请先解锁。

三层信息,三个字节。

这就是NRC(Negative Response Code,负响应码)的威力——它把“不行“这种无用的回复变成了可执行的指令。没有NRC的“不行“等于没有回答——诊断仪既不知道自己错在哪,也不知道该怎么改。有了NRC,诊断仪可以实现自动化自愈——被拒后自己调整、自己重试。

NRC不是装饰——它是UDS协议的自我纠正机制。它是让诊断仪从“盲人摸象“升级为“自动化诊断“的关键基础设施。


NRC的编码结构——三字节里承载一个对话

负响应帧结构:
┌──────┬──────────┬──────────┐
│ 0x7F │   SID    │   NRC    │
├──────┼──────────┼──────────┤
│NR_SI │ 请求回声  │ 拒绝原因  │
│1字节  │ 1字节    │ 1字节    │
└──────┴──────────┴──────────┘

三个字节分别回答了三个问题:

  1. 0x7F — “这是一个负响应”(不是正响应)
  2. SID回声 — “我拒绝的是哪个请求”(上下文——诊断仪可能发了多个请求)
  3. NRC — “为什么拒绝”(可执行的原因编码)

为什么用0x7F而不是0xFF?

正响应SID的编码规则是请求SID + 0x40,覆盖0x40~0x7E。0x7F是此范围之外紧邻的下一个值——bit6=1(标识为响应而非请求),同时不与任何正响应SID冲突。0xFF虽然技术上也可行,但选择0x7F保持了编码的紧凑性:所有响应(正/负)集中在0x40~0x7F区间,诊断栈只需检查bit6即可区分请求与响应,无需单独处理0xFF这个离群值。


全量NRC速查表——每个“不“背后的含义

NRC缩写全称含义医院类比诊断仪动作
0x10GRGeneral Reject一般性拒绝——ECU无法给出更具体的原因“这个治疗我不能做,理由复杂不细说”无法自动恢复——日志记录、人工分析
0x11SNSService Not Supported此SID本ECU完全不支持“我们科室不提供这项检查”永久跳过此SID——此ECU永远不会支持
0x12SFNSSub-Function Not Supported服务支持但此子功能不支持“我们有心电图机,但不做动态心电图”换子功能或用其他SID实现类似目的
0x13IMLOIFIncorrect Message Length Or Invalid Format报文长度或格式与标准/配置不匹配“化验单格式错——拒收”检查请求字节数、检查DID长度定义
0x14RTLResponse Too Long响应数据超过传输层MTU“报告太长打印不出来——分段给”拆分请求——减少DID数量或分批发
0x21BRRBusy Repeat RequestECU忙——稍后重发相同请求“医生正在手术——几分钟后再来”等待后原样重试——等P2*周期过后
0x22CNCConditions Not Correct条件不满足——ECU状态不正确“先挂急诊科的号——这个科现在处理不了”检查并调整会话/状态——切换到正确会话
0x24RSERequest Sequence Error请求序列错误——步骤顺序不对“先叫挂号→再就诊→再开药——你跳步了”从正确序列第一步开始:建会话→解锁→操作
0x25NRFSCNo Response From Subnet Component子网组件无响应“拍了CT但CT室没回复”检查网关、子网络连接——重试
0x26FPEORAFailure Prevents Execution Of Requested Action已存在的故障阻止了请求执行“你有心脏病史——不能用这个麻醉药”先处理已存在的故障——清除或修复
0x31ROORRequest Out Of Range请求参数超出有效范围“没有第439号床位——我们楼就三层”查ODX确认有效的DID/地址/例程ID
0x33SADSecurity Access Denied安全等级不足——未解锁或解锁失败“你没权限看这份病历——先出示授权”执行0x27解锁——requestSeed→sendKey
0x35IKInvalid KeySecurityAccess密钥计算错误“授权码不对——请重新生成”重新请求种子→重新计算密钥→重新发送
0x36ENOAExceeded Number Of Attempts尝试次数超限——触发延迟锁定“输错密码太多次——30分钟后重试”等待延迟定时器到期后重试
0x37RTDNERequired Time Delay Not Expired延迟时间未到——还在锁定waiting period内“还得等20分钟——急也没用”继续等待——使用TesterPresent维持会话
0x38-0x4FRBEDLSDReserved By Extended Data Link Security扩展数据链路安全保留(加密通信相关——超出本书范围)
0x70UDNAUpload/Download Not Accepted上传下载操作未接受——可能因为前面对话未完“上次手术没做完——不能开始新手术”完成或正确终止前序下载/上传流程
0x71TDSTransfer Data Suspended数据传输已暂停“手术中途暂停——需要确认继续”发送0x36继续传输或0x37退出传输
0x72GPFGeneral Programming Failure刷写操作一般性失败“手术过程中发生了意外”检查Flash状态——可能需要擦除后重新编程
0x73WBSCWrong Block Sequence Counter块序列号错误——下载传输的数据块序号不对“药品批次号对不上——送错了”重新传输当前块——BlockSequenceCounter校验失败
0x78RCRRPRequest Correctly Received, Response Pending请求已收到、处理中——最终响应稍后给出“化验正在做——需要等一会出结果”等待——扩展P2*时间窗口——这是唯一的“正常中的异常“
0x7ESFNSIASSub-Function Not Supported In Active Session当前会话不支持此子功能“这个检查只能在住院部做——门诊做不了”切换到正确的会话→重试
0x7FSNSIASService Not Supported In Active Session当前会话不支持此服务“这个治疗只能在手术室做——你得先住院”发送0x10切换到指定会话→重试
0x81RPMTOHRPM Too High发动机转速太高——操作被拒绝“心跳太快——不能打这个针”请求降低转速或等待怠速状态→重试
0x82RSORPM Too Low发动机转速太低——操作被拒绝“血压太低——不适合做负荷测试”提高发动机转速→重试
0x83EIREngine Is Running发动机正在运转——此操作不能在运转时执行“不能边跑边换零件——先熄火”先停止发动机→重试
0x84EINREngine Is Not Running发动机未运转——此操作需要发动机在运转状态“这一项需要发动机转起来才能测”先启动发动机→重试
0x85ERTTLEngine Run Time Too Low发动机运行时间太短——温度/状态未稳定“刚启动——水温还没上来——不能测”等待发动机暖机→重试
0x86TEMPTOHTemperature Too High温度太高——条件不满足“发烧40度——不能做剧烈活动”等待降温或主动散热→重试
0x87TEMPTOLTemperature Too Low温度太低——条件不满足“体温过低——不适合进行代谢测试”等待升温或主动加热→重试
0x88VSTLVehicle Speed Too Low车速太低——条件不满足“原地不动——没法测ABS”必须行驶到一定速度→重试
0x89VSTHVehicle Speed Too High车速太高——条件不满足“速度太快——没法执行精确的转向角校准”减速到允许范围→重试
0x8ATPTOHThrottle/Pedal Too High节气门开度/踏板位置太高“油门踩太深——原地测不了怠速数据”松开油门→重试
0x8BTPTOLThrottle/Pedal Too Low节气门开度/踏板位置太低“没踩油门——无法测试加速响应”补油到正确开度→重试
0x8CTVINRTransmission Not In Neutral变速箱不在空档“挂挡了——不能做静态测试”挂到空档或P档→重试
0x8DTVINGTransmission Not In Gear变速箱未挂挡“空档——测试不了档位切换”挂到D档或R档→重试
0x8FBSCNEBrake Switch(es) Not Closed刹车开关未闭合——未踩刹车“没踩刹车——没法测试刹车系统”踩下刹车→重试
0x90SLNOIPShifter Lever Not In Park换挡杆不在P档“不在P档——有些设置不能改”切换到P档→重试
0x91TCCLTorque Converter Clutch Locked液力变矩器锁止——某些测试不能做“离合器锁住了——先解锁”解锁变矩器→重试
0x92VTHVoltage Too High电压太高——环境条件不满足“供电异常——不适合精密操作”检查电源系统→调整→重试
0x93VTLVoltage Too Low电压太低——环境条件不满足“电池快没电了——不能做要求稳定的检查”给电池充电或更换电源→重试

这个表看起来很枯燥——但它是你写一个能自我修复的诊断仪程序的必备知识。每一个NRC都不是终点——它是一个信号,告诉你“应该调整什么条件才能让这个操作在下次被接受“。


深度专题:NRC 0x78——唯一的“正-负混合响应”

在所有NRC中,0x78 RCRRP(Request Correctly Received, Response Pending)是独一无二的存在。从帧格式上看,它是负响应:0x7F + SID + 0x78。从语义上看,它传达的却是正向信息:“你的请求没有问题——我受理了——只是需要更多时间。

为什么不用正响应? 因为正响应一旦发出,就是最终结果。而0x78的正响应还没有产生——ECU需要时间——它不能谎报结果。所以它必须在“还没结果“时给诊断仪一个回应——而UDS里唯一的“未完成但正常“的信号通道就是NRC 0x78。

0x78触发的协议行为链:

诊断仪发送请求(假设是0x22读一个复杂DID)
  │
  ▼ P1定时器开始——诊断仪等待
  │
ECU收到请求 → 确认格式正确 → 确定需要额外处理时间
  │
  ▼ 在P2超时前返回
诊断仪 ← 0x7F 0x22 0x78
  │
  ▼ 诊断仪收到0x78 → 切换到P2*(增强型超时——典型值5000ms)
  │
ECU继续处理(例如:NvM异步读取未完成、等待传感器采集窗口)
  │
  ▼ 处理完成
诊断仪 ← 0x62 ...(真正的正响应)

0x78可以出现多次! 如果ECU在P2周期内仍无法完成,它可以继续发送第二个、第三个0x78——每次重置诊断仪的P2等待计时器。在固件刷写场景中,ECU可能持续发送数分钟的0x78直到刷写完成。

核心洞察:0x78是UDS协议设计者面对“异步计算现实“的妥协方案。在理想世界里,所有ECU都能在毫秒内完成任意DID的计算。在真实世界里,有些操作需要等待外部事件(NvM异步写入、CAN消息窗口、传感器数据就绪)。0x78让UDS支持异步处理而不破坏同步的请求-响应模型——这是一个优雅的hack,用负响应格式包装了一个正向的等待信号。


功能寻址时NRC的抑制规则——为什么要假装听不见

功能寻址(Functional Addressing)是诊断仪同时向总线上的所有ECU发送同一请求的能力。例如:诊断仪通过功能ID发送0x10 0x01(切换到默认会话)——所有ECU同时切换。这很高效——但也带来了一个问题:

如果50个ECU中有45个不支持某个SID——它们应该同时回复NRC 0x11吗?

如果它们都回复——CAN总线上会瞬间堆积45条负响应帧——造成总线拥塞和仲裁混乱。更致命的是:有些ECU在CAN ID的低位不同——它们会在不同的仲裁时隙中挤占总线——最终到达诊断仪的数据可能错乱。

解决方案:功能寻址时,某些NRC被静默抑制。 ECU收到功能寻址请求时,如果判断NRC属于特定类别,它不发送任何响应——假装没有收到。这不是bug——这是协议设计的刻意行为。

被抑制的NRC原因
0x11 (SNS)此ECU不支持该服务——但功能寻址的目标可能不包含此ECU——不回复
0x12 (SFNS)子功能不支持
0x31 (ROOR)DID/地址/例程不在此ECU中
0x7E (SFNSIAS)子功能不在当前会话下支持
0x7F (SNSIAS)服务不在当前会话下支持

哪些NRC在功能寻址时仍然发送? 那些“服务存在、但当前状态不适合执行“的错误:

  • 0x22 (CNC) — 我认识你要我做的手术——但我现在的状态不允许
  • 0x33 (SAD) — 我知道你想读的数据——但安全访问没解锁
  • 0x24 (RSE) — 请求序列错误——步骤没走对

这些NRC之所以保留——是因为它们告诉诊断仪:“不是ECU不支持——而是操作步骤需要调整。“这对诊断仪的自愈逻辑有价值。


从NRC到自愈——一个成熟诊断仪的内在机制

生产级别的诊断仪不会在收到NRC时就把控制权交还给操作员——它会根据NRC自动调整策略。以下是一个自愈引擎的简化工作流程:

收到负响应帧:
  │
  ├─ 0x11 (SNS) → 记录日志 → 永久标记此ECU不支持该服务 → 继续下一个任务
  │
  ├─ 0x7F (SNSIAS) → 提取该服务要求的会话类型 → 发送0x10切换会话 → 等待正响应 → 重试原请求
  │
  ├─ 0x7E (SFNSIAS) → 同上——但仅为子功能切换会话
  │
  ├─ 0x33 (SAD) → 提取该DID/操作要求的安全等级 → 发0x27 requestSeed → 计算密钥 → sendKey → 等待正响应 → 重试原请求
  │
  ├─ 0x35 (IK) → 重新请求种子 (0x27 requestSeed) → 重新计算密钥 → sendKey → 重试
  │
  ├─ 0x36 (ENOA) → 查询该ECU的延迟定时器时长 → 启动局部等待 → 到期后重试
  │
  ├─ 0x37 (RTDNE) → 等待剩余延迟时间 → 重试
  │
  ├─ 0x78 (RCRRP) → 切换到P2*等待 → 扩展接收窗口 → 等待最终响应
  │
  ├─ 0x24 (RSE) → 重置会话 → 重新建会话 → 重新解锁 → 重新执行整个操作序列
  │
  ├─ 0x22 (CNC) → 检查ECU当前状态 → 尝试调整条件 → 重试
  │                 (某些CNC需要人工判断——例如"发动机未运转"——诊断仪不能自动启动发动机)
  │
  └─ 0x10 (GR) → 无法自动恢复 → 记录完整上下文 → 提示操作员人工介入

这种自愈机制可以让诊断仪在后台自动完成“会话切换→解锁→重试“这条完整的恢复链——把几十毫秒的自动恢复变成无需人工介入的流畅过程。


本篇小结

  1. NRC不是装饰——它是UDS协议的自我纠正机制。每一个负响应都携带三层信息:“这是负响应(0x7F)” + “我拒绝了哪个请求(SID回声)” + “为什么(NRC)”。
  2. 0x78 RCRRP 是UDS中唯一的“正-负混合响应“——格式是负响应,语义是正向的“处理中,请等待“。它是诊断协议对ECU内部异步计算现实的妥协。
  3. 功能寻址时SNS/SFNS/ROOR/SFNSIAS/SNSIAS五类NRC被静默抑制——防止不支持某功能的ECU同时回复NRC淹没总线。仅保留“服务存在但条件不满足“类NRC(如0x22 CNC、0x33 SAD)。
  4. 生产级诊断仪依赖NRC自愈引擎——收到NRC不意味着“人工介入“——多数情况下诊断仪可以自动完成“会话切换→解锁→重试“的恢复链。
  5. NRC从0x11到0x93覆盖了从“服务不支持“到“换挡杆不在P档“的几乎每一种拒绝场景——它是对ECU内部状态的一个完整“原因反馈“机制。

【下集预告】: 你已经读完了ISO 14229-1:2013负响应码的完整体系——从NRC的报文格式、分类表、到诊断仪的自愈策略。下一节进入2.19周期读取(0x2A),继续探索UDS的更多服务。