1. 引言
在UDS刷写流程中,写入数据(0x2E Service) 承担着一个看似简单却不可或缺的角色------向ECU写入特定的配置信息。如果说0x34-36-37是负责传输固件这个"大块头"的"重型卡车",那么0x2E就是负责传递VIN码、软件版本、指纹信息等"小纸条"的"信使"。
在固件升级场景中,0x2E服务最常见的用途是写入指纹信息(Fingerprint)。指纹信息通常包含软件版本号、编译时间、刷写工具标识等数据,用于在刷写完成后标记当前ECU的软件状态,便于后续的版本追溯和故障排查。
TOOMOSS_SID2E_WriteDataByID.vi封装了UDS 0x2E服务的完整流程,通过调用TOOMOSS_SendAndWaitResp.vi通信基座完成请求发送和响应接收。
2. 子VI功能概述
2.1 设计目标
-
根据用户指定的DID和数据,构造0x2E服务请求报文
-
调用通信基座发送请求并等待ECU响应
-
解析响应报文,判断数据写入是否成功
-
返回响应数据和执行状态
2.2 UDS 0x2E服务协议说明
服务名称:WriteDataByIdentifier(按标识符写数据)
功能:客户端通过数据标识符(DID)向服务器指定的内部位置写入数据记录。数据可能受保护,也可能不受保护。
2.2.1 请求报文格式
0x2E服务不支持Sub-function参数,请求报文格式非常直接:
| 字节位置 | 参数 | 说明 |
|---|---|---|
| Byte 0 | SID | 固定值 0x2E |
| Byte 1 | DID(高字节) | 数据标识符的高8位 |
| Byte 2 | DID(低字节) | 数据标识符的低8位 |
| Byte 3 ~ N | dataRecord | 要写入的实际数据 |
请求数据构造方式:
[0x2E, DID_High, DID_Low, data0, data1, data2, ...]
2.2.2 肯定响应格式
| 字节位置 | 参数 | 说明 |
|---|---|---|
| Byte 0 | SID | 0x6E(0x2E + 0x40) |
| Byte 1 | DID(高字节) | 请求中的DID高字节 |
| Byte 2 | DID(低字节) | 请求中的DID低字节 |
关键点 :0x2E服务的肯定响应固定为3个字节,仅包含SID和DID回声。不返回写入的数据内容。
2.2.3 否定响应格式
Byte 0: SID = 0x7F
Byte 1: 0x2E
Byte 2: NRC(负响应码)
2.3 输入参数
| 控件 | 类型 | 说明 |
|---|---|---|
| 设备句柄 | U32 | 由TOOMOSS_OpenDev(CAN).vi返回的设备句柄 |
| 通道号 | 枚举(CAN1/CAN2) | 选择使用CAN1还是CAN2通道 |
| 物理地址 | U32 | 请求报文ID(发送给ECU的CAN ID) |
| 响应地址 | U32 | 期望的响应报文ID(ECU回复的CAN ID) |
| DID | U16 | 数据标识符,如0xF190(VIN码)、0xF188(版本号)等 |
| 写入数据 | U8数组 | 要写入的实际数据内容 |
| 数据长度 | I32 | 写入数据的有效字节数 |
2.4 输出参数
| 控件 | 类型 | 说明 |
|---|---|---|
| 响应数据 | U8数组 | ECU返回的完整响应数据(肯定响应或否定响应) |
| 返回值 | I32 | 0表示成功,-1表示失败 |
| 错误信息 | 字符串 | 失败时的错误描述 |
3. 前面板设计

4. 程序框图设计
4.1 整体流程
DID(U16)→ 拆分为高字节和低字节
写入数据(U8数组)→ 作为dataRecord
↓
构造请求数据[0x2E, DID_High, DID_Low, data...]
↓
调用 TOOMOSS_SendAndWaitResp.vi
↓
判断响应类型
┌───────────────┼───────────────┐
↓ ↓ ↓
返回<0 返回=0 返回>0
(调用失败) (超时) (收到响应)
↓ ↓ ↓
返回-1 返回-1 判断响应首字节
┌───────────────┼───────────────┐
↓ ↓
0x6E 0x7F
(肯定响应) (否定响应)
↓ ↓
返回0 返回-1
解析NRC返回错误信息
4.2 第一步:拆分DID
DID是一个2字节(U16)的数据标识符,需要拆分为高字节和低字节以便放入请求报文:
-
DID高字节 =
(DID >> 8) & 0xFF -
DID低字节 =
DID & 0xFF
在LabVIEW中,可以使用循环移位(右移8位) 和与(0xFF) 操作实现拆分,或使用强制类型转换将U16转换为2字节U8数组。
4.3 第二步:构造请求数据
使用创建数组函数按顺序组合:
[0x2E, DID_High, DID_Low, 写入数据...]
其中写入数据部分通过连接数组函数拼接到固定头部之后。
4.4 第三步:调用通信基座
将上一步构造的请求数据作为输入,调用TOOMOSS_SendAndWaitResp.vi:
| 输入参数 | 来源/值 |
|---|---|
| 设备句柄 | 子VI输入 |
| 通道号 | 子VI输入 |
| 物理地址 | 子VI输入 |
| 响应地址 | 子VI输入 |
| 请求数据 | 构造的[0x2E, DID_High, DID_Low, data...] |
| 数据长度 | 3 + 写入数据长度 |
4.5 第四步:判断响应类型
从通信基座获取返回值,分三种情况处理:
情况一:返回值 < 0(通信失败)
-
通信基座内部调用DLL失败
-
直接透传错误信息,子VI返回 -1
情况二:返回值 = 0(无响应/超时)
-
ECU未响应
-
设置错误信息为"ECU未响应,请检查物理地址和通信连接"
-
子VI返回 -1
情况三:返回值 > 0(收到响应)
-
响应数据有效,继续解析
-
判断响应数据的首字节(Byte 0)
肯定响应判断:
text
if 响应数据[0] == 0x6E → 写入成功,返回0[reference:17]
响应数据0 = 0x6E = 0x2E + 0x40,符合UDS肯定响应规则。
否定响应判断:
if 响应数据[0] == 0x7F → 写入失败
NRC = 响应数据[2]
根据NRC值生成对应的错误信息
返回-1
4.6 常见NRC错误信息映射
| NRC | 含义 | 错误信息 |
|---|---|---|
| 0x12 | 子功能不支持 | "该ECU不支持写入数据服务" |
| 0x13 | 报文长度错误 | "数据长度与DID定义不匹配" |
| 0x22 | 条件不满足 | "先决条件不满足,请检查当前会话状态" |
| 0x31 | 请求超出范围 | "DID无效或写入地址超出范围" |
| 0x33 | 安全访问被拒绝 | "安全访问被拒绝,请先完成0x27服务解锁" |
| 0x72 | 一般编程错误 | "写入NVM失败,请检查DID是否可写" |
| 0x7E | 当前会话下子功能不支持 | "当前会话下不支持写入数据服务" |
| 0x7F | 当前会话下服务不支持 | "当前会话下不支持2E服务,请先进入扩展会话" |
| 其他 | 未知 | "写入数据失败,NRC=0x%02X" |
5. 程序框图关键节点示意
5.1 DID拆分与请求数据构造
5.2 通信基座调用

5.3 响应解析

6. 在UDS刷写流程中的位置
在整个UDS刷写流程中,TOOMOSS_SID2E_WriteDataByID.vi位于主编程阶段:
主编程阶段:
(1) 切换到编程会话 [10 02]
(2) 安全访问 [27]
(3) 写入指纹信息 [2E F1 84] ← 调用本子VI
(4) 下载flash driver [34-36-37]
(5) 擦除编程分区 [31 01 FF 00]
(6) 下载APP数据 [34-36-37]
6.1 典型应用场景
| 场景 | DID示例 | 数据内容 | 说明 |
|---|---|---|---|
| 写入指纹信息 | 0xF184 | 软件版本+编译时间+工具标识 | 刷写后标记ECU状态 |
| 写入VIN码 | 0xF190 | 17字节ASCII码 | 整车下线配置 |
| 写入版本号 | 0xF188 | 4字节版本信息 | 标记软件版本 |
| 写入标定参数 | 0xF189 | 4字节浮点数 | 更新标定数据 |
6.2 前置条件
执行0x2E服务前,通常需要满足以下条件:
-
会话状态 :已切换到扩展会话 或编程会话
-
安全访问:已通过0x27服务完成安全访问解锁
-
DID合法性 :DID在ECU中已定义且为可写类型
-
数据长度:写入数据长度与DID定义的长度完全匹配
7. 典型调用示例
7.1 写入指纹信息(DID = 0xF184)
输入配置:
-
物理地址 = 0x700
-
响应地址 = 0x708
-
DID = 0xF184
-
写入数据 =
[0x56, 0x01, 0x02, 0x03, 0x20, 0x26, 0x07, 0x27](版本号+时间戳) -
数据长度 = 8
实际发送 :[0x2E, 0xF1, 0x84, 0x56, 0x01, 0x02, 0x03, 0x20, 0x26, 0x07, 0x27]
期望响应 :[0x6E, 0xF1, 0x84](肯定响应,3字节)
7.2 写入VIN码(DID = 0xF190)
输入配置:
-
物理地址 = 0x700
-
响应地址 = 0x708
-
DID = 0xF190
-
写入数据 = 17字节ASCII VIN码
-
数据长度 = 17
实际发送 :[0x2E, 0xF1, 0x90, 0x4C, 0x56, ...](VIN码数据)
期望响应 :[0x6E, 0xF1, 0x90](肯定响应)
7.3 否定响应示例(数据长度错误)
若写入数据长度与DID定义长度不匹配,ECU返回 [0x7F, 0x2E, 0x13]。子VI应解析NRC=0x13,返回错误信息:"数据长度与DID定义不匹配"。
7.4 否定响应示例(安全访问未解锁)
若未完成安全访问,ECU返回 [0x7F, 0x2E, 0x33]。子VI应解析NRC=0x33,返回错误信息:"安全访问被拒绝,请先完成0x27服务解锁"。
8. 错误处理
| 错误场景 | 返回值 | 错误信息 |
|---|---|---|
| 设备句柄无效 | -1 | "设备句柄无效,请检查设备是否已打开" |
| 通信基座返回<0 | -1 | 透传通信基座的错误信息 |
| 通信基座返回=0 | -1 | "ECU未响应,请检查物理地址和通信连接" |
| 响应首字节为0x7F | -1 | 根据NRC返回对应错误信息 |
| 响应首字节非0x6E非0x7F | -1 | "收到未知响应,SID=0x%02X" |
| DID拆分错误 | -1 | "DID无效,请检查DID值是否正确" |
9. 注意事项
| 要点 | 说明 |
|---|---|
| 会话要求 | 0x2E服务通常需要扩展会话 或编程会话,请先执行0x10服务切换会话 |
| 安全访问 | 写入DID通常需要先解锁0x27安全访问,否则会返回NRC=0x33 |
| DID可写性 | 部分DID是只读的,写入只读DID会返回NRC=0x31 |
| 数据长度匹配 | 写入数据的长度必须与DID定义的长度完全一致,否则返回NRC=0x13 |
| 正响应长度 | 0x2E的肯定响应固定为3个字节,如果响应长度不等于3且首字节为0x6E,可能存在异常 |
| 多帧传输 | 如果写入数据超过7字节(CAN单帧数据域限制),0x2E服务需要ISO-TP多帧传输支持 |
| NVM写入风险 | 写入DID实际上是在操作ECU的非易失性存储器(NVM),频繁写入可能影响NVM寿命 |
10. 总结
TOOMOSS_SID2E_WriteDataByID.vi封装了UDS 0x2E写入数据服务,虽然它的实现逻辑相对简单------构造[2E + DID + Data]请求、发送、等待3字节响应------但它在刷写流程中扮演着标记和记录的重要角色。
0x2E服务与0x22服务(读数据)是一对"孪生兄弟"------一个负责写入,一个负责读取。在刷写场景中,通常会在刷写完成后使用0x2E写入指纹信息(软件版本、编译时间等),并在后续诊断中使用0x22读取验证。
理解DID的可写性约束、数据长度匹配要求以及安全访问的前置条件,是实现稳定写入的关键。在实际项目中,建议在写入前先使用0x22读取确认DID的当前值,写入后再读取验证,确保数据正确写入。
下一篇将介绍 TOOMOSS_SID31_RoutineControl.vi --- 例程控制(擦除编程分区、完整性校验等),敬请期待。