第3章:UDS服务基础:安全访问、通信控制与测试仪在线
大家好,欢迎来到第三章。今天我们要聊的三个服务——0x27安全访问、0x28通信控制、0x3E测试仪在线——可以说是UDS诊断栈里最常用的“三剑客”。我个人习惯把它们放在一起讲,因为它们在很多场景下是配合使用的。你想想看,一个完整的诊断会话,往往是从安全解锁开始,然后控制通信,最后保持连接。嗯,咱们一个一个来拆解。
3.1 安全访问服务 (0x27) —— 诊断的“看门人”
安全访问服务,说白了就是给诊断加了一把锁。为什么需要它?因为不是谁都能随便改写ECU里的数据。比如刷写软件、标定参数、执行特殊功能,这些操作如果被恶意或误操作触发,后果很严重。我在项目中遇到过,有台车在产线上因为诊断仪没做安全校验,直接把标定数据写乱了,最后返工了好几天。
0x27服务的核心机制是“挑战-应答”。流程是这样的:
- 诊断仪发送0x27 + 请求种子(seed)的子功能,比如0x01表示请求种子。
- ECU返回一个随机种子(通常是4字节或8字节)。
- 诊断仪用约定的算法(比如AES、自定义多项式)计算密钥(key)。
- 诊断仪发送0x27 + 发送密钥的子功能(如0x02) + 计算出的密钥。
- ECU内部也计算一遍,比对一致则解锁成功。
重要: 种子和密钥的算法是OEM(整车厂)自己定义的,标准里只规定了流程,没规定算法。所以不同车企的算法千差万别,有的甚至用查表法。
这里有个细节要注意:安全等级。0x27服务支持多个安全等级,比如0x01/0x02是等级1,0x03/0x04是等级2。等级越高,能访问的权限越大。我建议在设计诊断栈时,把安全等级和具体的服务/子功能做一张映射表,这样代码逻辑清晰,也方便后期维护。
我的小技巧: 在实现种子生成时,建议加入时间戳或计数器,避免种子重复。我曾经见过一个项目,种子是固定的,结果被破解后整个ECU的安全防线就形同虚设了。
3.2 通信控制服务 (0x28) —— 诊断的“开关”
0x28服务,它的作用是控制ECU的通信行为。说白了,就是让ECU“闭嘴”或者“说话”。为什么需要这个?举个例子:你在做CAN总线测试时,如果ECU一直往外发报文,会干扰你的诊断通信。这时候,你可以用0x28服务让ECU暂时停止发送某些报文。
0x28服务有以下几个子功能:
| 子功能 | 含义 | 说明 |
|---|---|---|
| 0x00 | 启用Rx和Tx | 恢复所有通信 |
| 0x01 | 禁用Rx和Tx | 完全静默 |
| 0x02 | 启用Rx,禁用Tx | 只收不发 |
| 0x03 | 禁用Rx,启用Tx | 只发不收 |
你可能会问:“禁用Rx和Tx,那诊断通信还能继续吗?” 嗯,这里要注意:0x28服务控制的是应用报文,而不是诊断报文本身。也就是说,ECU仍然可以响应0x28请求,但其他非诊断的报文(比如周期发送的传感器数据)会被抑制。
避坑指南: 我曾经在项目里遇到一个问题:用0x28禁用Tx后,ECU的看门狗因为长时间没有发送报文而触发了复位。所以,在使用0x28服务时,一定要确认ECU的底层机制是否依赖周期性报文来维持正常运行。
3.3 测试仪在线服务 (0x3E) —— 诊断的“心跳”
0x3E服务,也叫“保持激活”服务。它的作用很简单:告诉ECU“诊断仪还在,别超时”。
UDS协议规定,ECU在非默认会话(比如扩展会话、编程会话)下,如果一段时间内没有收到任何诊断请求,就会自动跳回默认会话。这个时间通常由S3Server定时器控制,一般是5秒左右。0x3E服务就是用来重置这个定时器的。
0x3E服务只有一个子功能:0x00(请求保持激活)。它的响应也很简单,就是肯定响应码(0x7F + 0x3E + 0x00)。
关键点: 0x3E服务不需要安全访问解锁就可以使用。这是标准规定的,因为它的目的就是维持连接,而不是执行敏感操作。
在实际项目中,我建议诊断仪每隔2-3秒发送一次0x3E请求。为什么是这个频率?因为要留出余量。如果S3Server定时器是5秒,你4秒发一次,万一网络延迟一次,就可能超时。我个人习惯用2.5秒的间隔,既不会太频繁占用总线,又能保证连接稳定。
这里有个容易忽略的点:0x3E服务只对当前会话有效。如果你从扩展会话切换到编程会话,需要重新发送0x3E来维持新的会话。我记得有一次调试,测试人员发现ECU总是莫名其妙跳回默认会话,查了半天才发现是切换会话后忘了发0x3E。
3.4 三个服务的配合使用
在实际诊断流程中,这三个服务经常是串在一起的。我画一个典型的流程给你看:
- 诊断仪进入扩展会话(0x10 0x03)。
- 发送0x27服务,完成安全解锁。
- 发送0x28服务,禁用某些应用报文,避免干扰。
- 执行需要高权限的诊断操作(比如写入数据、执行例程)。
- 定时发送0x3E服务,保持会话不超时。
- 操作完成后,发送0x28服务恢复通信,或者发送0x11服务复位ECU。
这个流程看起来简单,但每个环节都有坑。比如,安全解锁失败后,ECU通常会有一个“延迟时间”,在延迟时间内不允许再次尝试解锁。这个延迟时间一般是1秒到10秒不等,具体看OEM定义。我曾经见过一个项目,测试人员连续快速发送解锁请求,结果ECU直接进入了“锁定状态”,必须断电重启才能恢复。
我的建议: 在实现诊断栈时,一定要把这三个服务的状态机设计好。比如,安全解锁状态、通信控制状态、会话状态,它们之间是相互影响的。我习惯用一个全局的状态结构体来管理,这样代码可读性高,也容易调试。
3.5 代码示例:0x3E服务的简单实现
最后,给你看一段简化的0x3E服务处理代码。这只是示意,实际项目中要考虑更多细节,比如超时处理、错误码等。
// 假设有一个全局的会话定时器
static uint32_t session_timer = 0;
// 处理0x3E请求
uint8_t handle_service_0x3E(uint8_t *request, uint16_t length) {
// 检查子功能
if (request[1] != 0x00) {
// 子功能不支持,返回NRC 0x12
return 0x12;
}
// 重置会话定时器
session_timer = 0;
// 返回肯定响应
// 响应格式:0x7E + 0x00
response[0] = 0x7E;
response[1] = 0x00;
return 0; // 表示成功
}
// 在主循环中调用,检查会话是否超时
void check_session_timeout(void) {
session_timer++;
if (session_timer > S3_SERVER_TIMEOUT) {
// 超时,跳回默认会话
switch_to_default_session();
}
}
这段代码很简单,但核心逻辑就在那里:收到0x3E就重置定时器,定时器超时就切换会话。嗯,实际项目中你还要考虑多会话、多任务的情况,但原理是一样的。
好了,第三章的内容就到这里。这三个服务是UDS诊断栈的基石,理解透了,后面的高级服务学起来就轻松多了。下一章我们会聊0x22和0x2E服务——读写数据,到时候见。