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

1.5 思想实验——给一个ECU挂一次号

你就是诊断仪

你现在不是人类了。你是一台诊断仪。

你有一个OBD插座、一根诊断线、和一套存储在ROM里的UDS协议栈。你面前是一辆发动机未启动、但钥匙已拧到ON档的车——全车通电,几十个ECU在CAN总线上安静地等待着谁先开口说第一句话。

你是那个先开口的。

你手里的工具不是扳手、不是万用表、不是示波器。你唯一的工具是——用ISO 14229定义的语法,向每一个ECU发问,然后根据回答决定下一步该做什么。

下面是一整趟完整的诊断之旅。跟着我,一步一步走。


第一步:挂号——选择病人,建立诊断会话

你不能对着整辆车所有ECU一起喊“把你们的故障码都报出来“——那会把CAN总线瞬间塞满。你需要点名一个具体的ECU。

你用物理寻址——CAN帧ID里编码了目标ECU的地址。只有你点名的那个会应答。其他的ECU默默地听着,不吭声。

你发出第一个请求:

0x10 0x03
SID=0x10(诊断会话控制)  Subfunction=0x03(扩展诊断会话)

ECU收到了。它检查自己的会话配置表——0x03是扩展诊断会话,允许进入。于是它回话:

0x50 0x03 0x00 0x32 0x00 0xFA
SID+0x40=0x50  会话类型0x03  P2Server_max=50ms  P2*Server_max=250ms

这一步在医院里叫挂号。你告诉ECU“我要进入诊断模式“,ECU回复“好的,你挂的是扩展诊断科——之后你问我任何问题,我都在50毫秒以内回复;如果问题处理时间更长,我需要超过250毫秒,我会先给你发一句’等着’(这个’等着’机制在UDS中叫NRC 0x78)“。

为什么扩展诊断会话? 如果你留在默认会话(0x01),你能读故障码也能读一些非敏感的DID——但你不能写任何东西、不能执行主动控制、不能请求安全访问。默认会话是一个只读的公共空间——任何人接上诊断仪都能进入。扩展会话是一个需授权才能进入的诊察区——你有了,就可以准备做更进一步的动作了。

但你还不能“开药“。你现在只是坐在诊察室里——还没有权限翻看某些敏感的病历页(比如安全气囊的配置参数、转向角传感器的零点标定值)。要获得这些权限,你需要:


第二步:病历授权——安全访问

你向ECU发出:

0x27 0x01
SID=0x27(安全访问)  Subfunction=0x01(请求种子——第1级安全等级)

ECU回复:

0x67 0x01 0xA3 0x7F 0x2B 0x91
SID+0x40=0x67  level=0x01  seed[4]=A3 7F 2B 91

4字节的随机种子。你手里有一把密钥——你在ROM里存着和ECU共享的密钥计算算法。你把种子喂进算法,算出密钥:

Key = f(0xA37F2B91) → 0x3E 0xD5 0x8A 0x02

你发出第二条安全访问请求:

0x27 0x02 0x3E 0xD5 0x8A 0x02
SID=0x27  Subfunction=0x02(发送密钥——对应level 1的密钥)  Key[4]

ECU自己也在内部算了一遍。两者匹配。它回话:

0x67 0x02
SID+0x40=0x67  level=0x02(密钥通过)

现在ECU的安全等级已解锁到Level 1。这一步在医院里是“获取病历权限“——你不是路人,你是授权医生。你可以查所有常规检查结果(DID),也可以执行诊断治疗(例行程序),但不能给病人做手术(刷写固件需要更高级别的安全访问或者进入编程会话)。

这一步的设计有那么多细节——为什么requestSeed(0x01)和sendKey(0x02)是相隔1而不是任意偏移?为什么不同的安全等级(0x01/0x03/0x05)共享同一个0x27 SID而不是各自独立的SID?这些谜底留到第二章第6节去解开。


第三步:问诊——读DTC

你挂好号、授权完、先不急着开药。你得先了解病史。你问ECU——你有多少confirmed(已经确认的、真正存在的、不是偶发信号的)故障码?

0x19 0x01 0x09
SID=0x19(读DTC信息)  子功能0x01(按状态掩码报告数量)  DTCStatusMask=0x09

掩码0x09 = bit3(confirmedDTC) + bit0(testFailed) = 当前确认且刚刚检测到失败的DTC。

ECU查了查它的内部DTC表,回复:

0x59 0x01 0x09 0x0F 0x00 0x03
SID+0x40=0x59  子功能0x01  statusAvailabilityMask=0x09
DTCFormatIdentifier=0x0F(ISO 14229-1三字节格式)  DTCCount=3个

3个confirmed故障。你接着问——都是哪三个?

0x19 0x02 0x09
子功能0x02(按状态掩码报告DTC列表)  DTCStatusMask=0x09

ECU回复:

0x59 0x02 0x09
  DTC#1: 0x01 0x02 0x03  status=0x09  → P0102(MAF传感器电路低电压)
  DTC#2: 0x01 0x03 0x05  status=0x09  → P0305(5号缸检测到失火)
  DTC#3: 0x04 0x02 0x01  status=0x09  → C0421(ABS泵电机故障)

你有三个症状。但“失火“这个诊断描述不够——发症的当时是什么状况?车速多少?发动机转速多少?进气温度多少?你需要冻结帧(Freeze Frame)——DTC发生那一刻ECU自动记录下的运行参数快照。

你锁定P0305:

0x19 0x04 0x01 0x03 0x05 0xFF
子功能0x04(按DTC号报告快照)  DTC=0x010305  recordNumber=0xFF(全部快照记录)

ECU返回:

0x59 0x04 0x01 0x03 0x05 0x09 0x02
  recordNumber=0x01  snapshotData[0..n]...
    VehicleSpeed=87 km/h
    EngineSpeed=2450 rpm
    CoolantTemp=92°C
    LTFT=+3.1%
    EngineLoad=62%
    TimeSinceEngineStart=174 seconds

这告诉你——P0305是车辆在高速巡航时(87km/h、中负荷62%)发生的。水温92°C是正常水温——不是冷启动的失火——是因为5号缸在持续负荷下掉了点火或喷油。你已经大致知道病因和病发情景了。这就是冻结帧的意义——不只知道“得了什么病“,还知道“什么时候因什么诱因发病“。


第四步:化验——读DID

DTC告诉你出了问题。但要确诊,需要实时数据。你怀疑MAF传感器可能读数偏了——于是你直接读当前MAF值:

0x22 0x01 0x10
SID=0x22(按ID读数据)  DID=0x0110(MAF空气流率, 0~655.35 g/s, 分辨率0.01)

ECU回复:

0x62 0x01 0x10 0x00 0xAA
SID+0x40=0x62  DID=0x0110  data=0x00AA=170 → 1.70 g/s

发动机怠速(750rpm)时1.70g/s的MAF读数——以这辆2.0L发动机来说偏低了。正常值应该在2.0~2.5g/s左右。但你还需要确认是在哪个工况下偏低的——于是你同时请求三个DID(如果ECU支持多DID读取):

0x22 0x01 0x10 0x01 0x0C 0x01 0x11
DID=0x0110(MAF) + DID=0x010C(发动机转速) + DID=0x0111(节气门绝对位置)

ECU回复三个数据块绑在一起:

0x62 0x01 0x10 0x00 0xAA    0x01 0x0C 0x0B 0xB8    0x01 0x11 0x1A
 └──DID=0x0110──┘  MAF=170   └──DID=0x010C──┘ RPM=752 └DID=0x0111┘ Throttle=11.2%

这一步在医院里是开化验单。“血糖多少”→读DID 0x0110血糖值。“血压多少”→读DID 0x0112血压值。你可以同时要十几项结果——ECU在一个响应帧里一次全返。DID的核心价值不是“返回一个数值“——是返回一个语义标签对应的数值。你不说“给我地址0x40024000“,你说“给我MAF传感器值“——ECU知道去哪找。


第五步:触诊——IO控制

确诊需要验证。你怀疑MAF传感器的信号线可能有间歇性短路——但你不可能用万用表去测量ECU内部的ADC采样值。你需要让ECU执行一个动作,同时观察这个动作的响应。

0x2F 0x01 0x10 0x03 0x0B 0xB8
SID=0x2F(IO控制)  DID=0x0110(MAF空气流率)
IOControlParameter=0x03(短期替代)  强制把MAF读数值写入0x0BB8=3000即30.00 g/s

ECU执行替换——它照着你设的值欺骗了自己的控制逻辑——然后回复:

0x6F 0x01 0x10 0x03 0x0B 0xB8
SID+0x40=0x6F  DID=0x0110  控制参数0x03  当前控制状态=你设的30.00 g/s

你接下来观察发动机的短期燃油修正(STFT)变化——如果ECU的控制环在看到“MAF=30.00 g/s“时正确地增加了喷油量,那就说明MAF信号链在“读到假值“的情形下下游逻辑是正常的——这帮助你排除了“可能是进气歧管漏气“的选项。而如果ECU没反应,那就是MAF芯片本身有硬件故障——因为软件路径是通的。

完成后,你把控制权交还给ECU:

0x2F 0x01 0x10 0x00
IOControlParameter=0x00(交还ECU控制)

这一步在医院里是触诊。医生不是只问你“这里疼不疼“——他还要按一下、敲一下、让你“深呼吸,再深一点“——观察你的主动反应。IO控制的本质就是对ECU说:“在你这边,把这个值暂时假改成X——然后告诉我发生了什么。


第六步:治疗——例行控制和下载

你已经确诊了三个问题:

  1. MAF传感器读数偏低→可能是传感器本身脏污
  2. ABS泵电机报故障C0421→可能是泵体卡滞,需要自检程序重新激活
  3. 你的ECU软件版本太旧——有一版标定把5号缸的失火检测灵敏度提高了

针对#2——你执行ABS泵的例行自检程序。这个程序的routineID是0x0201:

0x31 0x01 0x02 0x01
SID=0x31(例行控制)  子功能0x01(开始例行程序)  routineID=0x0201

ECU启动泵电机自检——泵转2秒→建立压力→检测压力保持→泄压→回复:

0x71 0x01 0x02 0x01 0x01
SID+0x40=0x71  子功能0x01  routineID=0x0201  routineStatus=0x01(完成)

自检通过——泵体没有卡滞。大概率是上次掉电时有状态卡在“故障“状态,现在自检清除之后标志位恢复了。

针对#3——你需要给ECU刷入新的固件。这是一个手术级别的操作。你得先切换到编程会话(0x02),然后解锁更高级的安全等级——之后才是下载序列。

但刷写流程我留到第二章第16节完整展开。这里你只需知道概要:编程会话→安全访问→请求下载→传输数据(循环多次)→传输终止→ECU复位→切回默认会话。那是UDS里最复杂的一条服务链,其逻辑严密程度不亚于一套分布式系统的两阶段提交。


第七步:结账离开——回到默认会话、清除上下文

诊断完成。新固件刷入。你要做最后一件事——清除DTC并释放诊断会话。

0x14 0xFF 0xFF 0xFF
SID=0x14(清除诊断信息)  groupOfDTC=0xFFFFFF(全部组)

ECU回复:

0x54
SID+0x40=0x54  →  清除完毕

然后你不再发送保活。ECU的S3会话超时倒计时归零后自动切回默认会话——默认会话不需要保活。安全等级自动回锁。

诊断会话生命周期——完整结束。

这一步在医院里是结账、退号、病历归档。已修好的问题关掉症状条目,诊断会话关闭让ECU回归正常运行状态。


复盘:一条完整的诊断工作流映射

回过头看,你刚才执行的这七个步骤不是一个随机的SID序列——它遵循的正是两千年前中医那套望闻问切:

诊断阶段中医映射UDS服务你在做的事
挂号0x10 诊断会话控制声明身份、确定权限范围
授权0x27 安全访问证明你有权做下一步
望+闻被动收集0x19 读DTCECU自报告的症状
主动问询0x22 读DID主动拉取实时体征数据
触诊探查0x2F IO控制主动干扰并观察系统反应
针灸针刺治疗0x31 例行控制执行程序化治疗操作
手术结构改变0x34/36/37 固件下载永久改变ECU的软件
归档0x14 清除DTC记录已修复、复位状态
结账S3超时→切默认释放诊断资源

核心洞察:这不是巧合。所有复杂系统的诊断——人体、发动机、计算机网络——都共享同一种认知框架:先建立身份和权限,再收集数据,再主动探查,再执行干预。 UDS和中医共享的不是具体的工具——是思维方式。不同之处在于:中医用银针、脉诊和汤药,你用SID、DID和CAN帧。而最根本的哲学追问是——你怎么知道一个不透明的系统里面到底发生了什么?


本篇小结

  1. 一次完整的UDS诊断工作流遵循“建立会话→授权→读DTC→读DID→主动测试→执行干预→清除DTC→释放会话“的固定顺序。这个顺序不是协议规定的语法规则——而是诊断认知逻辑的自然展开。
  2. DID的核心价值是语义化寻址——你说“给我发动机转速“,而不是“给我地址0x40024000“——这让诊断仪可以适配不同硬件平台。
  3. 0x27 SecurityAccess的Seed/Key不是密码学炫技——它是一个在有限ECU算力约束下确保只有授权设备才能执行敏感操作的门禁系统。
  4. 0x31 RoutineControl和0x2F IOControl的地位在UDS中是平行的——它们都是主动干预手段,前者适用于程序化流程(几秒到几分钟的自动操作序列),后者适用于直接的瞬时参数修改。
  5. 诊断和医学在认知框架上的同构不是比喻——它们面对的是同一个根本问题:不透明系统的内部状态推断

【下集预告】:恭喜——你已经挂过一次完整的号了。现在你知道诊断工作流的全貌、知道每一组SID的定位和相互作用。接下来我们要把每一个SID拆开,从报文格式到子功能码,从正响应编码到每一种负响应码的精确触发条件。一句话:我们要读ISO 14229了——不是那种从头啃到尾的死读,而是把标准里的每一个设计决策还原成它背后的诊断意图。你准备好了吗?