2.13 例行控制(0x31)——执行治疗
当你需要的不是改一个值,而是执行一整套操作
你坐在诊断仪面前,面前是一辆刹车踏板感觉偏软的轿车。你怀疑ABS液压单元内部有一个蓄压阀卡住了——但你不能拆开液压单元去看。你需要让ECU自己执行一套自动化检测流程:泵加压→保持压力→测量泄压速率→释放→回报结果。
这套流程有十几个步骤,每步都涉及ECU内部的多个寄存器操作——微控制器要切换阀体作动器、轮速传感器要监测每一个轮子的转速变化、压力传感器要连续采样并做峰值保持——整个过程持续数秒。
你不能用0x2F来做这件事——IO控制只能让你临时替代一个信号值,但它不能执行时间维度上展开的操作序列。你需要的不只是“把EGR阀开到30%“——你需要“给我执行ABS泵自检这整段程序”。
0x31 RoutineControl就是UDS对“程序化诊断操作“的回答。
为什么需要三阶段:start/stop/requestResults
你可能会觉得——不就一个“执行例程“的SID吗?为什么分三个子功能?
因为例程和简单的参数读写不同——它有两种属性:异步和有持续时间。
| 例程特征 | 说明 | 现实案例 |
|---|---|---|
| 异步 | 启动后不会在当前MainFunction周期结束——需要多个ECU任务周期甚至多个驾驶循环才完成 | ADAS摄像头的在线标定——需要车以特定速度行驶在标线清晰的路面上持续数分钟 |
| 有中间状态 | 执行过程中可以取中间信息和进度 | ABS自检——压力建立、保压泄漏检测、压力释放状态的百分比进度 |
| 可被中止 | 诊断仪可能在执行期间改变主意——需要提前终止 | “紧急停止——不要继续这个例程——安全条件不足” |
这三种属性映射到三个子功能:
| 子功能 | 含义 | 等价操作 |
|---|---|---|
| 0x01 startRoutine | 启动程序 | “开始执行RID=0x0201的操作” |
| 0x02 stopRoutine | 终止程序 | “立即停止RID=0x0201的例程” |
| 0x03 requestRoutineResults | 请求中间结果 | “RID=0x0201自检目前到什么地步了?” |
核心洞察:0x31三阶段的子功能不是协议设计的审美偏好——它们是异步操作标准模型的直接翻译。启动一个不可预测时长的操作→随时询问它的状态→必要时中途终止——这三个动作覆盖了所有有状态操作的生命周期。
请求与响应格式
请求(startRoutine):
Byte 0: 0x31 (SID)
Byte 1: SubFunction (bit7=suppressPosRsp, bit6-0=0x01/0x02/0x03)
Byte 2-3: routineIdentifier (RID, 2字节大端序)
Byte 4..N: routineControlOptionRecord[] (可选输入参数)
RID(Routine Identifier)是一个2字节的数字——标识ECU上注册的诊断程序。ECU的配置表里存着RID→处理函数的映射。
正响应(startRoutine):
Byte 0: 0x71 (SID+0x40)
Byte 1: SubFunction回声
Byte 2-3: routineIdentifier回声
Byte 4..N: routineInfo[] / routineStatusRecord (输出)
routineInfo中包含routineStatus——一个字节编码执行状态:
| routineStatus值 | 含义 |
|---|---|
| 0x01 | 例程执行完毕 |
| 0x02 | 例程正在执行中 |
| 0x04 | 用户手动终止 |
| 0x05 | 例程执行失败——错误 |
| 0x06 | 例程因安全策略被终止 |
| 0x07 | 例程因依赖条件不满足被终止 |
完整的ABS自检用例——从启动到取结果
诊断仪 → ECU:
0x31 0x01 0x02 0x01
(startRoutine, RID=0x0201——ABS泵自检)
ECU → 诊断仪:
0x71 0x01 0x02 0x01 0x01 0x00
(正响应: routineStatus=0x01——已完成, 无错误)
附加参数[0x00] = 自检通过码
---
如果例程执行需要时间——ECU先返回responsePending(0x78):
ECU → 诊断仪:
0x7F 0x31 0x78
(responsePending——还在跑, 诊断仪等待P2*时间)
... 几秒后...
ECU → 诊断仪:
0x71 0x01 0x02 0x01 0x01 0x00 0x01 0x23 0x45...
(最终响应: status=0x01完成, 附加泵压力曲线数据)
requestRoutineResults——取中间进度
如果例程运行时诊断仪想获取当前进度,它在任何时间都可以发:
诊断仪 → ECU: 0x31 0x03 0x02 0x01 (requestRoutineResults, RID=0x0201)
ECU → 诊断仪:
0x71 0x03 0x02 0x01 0x02 0x3C
(routineStatus=0x02还在跑, progressPercentage=0x3C=60%)
progressPercentage等中间结果字段由RID的配置表定义——ECU供应商在设计每个RID时规定了返回数据中哪些字节表示进度、哪些表示中间测量值。诊断仪通过ODX或诊断描述文件获知这些字段的布局。
核心洞察:requestRoutineResults是0x31对异步操作的“心跳窗口“——诊断仪不需要阻塞式等待例程执行完毕。它可以随时问“到哪了?“——ECU返回当前的进度数据和中间计算值。这在长时间运行的ADAS标定中尤其重要——你不想等10分钟再看结果。
stopRoutine——让程序提前终止
如果你发现某个例行程序在不安全的状态下运行,你需要强制终止它:
诊断仪 → ECU: 0x31 0x02 0x02 0x01 (stopRoutine, RID=0x0201)
ECU → 诊断仪: 0x71 0x02 0x02 0x01 0x04
(routineStatus=0x04——用户手动终止)
为什么RoutineControl在默认会话不可用
0x31在标准层面并不限制会话——但大多数OEM将其配置为仅非默认会话(扩展会话0x03或编程会话0x02)可用。某些需要安全等级支持的例程还额外要求0x27解锁。这是因为例行程序可以是破坏性的——让ABS泵持续加压可能损坏液压密封——不能让任何插上OBD口的设备随便触发。如果你在默认会话下发0x31——ECU回复NRC 0x7F(serviceNotSupportedInActiveSession)。
和0x2F IO控制的根本区别
0x2F是即时操作——把当前信号值改成X——0ms延迟、马上生效、完成后立即交还。
0x31是异步操作——“开始一个任务”——没有预期完成时间、可以在过程中取进度、可以中途停止。
两者面对的两种不同的诊断场景:
- “这个EGR阀动作有没有延迟?” → 0x2F——把它开30%,看看反馈传感器多久响应
- “这个ABS泵有没有内部泄漏?” → 0x31——执行一整套自检程序,需要10秒、每个步骤都有压力和轮速的采集
本篇小结
- 0x31 RoutineControl是UDS的程序化诊断操作接口——用RID标识要执行的程序。
- 三种子功能(start/stop/requestResults)构成了异步操作的标准模型——启动、中止、查询进度。
- routineStatus字节返回了一个全面的执行状态——完成(0x01)、运行中(0x02)、用户终止(0x04)、失败(0x05)……
- requestRoutineResults是长时间运行程序的关键辅助——让诊断仪在程序执行期间取中间信息和进度。
- 0x31和0x2F的不同——前者异步、有状态、可中断的程序级操作;后者同步、瞬时的参数更换。
【下集预告】:0x31执行的是程序——0x2F是即时操作。你把这个信号值设为X——系统会怎么反应?就像医生按住病人的腹部问’按这里疼不疼’——你主动干扰,观察系统的被动反馈。