ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

UDS 0x28通信控制服务测试用例设计:从协议原理到CANoe落地

UDS 0x28通信控制服务测试用例设计:从协议原理到CANoe落地 在车载网络诊断测试中0x28服务是每个测试工程师迟早要面对的服务之一。它的请求格式看起来非常简单一个SID、一个子功能、一个通信类型参数总共只有三个字节。但真正动手设计用例时很多人会卡住0x28服务到底要测哪些子功能通信类型怎么组合关闭通信之后还能不能收到诊断响应这些问题需求文档里往往不会直接写出答案。这篇文章不打算只讲ISO 14229里的协议定义而是要解决一个更实际的问题当需求文档中只有一句“通过0x28服务控制ECU通信”时如何一步步把它拆成可执行、可追溯、能覆盖正常和异常路径的测试用例。同时我会给出可直接运行的Python发送示例、CANoe/CAPL校验代码和用例模板方便在总线测试环境中直接落地。读完这篇文章你会得到三样东西一是0x28服务协议层的完整认知二是从需求到用例的分析方法三是可以照抄的用例框架和代码框架。1. 网络诊断测试中0x28服务为什么值得单独设计用例很多UDS服务属于“读一次、写一次”的类型比如0x22读取数据、0x2E写入数据测试重点通常集中在参数取值和响应码上。但0x28服务完全不一样它是一个改变ECU通信状态的操作型服务。测试工程师发出一个0x28请求ECU可能从此不再发送应用报文也可能不再接收总线上其他节点的网络管理报文。影响范围小到单个ECU的通信行为大到整个总线网络的管理状态。正因为影响面大整车厂和Tier 1的需求文档通常会对0x28服务做严格约束在哪个会话下允许调用、是否需要安全访问解锁、通信类型支持哪些、通信关闭后如何恢复。如果测试用例只做到“请求发出去、正响应收到”这个用例的验证价值非常低。真正要验证的是ECU的通信行为是否真的按预期改变了以及改变之后能不能恢复。另一个常见误区是把0x28服务理解成“把ECU所有报文都停掉”。实际工程中0x28服务控制的是应用层通信和网络管理通信物理寻址的诊断报文比如0x7E0/0x7E8通常不会被关闭否则ECU根本没有办法再次接受恢复指令。这一点如果不提前在需求里确认测试时很容易把台架搞到“死机”状态只能通过断电复位恢复。所以0x28服务的用例设计本质上不是“协议用例”而是“网络状态用例”。它要求测试工程师同时理解UDS协议、通信状态、诊断会话、安全访问和网络管理行为这也是为什么值得单独写一篇文章来拆解。2. 0x28服务核心技术原理2.1 服务定义与请求格式0x28服务在UDS标准中的全称是CommunicationControl中文通常翻译为“通信控制”。它由诊断仪发送给ECU用来开启或关闭指定的通信类型。请求格式如下字节参数名称取值说明Byte 1SID固定为0x28Byte 2Sub-function0x00 ~ 0x03高bit为抑制正响应位Byte 3Communication type0x01 ~ 0xFF具体支持范围看需求子功能的定义如下子功能名称作用0x00enableRxAndTx开启接收和发送0x01enableRxAndDisableTx开启接收关闭发送0x02disableRxAndEnableTx关闭接收开启发送0x03disableRxAndTx关闭接收和发送通信类型的定义如下取值含义0x01Normal communication即正常应用报文0x02NM communication即网络管理报文0x03Normal and NM communication即应用报文和网络管理报文都控制0x04 ~ 0xFFISO标准中为保留值部分整车规范会自定义为“所有通信类型”这里要特别提一下Sub-function的高bit。UDS标准中Sub-function参数的最高位是SuppressPosRspMsgIndicationBit也就是“抑制正响应位”。如果发送0x80、0x81、0x82、0x83ECU会被要求不要返回正响应。很多测试工程师第一次遇到“发0x80收不到任何响应”时第一反应是总线出问题了实际上这是协议行为。2.2 响应格式与NRC0x28服务正响应格式为字节取值Byte 10x68Byte 2子功能负响应格式与所有UDS服务一致字节取值Byte 10x7FByte 20x28Byte 3NRC常见NRC如下NRC含义出现场景0x12SubFunctionNotSupported发送了不支持的子功能比如0x040x13IncorrectMessageLengthOrInvalidFormat请求长度不是3字节或格式错误0x22ConditionsNotCorrect当前条件不满足比如安全访问未解锁、网络状态不允许0x31RequestOutOfRange通信类型取值越界或保留值不被支持0x7ESubFunctionNotSupportedInActiveSession当前会话不允许该子功能0x7FServiceNotSupportedInActiveSession当前会话不允许调用0x28服务NRC的具体使用取决于ECU软件实现和整车需求不同项目之间可能存在差异。设计用例时不能只写“返回负响应”还要写清楚在哪个前置条件下返回哪个NRC。2.3 0x28服务与诊断通信的关系这是0x28服务最容易误解的地方。ISO 14229中0x28服务的通信类型指的是“应用通信”和“网络管理通信”而不是“诊断通信”。诊断请求和诊断响应走的是诊断报文通常在物理寻址或功能寻址通道上传输。因此0x28服务关闭了Normal communication之后诊断仪通过物理寻址继续发送0x22、0x3E等服务ECU仍然可以正常响应。这个设计非常关键如果0x28把诊断通信也关闭了ECU将永远无法接收恢复通信的指令只能通过整车上电下电来恢复这在生产测试和售后诊断中都是不可接受的。设计用例时必须把“诊断通信不受影响”作为一条显式断言写进去而不是默认它就是这样的。3. 从“需求”到“用例”设计方法论3.1 需求分析切入点拿到需求文档后不要急着写用例。先做三件事第一提取明确功能。需求里通常会说“ECU应支持0x28服务”“支持子功能0x00、0x01、0x03”“支持通信类型0x01、0x02、0x03”。这部分是正向用例的直接来源。第二识别隐含约束。比如需求是否要求必须在扩展诊断会话下调用是否需要通过0x27安全访问关闭通信后是否允许立即恢复这些约束往往藏在“条件”两个字里不细读需求很可能漏掉。第三反向找出未定义范围。比如需求没有写0x04通信类型怎么处理没有写子功能0x02是否支持。这些未定义点就是边界用例和异常用例的重点。需求文档常见的坑是只写“支持0x28服务”但没有写清楚子功能集合、通信类型集合、会话条件、安全条件和恢复方式。碰到这种情况测试工程师应该在需求评审阶段就把问题提出来而不是等到用例评审时和数据交互。3.2 用例设计方法与覆盖维度针对0x28服务最实用的测试设计方法有四种等价类划分法。把子功能和通信类型分成合法类和非法类。合法类是需求明确支持的所有组合非法类是保留值、越界值和不支持的子功能。边界值分析。0x28服务虽然只有三个字节但边界值很多通信类型取0x00、0x01、0x03、0xFF子功能取0x03、0x04、0x7F、0x80都是典型的边界场景。状态迁移法。0x28服务本质上是状态切换服务通信状态在“正常”和“关闭”之间迁移。状态迁移法最适合设计“关闭→恢复”“连续关闭两次”“恢复后再关闭”这类时序用例。错误推断法。根据协议经验和常见实现主动构造缺字节、多字节、保留子功能、保留通信类型的请求验证ECU能否正确返回NRC。用例的覆盖维度可以整理成一个矩阵子功能 × 通信类型 × 诊断会话 × 安全状态 × 通信状态。测试工程师可以先用矩阵把全部组合列出来再根据需求支持范围筛掉无效组合剩下的每一项都是一条用例。4. 一份示例需求与用例分解为了把上面的方法论落到具体场景我写一份示例需求。这个需求的结构和大多数整车网络诊断需求类似实际项目以你们需求文档为准。R-NetDiag-0x28-01ECU应支持0x28服务。 R-NetDiag-0x28-02ECU支持子功能0x00、0x01、0x03。 R-NetDiag-0x28-03ECU支持通信类型0x01、0x02、0x03。 R-NetDiag-0x28-04仅允许在扩展诊断会话下执行0x28服务。 R-NetDiag-0x28-05执行0x28服务前需通过0x27安全访问解锁。 R-NetDiag-0x28-060x28服务不得关闭物理寻址诊断报文。 R-NetDiag-0x28-07通信关闭后诊断仪可发送0x28 0x00恢复通信。4.1 正常功能用例正常功能用例的核心目标是证明“在合法条件下0x28服务能完成需求描述的功能”。用例ID前置条件测试步骤预期结果TC_0x28_001扩展会话安全解锁通信正常发送0x28 0x00 0x01正响应0x68 0x00Normal通信保持或恢复TC_0x28_002扩展会话安全解锁通信正常发送0x28 0x03 0x01正响应0x68 0x03Normal收发停止诊断响应正常TC_0x28_003扩展会话安全解锁通信正常发送0x28 0x01 0x02正响应0x68 0x01NM发送停止NM接收保持TC_0x28_004扩展会话安全解锁通信已关闭发送0x28 0x00 0x01正响应0x68 0x00通信恢复TC_0x28_003里有一个容易被忽略的断言子功能0x01是“enableRxAndDisableTx”意思是接收保持开启但发送被关闭。很多测试用例只检查“响应是否正确”不检查“行为是否正确”。对于0x28服务行为断言比响应断言更重要。4.2 异常与边界用例异常用例的目标是验证ECU对非法请求、越界参数和格式错误的处理。这类用例在测试报告中非常能说明问题因为负响应码才是真正考验协议栈实现质量的地方。用例ID前置条件测试步骤预期结果TC_0x28_101扩展会话安全解锁发送0x28 0x03 0x00负响应0x7F 0x28 0x31TC_0x28_102扩展会话安全解锁发送0x28 0x03 0xFF负响应0x7F 0x28 0x31或按需求返回其他NRCTC_0x28_103扩展会话安全解锁发送0x28 0x04 0x01负响应0x7F 0x28 0x12TC_0x28_104扩展会话安全解锁发送0x00 0x01负响应0x7F 0x28 0x13TC_0x28_105扩展会话安全解锁发送0x28 0x00 0x01 0xAA负响应0x7F 0x28 0x13TC_0x28_106默认会话发送0x28 0x03 0x01负响应0x7F 0x28 0x7E具体以需求为准这里要说明TC_0x28_101里的0x00通信类型在ISO标准中是保留值多数ECU会返回0x31但也有ECU把0x00当成本地通信类型处理。所以预期结果要对照需求文档最终确认不要直接在用例库里写死。4.3 状态与时序用例0x28服务测试最容易出bug的领域是状态和时序。通信关闭后ECU会进入一个“禁止发送”的状态这个状态下总线上的其他节点很可能因为收不到周期报文而开始报DTC。这时候再来一条恢复指令ECU能否顺利退出关闭状态就是测试的关键点。用例ID前置条件测试步骤预期结果TC_0x28_201扩展会话安全解锁发送0x28 0x03 0x01等待5秒后发送0x28 0x00 0x01第一个请求正响应通信停止第二个请求正响应通信恢复TC_0x28_202扩展会话安全解锁通信已关闭发送0x3E 0x00TesterPresent正响应0x7E 0x00证明诊断通道仍正常TC_0x28_203扩展会话安全解锁连续发送两次0x28 0x03 0x01两次均正响应第二次后通信仍然关闭TC_0x28_204扩展会话安全未解锁发送0x28 0x03 0x01负响应0x7F 0x28 0x33或0x22按需求定义TC_0x28_205通信关闭期间关闭后等待10秒再发恢复指令恢复成功ECU无异常下电或复位这类用例有个隐藏价值验证ECU在异常时序下不会“卡死”。比如连续关闭两次后内部状态机是否还能正确处理恢复请求关闭期间收到TesterPresent是否还能维持诊断会话恢复后NM报文的发送周期是否正常。5. 用例设计的落地实现示例5.1 通过Python发送0x28服务请求在测试台架上除了使用CANoe这类商业工具Python配合USB-CAN设备也是一种轻量级方案。下面是一个基于python-can库的示例请在已授权的测试环境中使用。# requirements: python-can import can def build_0x28_request(sub_function: int, comm_type: int) - bytes: # 0x28服务请求固定为3字节 return bytes([0x28, sub_function, comm_type]) def send_request(bus: can.interface.Bus, arbitration_id: int 0x7E0): request build_0x28_request(0x03, 0x01) # disableRxAndTx Normal msg can.Message( arbitration_idarbitration_id, datarequest, is_extended_idFalse, is_fdFalse, ) bus.send(msg) print(f[TX] {request.hex( ).upper()} - 0x{arbitration_id:X}) def wait_response( bus: can.interface.Bus, expected_arb_id: int 0x7E8, timeout: float 0.5, ) - bytes | None: resp bus.recv(timeouttimeout) if resp is not None: print(f[RX] {resp.data.hex( ).upper()} - 0x{resp.arbitration_id:X}) return bytes(resp.data) print([Timeout] No response) return None if __name__ __main__: # 请将can0替换为实际使用的网络接口 bus can.interface.Bus(channelcan0, interfacesocketcan) send_request(bus) resp wait_response(bus) if resp: assert resp[0] 0x68, fexpected positive response, got {resp.hex()} bus.shutdown()这段代码的核心逻辑很直接构造0x28 0x03 0x01三个字节发送到0x7E0物理寻址ID然后监听0x7E8。真实工程中诊断仪一般通过诊断数据库发送不会自己拼字节但在协议验证阶段用裸CAN帧快速验证ECU行为是可行的。5.2 CANoe/CAPL发送与校验示例CANoe是汽车网络诊断测试中非常常见的工具。下面是一个最简CAPL示例演示如何通过按键发送0x28服务请求并在收到正响应或负响应时输出日志。/* CANoe/CANalyzer示例发送0x28服务请求并校验响应 */ message 0x7E0 g_diagReq; on key d { g_diagReq.dlc 3; g_diagReq.byte(0) 0x28; // SID: CommunicationControl g_diagReq.byte(1) 0x03; // SubFunction: disableRxAndTx g_diagReq.byte(2) 0x01; // CommType: Normal communication output(g_diagReq); write([TX] 0x28 0x03 0x01 to 0x7E0); } on message 0x7E8 { if (this.byte(0) 0x68) { write([RX] Positive response, subfunction0x%02X, this.byte(1)); } else if (this.byte(0) 0x7F this.byte(1) 0x28) { write([RX] NRC0x%02X for 0x28 service, this.byte(2)); } }实际工程中请通过CANoe的诊断层函数或CDD数据库来发送服务而不是直接输出裸CAN帧。原因是真实诊断仪会处理一帧多帧、流控制、寻址格式等底层逻辑裸CAN帧只适合快速验证不适合作为正式测试用例的自动化脚本。5.3 用例的YAML结构化表达用例如果只写在Excel里需求一变更就很容易失控。推荐把0x28服务的用例结构化成YAML用数据驱动方式维护。service: 0x28 CommunicationControl project: xxx_network_diag_test preconditions: - ecu_session: extended - security: unlocked - network: normal cases: - id: TC_0x28_001 priority: P1 title: 关闭Normal通信并验证恢复流程 steps: - send: [0x28, 0x03, 0x01] - wait: 300ms - expect: positive_response_0x68_03 - expect: normal_communication_stopped - send: [0x28, 0x00, 0x01] - expect: positive_response_0x68_00 - expect: normal_communication_resumed这种结构化用例有几个好处一是可以在自动化框架中直接解析执行二是测试结果可以自动回填三是配合TestBuddy这类用例生成与管理工具可以基于需求变更自动生成用例骨架减少重复劳动。6. 用例执行与效果验证0x28服务的用例执行不能只看响应帧。完整的执行流程应该包含四步第一步搭建总线网络。在测试台架或HIL环境中接入ECU、CAN卡、电源和总线监控工具。确保整车上电状态正常并进入扩展诊断会话。如果测试中涉及功能寻址确认总线上只有目标ECU会响应避免误判。第二步记录基线通信状态。发送0x28之前先在CANoe的Trace窗口或报文统计分析中确认应用报文是否在发送、NM报文是否周期出现、ECU当前处于哪个网络状态。这个“基线”非常重要因为执行后的断言都基于基线变化来判断。第三步发送0x28请求并检查响应。通过Python脚本、CANoe Panel、诊断仪或CAPL发送请求记录响应帧并检查NRC是否与预期一致。第四步观察通信行为。如果请求是0x28 0x03 0x01关闭Normal通信那么Trace窗口中应用报文应当消失如果请求是0x28 0x01 0x02那么NM报文的发送侧应停止但接收侧仍然可以接收。判断成功的标准是“总线行为发生预期的变化”而不是“收到一个正响应”。最后一定要执行恢复流程。发送0x28 0x00 0x01恢复通信确认应用报文和NM报文都恢复发送。如果执行到这里失败测试台架可能直接失去与ECU的通信需要通过重新上电或Reset功能复位ECU。7. 常见问题与排查思路0x28服务测试中以下问题出现频率最高。问题现象可能原因排查方式解决方案发送0x28后无任何响应子功能bit7置位抑制了正响应检查发送帧的Sub-function是否等于0x80~0x83去掉抑制位或按需求确认允许使用返回0x7F 0x28 0x22安全条件或网络条件不满足检查安全访问是否解锁、状态机是否允许先执行0x27解锁或调整总线状态返回0x7F 0x28 0x31通信类型为保留值或越界对照需求确认通信类型支持范围改为合法值0x01~0x03返回0x7F 0x28 0x7E当前会话不支持该子功能检查当前诊断会话切换到扩展会话后重试关闭通信后诊断请求无响应误解了关闭范围或ECU异常检查0x28是否影响诊断通道查看ECU供电状态按需求恢复通信必要时重新上电恢复后通信时序异常恢复指令与关闭指令状态不匹配抓取恢复前后的NM报文时序统一使用0x00恢复所有通信功能寻址发送后无响应功能寻址下ECU可能不回复正响应检查请求ID是否为目标功能寻址ID确认需求是否要求功能寻址响应排查时有一个固定顺序先看请求帧是否发出去再看地址和数据是否正确然后看ECU返回的NRC最后看总线上的通信行为。不要一上来就怀疑ECU软件有bug很多问题都出在测试脚本构造的请求帧不符合协议格式。8. 最佳实践与工程建议第一需求评审阶段就要确认0x28服务的边界。测试工程师要用测试的
返回列表