实战二:诊断数据读取与写入——0x22和0x2E服务
好,咱们直接进入正题。这一章要聊的,是诊断开发里最基础、也最常用的两个服务——读数据(0x22)和写数据(0x2E)。
说白了,0x22就是你去问ECU“你现在某个参数是多少?”,0x2E就是你告诉ECU“把这个参数改成那个值”。听起来简单吧?但实际项目里,坑可不少。
2.1 通过ID读取数据(0x22服务)
0x22服务的格式非常固定。请求报文长这样:
| 字节0 | 字节1 | 字节2 |
| 0x22 | DID高字节 | DID低字节 |
DID就是数据标识符,两个字节,范围从0x0000到0xFFFF。每个DID对应ECU内部一个特定的数据。
举个例子,我想读ECU的当前车速。假设车速的DID是0x0101,那请求就是:
发送:22 01 01
ECU正常响应:
接收:62 01 01 00 64
这里0x62是肯定响应SID(请求SID+0x40),后面跟着DID,再后面就是数据。00 64换算成十进制是100,车速100km/h。
但要注意,不是所有DID都能随便读。有些DID需要安全访问后才能读,有些则不需要。这个后面会讲。
2.2 通过ID写入数据(0x2E服务)
0x2E服务是0x22的“反向操作”。请求格式:
| 字节0 | 字节1 | 字节2 | 字节3... |
| 0x2E | DID高字节 | DID低字节 | 数据 |
比如我想把车速传感器标定值改成0x0064(100),DID是0xF190:
发送:2E F1 90 00 64
ECU正常响应:
接收:6E F1 90 00 64
2.3 安全访问(0x27服务)实战
安全访问,说白了就是ECU的“门禁系统”。你想写数据?先证明你有权限。
0x27服务的流程分两步:
- 请求种子(Seed):客户端发送0x27 + 安全等级
- 发送密钥(Key):客户端根据种子计算出密钥,发回给ECU验证
举个例子,安全等级为0x01:
第一步:发送 27 01
ECU回复:67 01 12 34 56 78 (种子是0x12345678)
第二步:发送 27 02 9A BC DE F0 (密钥)
ECU回复:67 02 (解锁成功)
关键点:种子和密钥的算法是OEM自定义的。有的用简单的异或,有的用AES加密。我见过最坑的是某德系车,算法里还混了时间戳,你必须在100ms内完成计算并回复,否则种子失效。
2.4 实战中的避坑指南
- DID范围要确认:0x0000-0xFFFF虽然都能用,但OEM通常会划分保留区域。比如0xF000-0xFFFF是标定专用,0x0000-0x00FF是诊断仪专用。写之前一定看DID表。
- 写入前先读:我习惯先发0x22读一下当前值,确认后再写。有一次我直接写0x2E,结果把ECU的VIN码覆盖了...还好是开发阶段。
- 安全访问超时:ECU收到种子后,通常只给你几秒钟时间计算密钥。超时了就得重新请求种子。我建议在代码里加个定时器,超时自动重试。
- NRC处理:如果ECU回复NRC(否定响应码),别慌。0x22常见的NRC是0x31(请求超出范围),0x2E常见的是0x33(安全访问拒绝)和0x72(数据格式错误)。查一下文档就知道问题在哪。
2.5 代码示例(Python伪代码)
def read_data_by_id(did):
request = [0x22, (did >> 8) & 0xFF, did & 0xFF]
response = send_can_message(request)
if response[0] == 0x62:
data = response[3:] # 跳过SID和DID
return data
else:
handle_nrc(response[0])
def write_data_by_id(did, data):
# 先安全访问
if not security_access(0x01):
return False
request = [0x2E, (did >> 8) & 0xFF, did & 0xFF] + data
response = send_can_message(request)
return response[0] == 0x6E
def security_access(level):
# 请求种子
seed_req = [0x27, level]
seed_resp = send_can_message(seed_req)
if seed_resp[0] != 0x67:
return False
seed = seed_resp[2:] # 种子数据
# 计算密钥(算法由OEM定义)
key = calculate_key(seed)
# 发送密钥
key_req = [0x27, level + 1] + key
key_resp = send_can_message(key_req)
return key_resp[0] == 0x67
2.6 总结
0x22和0x2E是诊断开发的基础操作。0x27则是保护ECU不被误写的关键。三者配合使用,才能安全、高效地完成标定工作。
我个人觉得,新手最容易犯的错误就是忽略安全访问。你想想看,如果谁都能随便写ECU的数据,那车还怎么开?所以,该解锁的时候别偷懒。
下一章我们会聊更高级的标定协议——0x34和0x36服务。到时候你会发现,有了安全访问的基础,那些服务学起来就快多了。
公众号:蓝海资料掘金营,微信deep3321