news 2026/6/13 14:28:02

UDS 0x31服务:requestRoutineResults的“问诊”之道

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UDS 0x31服务:requestRoutineResults的“问诊”之道

朋友,在前面关于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配合使用的。它的典型使用模式是:

  1. 诊断仪发送startRoutine(0x31 0x01),请求ECU启动某个例程。
  2. ECU收到后立即返回肯定响应(0x71 0x01),表示例程已成功启动。但此时例程的核心逻辑可能还在执行中,结果是“待定”的。
  3. 诊断仪在之后的某个时刻发送requestRoutineResults(0x31 0x03),询问ECU:“刚才那个例程执行完了吗?结果是什么?”
  4. 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]
  • 31:UDS服务ID(RoutineControl)。
  • 03:子功能(requestRoutineResults)。
  • [RoutineIdentifier]:2字节的例程ID(如0xD101)。
3.2 肯定响应报文格式
肯定响应: 71 03 [RoutineIdentifier] [RoutineResults]
  • 71:肯定响应ID(31 + 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查询例程的执行状态,返回最终结果

关键点

  1. Start回调函数应该尽快返回。对于异步例程,Start回调函数只负责启动后台任务(如设置标志位、启动定时器、触发异步操作),然后立即返回E_OK。它不应该在回调函数内部等待例程完成。
  2. RequestResults回调函数负责返回结果。当诊断仪发送requestRoutineResults时,DCM调用RequestResults回调函数。该函数检查后台任务的执行状态,如果已完成,则返回E_OK并填充结果数据;如果还在执行中,则返回E_NOT_OK并设置NRC 0x22。
  3. 状态管理由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_progress=TRUE;g_health_check_start_time=GetCurrentTime();StartBackgroundTask(HEALTH_CHECK_TASK);/* 不返回最终结果,只确认启动 */*lenOut=0u;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){/* 例程未被启动过 */*err=0x22u;returnE_NOT_OK;}if(!g_health_check_completed){/* 例程还在执行中 */*err=0x22u;returnE_NOT_OK;}/* 例程已完成,返回结果 */*lenOut=8u;memcpy(DataOutVar,&g_health_check_result,8u);g_health_check_in_progress=FALSE;/* 清理状态 */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响应中返回结果 */*lenOut=4u;memcpy(DataOutVar,&sensor_value,4u);returnE_OK;}

第六章:真实案例——电池包健康状态估算例程

让我们用一个完整的真实案例,来展示requestRoutineResults在异步例程中的实际应用。

车型:某品牌纯电动SUV
ECU:电池管理系统(BMS)
例程0xD200(电池包健康状态SOH估算)

业务背景:电池包的SOH(State 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 (条件不正确)估算完成,SOH=92.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_progress=FALSE;staticbool g_soh_completed=FALSE;staticfloatg_soh_result=0.0f;Std_ReturnTypeAppl_Routine_SOHEstimate_Start(constuint8*DataInVar,uint8*DataOutVar,uint16 lenIn,uint16*lenOut,Dcm_NegativeResponseCodeType*err){if(g_soh_in_progress){*err=0x22u;/* 上一次估算还在进行中 */returnE_NOT_OK;}g_soh_in_progress=TRUE;g_soh_completed=FALSE;StartBackgroundTask(SOH_ESTIMATION_TASK);*lenOut=0u;returnE_OK;}Std_ReturnTypeAppl_Routine_SOHEstimate_RequestResults(constuint8*DataInVar,uint8*DataOutVar,uint16 lenIn,uint16*lenOut,Dcm_NegativeResponseCodeType*err){if(!g_soh_in_progress){*err=0x22u;/* 例程未被启动过 */returnE_NOT_OK;}if(!g_soh_completed){*err=0x22u;/* 例程还在执行中 */returnE_NOT_OK;}/* 例程已完成,返回SOH值 */*lenOut=4u;memcpy(DataOutVar,&g_soh_result,4u);/* 清理状态,为下一次估算做准备 */g_soh_in_progress=FALSE;g_soh_completed=FALSE;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响应直接携带结果,保持通信的简洁高效。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 14:27:50

OpenIM企业级IM系统架构深度解析与性能优化最佳实践

OpenIM企业级IM系统架构深度解析与性能优化最佳实践 【免费下载链接】open-im-server IM Chat OpenClaw 项目地址: https://gitcode.com/gh_mirrors/op/open-im-server OpenIM作为企业级开源即时通讯系统的技术标杆&#xff0c;其微服务架构设计、高可用性保障机制和性能…

作者头像 李华
网站建设 2026/6/13 14:26:52

Audio Router深度解析:Windows应用级音频路由的高级实现方案

Audio Router深度解析&#xff1a;Windows应用级音频路由的高级实现方案 【免费下载链接】audio-router Routes audio from programs to different audio devices. 项目地址: https://gitcode.com/gh_mirrors/au/audio-router Audio Router是一款专为Windows平台设计的开…

作者头像 李华
网站建设 2026/6/13 14:15:54

文档详细记录了嵌入式系统的底层技术参数,包含内存页表位域定义(如存在位、权限位等)、电源管理寄存器组地址、UDP协议栈配置(缓冲区大小49152端口起始)、闪存分区权限设置(/system区只读044

本文摘要&#xff1a;文档详细记录了嵌入式系统的底层技术参数&#xff0c;包含内存页表位域定义&#xff08;如存在位、权限位等&#xff09;、电源管理寄存器组地址、UDP协议栈配置&#xff08;缓冲区大小49152端口起始&#xff09;、闪存分区权限设置&#xff08;/system区只…

作者头像 李华
网站建设 2026/6/13 14:08:58

i.MX1 ARM9嵌入式处理器核心架构与驱动开发实战指南

1. 从手册到实战&#xff1a;i.MX1 ARM9嵌入式处理器核心架构深度剖析在嵌入式系统开发领域&#xff0c;尤其是早期的便携式智能设备&#xff0c;飞思卡尔&#xff08;现恩智浦&#xff09;的i.MX1系列处理器是一个绕不开的里程碑。手头这本厚厚的《MC9328MX1参考手册》第6.1版…

作者头像 李华