朋友在前面关于UDS诊断服务的系列探讨中我们深入了0x31例程控制的Start和Stop操作、否定响应码的判定顺序、DCM与SWC的回调机制。在这些讨论中0x31服务还有一个重要的操作类型我们没有专门展开——requestRoutineResults子功能0x03。你可能会问既然Start操作已经可以执行例程并返回结果了为什么还需要一个专门“请求结果”的操作它和Start返回的结果有什么不同在什么场景下应该使用它什么场景下不应该使用今天我们就来完整地拆解0x31服务中requestRoutineResults操作的设计理念、协议交互、适用场景和不适用场景让你彻底理解这个“问诊”操作的来龙去脉。第一章0x31服务的四种操作类型UDS 0x31服务RoutineControl例程控制定义了四种子功能每种都有特定的用途和交互模式。在深入requestRoutineResults之前我们先建立一个全局视图。子功能缩写请求码肯定响应码用途startRoutineStart0x010x71启动一个例程的执行stopRoutineStop0x020x71停止一个正在执行的例程requestRoutineResultsRequestResults0x030x71请求一个已启动例程的执行结果startRoutine stopRoutineStartStop0x01→0x020x71启动后立即停止较少使用0x31 例程控制的四种操作“例程执行中”“返回结果”“需要中断”“例程停止”“0x01 startRoutine启动例程执行”“0x02 stopRoutine停止例程执行”“0x03 requestRoutineResults请求例程执行结果”“0x01→0x02启动后立即停止”“例程完成”关键理解requestRoutineResults不是一个独立的操作它是与startRoutine配合使用的。它的典型使用模式是诊断仪发送startRoutine0x31 0x01请求ECU启动某个例程。ECU收到后立即返回肯定响应0x71 0x01表示例程已成功启动。但此时例程的核心逻辑可能还在执行中结果是“待定”的。诊断仪在之后的某个时刻发送requestRoutineResults0x31 0x03询问ECU“刚才那个例程执行完了吗结果是什么”ECU返回肯定响应0x71 0x03其中包含例程的最终执行结果。第二章为什么需要requestRoutineResults——Start响应的“时差”问题2.1 Start操作的两类例程在AUTOSAR CP平台中0x31例程的Start操作可以分为两类第一类同步例程执行时间短结果立即返回这类例程的核心逻辑在回调函数内部就完成了回调函数返回时例程的执行结果已经确定。因此Start操作的肯定响应中可以直接包含执行结果。示例读取一个传感器值、写入一个配置参数、执行一次内存自检。交互流程诊断仪 → ECU: 0x31 0x01 (Start例程) ECU → 诊断仪: 0x71 0x01 结果数据第二类异步例程执行时间长结果不能立即返回这类例程的核心逻辑需要较长时间才能完成例如需要等待硬件响应、需要多次通信交互或者例程启动后持续在后台运行直到被停止。在这种情况下Start操作只能确认“例程已启动”但无法立即提供最终结果。最终结果需要通过requestRoutineResults来获取。示例OTA固件完整性校验需要数分钟、电池包健康状态估算需要多轮充放电数据采集、执行器耐久测试需要运行数小时。交互流程诊断仪 → ECU: 0x31 0x01 (Start例程) ECU → 诊断仪: 0x71 0x01 (确认已启动但不含最终结果) ... 经过一段时间例程在后台执行... 诊断仪 → ECU: 0x31 0x03 (RequestResults) ECU → 诊断仪: 0x71 0x03 最终结果数据2.2 为什么Start响应不能总是携带结果你可能会问为什么不让所有例程的Start响应都携带结果这样不就不需要requestRoutineResults了吗原因一时间约束UDS通信有一个隐含的时间约束——诊断仪在发送请求后期望在较短时间内收到响应通常数百毫秒。如果例程的执行需要数分钟ECU不可能让诊断仪等待这么久。因此ECU必须先回复一个“已启动”的确认让诊断仪知道请求没有被忽略然后在例程执行完成后再通过requestRoutineResults返回结果。原因二状态管理有些例程启动后会持续运行如周期性数据采集直到被stopRoutine停止。这种例程没有“执行完成”的概念因此Start响应中不可能包含“最终结果”。但诊断仪仍然可能需要在例程运行过程中查询中间结果——这正是requestRoutineResults的用途。原因三诊断仪轮询机制requestRoutineResults为诊断仪提供了一种轮询机制。诊断仪可以在例程启动后周期性地发送requestRoutineResults检查例程是否已完成。如果ECU返回肯定响应0x71 0x03说明例程已完成并携带结果如果ECU返回否定响应如NRC 0x22条件不正确说明例程还在执行中。ECU诊断仪ECU诊断仪alt[例程还在执行][例程已完成]loop[轮询检查]0x31 0x01 (Start例程)0x71 0x01 (例程已启动)0x31 0x03 (RequestResults)7F 31 0x22 (条件不正确)0x71 0x03 结果数据第三章requestRoutineResults的协议交互3.1 请求报文格式请求: 31 03 [RoutineIdentifier]31UDS服务IDRoutineControl。03子功能requestRoutineResults。[RoutineIdentifier]2字节的例程ID如0xD101。3.2 肯定响应报文格式肯定响应: 71 03 [RoutineIdentifier] [RoutineResults]71肯定响应ID31 40 71。03回显子功能。[RoutineIdentifier]回显例程ID。[RoutineResults]例程执行结果数据格式由OEM定义。3.3 否定响应场景NRC场景说明0x12子功能不支持ECU不支持requestRoutineResults即该例程的所有结果都通过Start返回0x22条件不正确例程尚未启动、例程还在执行中、例程已完成但结果已过期0x31请求超出范围例程ID无效或不在当前会话允许范围第四章DCM中的配置与实现在AUTOSAR CP平台的DCM配置中如果一个例程支持requestRoutineResults操作需要为该例程配置三个回调函数操作类型回调函数职责StartAppl_Routine_Name_Start启动例程执行初始化返回“已启动”确认StopAppl_Routine_Name_Stop停止例程执行清理工作RequestResultsAppl_Routine_Name_RequestResults查询例程的执行状态返回最终结果关键点Start回调函数应该尽快返回。对于异步例程Start回调函数只负责启动后台任务如设置标志位、启动定时器、触发异步操作然后立即返回E_OK。它不应该在回调函数内部等待例程完成。RequestResults回调函数负责返回结果。当诊断仪发送requestRoutineResults时DCM调用RequestResults回调函数。该函数检查后台任务的执行状态如果已完成则返回E_OK并填充结果数据如果还在执行中则返回E_NOT_OK并设置NRC 0x22。状态管理由SWC负责。DCM只负责调用回调函数不维护例程的执行状态。SWC需要在回调函数之间保存状态如使用全局变量或NVRAM标志以便RequestResults回调函数能够判断例程是否已完成。第五章什么时候使用requestRoutineResults什么时候不使用这是本文的核心问题。以下是从工程实践角度总结的指导原则。5.1 适合使用requestRoutineResults的场景场景典型示例为什么需要长时间执行的例程OTA固件完整性校验、Flash擦除、电池包健康状态估算Start只能确认启动结果需要数分钟甚至数小时后才能返回需要阶段性结果执行器耐久测试、连续数据采集例程持续运行诊断仪需要周期性查询中间结果后台运行的监控例程发动机失火监控、传感器漂移检测例程启动后持续在后台运行直到被Stop操作停止异步硬件操作等待外部设备响应、等待网络通信完成硬件响应时间不可预测Start无法同步等待典型的Start回调函数实现异步模式Std_ReturnTypeAppl_Routine_HealthCheck_Start(constuint8*DataInVar,uint8*DataOutVar,uint16 lenIn,uint16*lenOut,Dcm_NegativeResponseCodeType*err){/* 启动后台健康检查任务立即返回 */g_health_check_in_progressTRUE;g_health_check_start_timeGetCurrentTime();StartBackgroundTask(HEALTH_CHECK_TASK);/* 不返回最终结果只确认启动 */*lenOut0u;returnE_OK;}典型的RequestResults回调函数实现Std_ReturnTypeAppl_Routine_HealthCheck_RequestResults(constuint8*DataInVar,uint8*DataOutVar,uint16 lenIn,uint16*lenOut,Dcm_NegativeResponseCodeType*err){if(!g_health_check_in_progress){/* 例程未被启动过 */*err0x22u;returnE_NOT_OK;}if(!g_health_check_completed){/* 例程还在执行中 */*err0x22u;returnE_NOT_OK;}/* 例程已完成返回结果 */*lenOut8u;memcpy(DataOutVar,g_health_check_result,8u);g_health_check_in_progressFALSE;/* 清理状态 */returnE_OK;}5.2 不适合使用requestRoutineResults的场景场景典型示例为什么不需要立即返回结果的简单操作读取传感器值、写入配置参数、执行一次内存自检Start回调函数内部就完成了所有工作结果可以直接在Start响应中返回仅触发动作的例程复位ECU、清除故障码、打开/关闭某个输出这些操作没有“结果”需要返回只需要确认“已执行”使用Start响应携带结果已足够大多数标准诊断操作如果例程在数百毫秒内完成没有必要引入额外的RequestResults轮询如果在不适合的场景中强行使用requestRoutineResults会带来以下问题增加通信复杂度诊断仪需要额外的RequestResults请求增加总线负载。增加SWC实现复杂度SWC需要管理异步状态即使是简单的立即返回操作。增加诊断仪的响应延迟诊断仪需要等待额外的轮询周期才能获取结果。不适合场景的Start回调函数实现同步模式Std_ReturnTypeAppl_Routine_ReadSensor_Start(constuint8*DataInVar,uint8*DataOutVar,uint16 lenIn,uint16*lenOut,Dcm_NegativeResponseCodeType*err){floatsensor_value;/* 读取传感器值立即完成 */Rte_Read_SensorValue(sensor_value);/* 直接在Start响应中返回结果 */*lenOut4u;memcpy(DataOutVar,sensor_value,4u);returnE_OK;}第六章真实案例——电池包健康状态估算例程让我们用一个完整的真实案例来展示requestRoutineResults在异步例程中的实际应用。车型某品牌纯电动SUVECU电池管理系统BMS例程0xD200电池包健康状态SOH估算业务背景电池包的SOHState of Health健康状态估算需要采集多轮充放电数据并进行复杂的数学模型运算整个过程耗时约30秒。诊断仪在启动估算后需要周期性查询估算是否完成并在完成后获取SOH值。DCM配置操作回调函数说明StartAppl_Routine_SOHEstimate_Start启动后台SOH估算任务RequestResultsAppl_Routine_SOHEstimate_RequestResults查询估算是否完成返回SOH值完整交互流程后台SOH估算任务BMS SWCDCM诊断仪后台SOH估算任务BMS SWCDCM诊断仪阶段1启动估算阶段2后台执行阶段3轮询结果alt[估算未完成]loop[每5秒查询一次]阶段4获取最终结果0x31 0x01 D2 00 (Start SOH估算)Appl_Routine_SOHEstimate_Start()g_soh_in_progress TRUE启动后台估算任务E_OK (不返回结果)0x71 0x01 D2 00 (已启动)采集充放电数据...运行估算模型...0x31 0x03 D2 00 (RequestResults)Appl_Routine_SOHEstimate_RequestResults()检查 g_soh_completedE_NOT_OK NRC 0x227F 31 22 (条件不正确)估算完成SOH92.5%g_soh_completed TRUE0x31 0x03 D2 00 (RequestResults)Appl_Routine_SOHEstimate_RequestResults()检测到 g_soh_completed TRUEDataOutVar 92.5%E_OK0x71 0x03 D2 00 92.5%SWC代码框架/* 全局状态变量用于Start和RequestResults之间传递状态 */staticbool g_soh_in_progressFALSE;staticbool g_soh_completedFALSE;staticfloatg_soh_result0.0f;Std_ReturnTypeAppl_Routine_SOHEstimate_Start(constuint8*DataInVar,uint8*DataOutVar,uint16 lenIn,uint16*lenOut,Dcm_NegativeResponseCodeType*err){if(g_soh_in_progress){*err0x22u;/* 上一次估算还在进行中 */returnE_NOT_OK;}g_soh_in_progressTRUE;g_soh_completedFALSE;StartBackgroundTask(SOH_ESTIMATION_TASK);*lenOut0u;returnE_OK;}Std_ReturnTypeAppl_Routine_SOHEstimate_RequestResults(constuint8*DataInVar,uint8*DataOutVar,uint16 lenIn,uint16*lenOut,Dcm_NegativeResponseCodeType*err){if(!g_soh_in_progress){*err0x22u;/* 例程未被启动过 */returnE_NOT_OK;}if(!g_soh_completed){*err0x22u;/* 例程还在执行中 */returnE_NOT_OK;}/* 例程已完成返回SOH值 */*lenOut4u;memcpy(DataOutVar,g_soh_result,4u);/* 清理状态为下一次估算做准备 */g_soh_in_progressFALSE;g_soh_completedFALSE;returnE_OK;}第七章常见误解与工程要点7.1 误解一“所有例程都需要配置requestRoutineResults”澄清只有异步执行的、需要较长时间才能完成的例程才需要。大多数简单的同步例程不需要它们的结果直接通过Start响应返回。7.2 误解二“RequestResults响应中的数据必须和Start响应不同”澄清没有这个规定。数据格式完全由OEM定义两者可以相同也可以不同。但在实践中Start响应通常只包含确认信息如“已启动”而RequestResults响应包含最终的业务数据。7.3 工程要点状态清理RequestResults回调函数在返回最终结果后应该清理内部状态如将g_soh_in_progress设置为FALSE以便下一次Start能够正常启动新的例程执行。否则第二次Start可能因为状态残留而被拒绝。7.4 工程要点并发控制如果同一个例程可能被多个诊断仪同时请求功能寻址SWC需要实现并发控制确保同一时刻只有一个例程实例在运行。这通常通过互斥锁或状态标志来实现。第八章总结朋友通过今天的深度解析我们完整地走过了requestRoutineResults的设计理念、协议交互、配置实现和适用场景。维度总结本质requestRoutineResults是0x31服务中用于获取异步例程最终结果的操作与Start的关系Start负责启动RequestResults负责查询结果两者配合使用适用场景长时间执行的例程、需要阶段性结果的例程、后台运行的监控例程不适用场景立即返回结果的简单操作、仅触发动作的例程、Start响应已足够的情况DCM配置需要为每个操作类型配置独立的回调函数Start/Stop/RequestResults状态管理SWC负责在回调函数之间维护例程的执行状态requestRoutineResults是UDS诊断协议中一个精巧的设计。它解决了“异步执行”与“同步通信”之间的矛盾——例程可以在后台慢慢执行而诊断仪可以通过周期性的轮询来获取最终结果。这种设计让诊断仪既能触发复杂的长时间操作又不必一直占用总线等待响应。下一次当你在DCM配置工具中为一个例程勾选“RequestResults”时你可以清晰地判断这个例程是否真的需要异步执行它的执行结果是否无法在Start响应中立即返回如果是那么requestRoutineResults就是你正确的选择如果不是那就让Start响应直接携带结果保持通信的简洁高效。