![]()
1. 服务概述
TesterPresent(0x3E)是 Unified Diagnostic Services(UDS,ISO 14229-1)中定义的一个诊断服务。该服务允许诊断仪(Tester)向 ECU 表明自己仍然在线,即使没有其他诊断请求需要发送。主要用途是保持 ECU 处于非默认会话模式(如编程会话、扩展诊断会话等),防止 ECU 因超时自动退回默认会话。
1.1 背景 – S3 Server 超时
ECU 内部有一个 S3Server 定时器。当 ECU 处于某个非默认会话模式时,该定时器会在每次收到任意诊断请求(包括 TesterPresent)后重置。如果在 S3Server 超时时间内未收到任何诊断请求,ECU 将主动退出当前非默认会话,返回至 Default Session(默认会话)。为了维持非默认会话,诊断仪必须以小于 S3Server 定时器值的周期持续发送 TesterPresent 请求。
典型 S3Server 值:5000 ms(5秒)。因此 TesterPresent 发送周期通常设为 2000~4000 ms。2. 消息格式 2.1 请求格式
字节位置
参数名称
值(hex)
描述
0
SID
0x3E
服务标识符
1
subFunction
0x00 或 0x80
子功能:
• 0x00 – 不抑制正响应 (ECU 应返回正响应)
• 0x80 – 抑制正响应 (ECU 不发送正响应,仅执行内部操作)
注意:子功能的 bit7 为 suppressPosRspMsgIndicationBit。当该位=1(即 0x80)时,ECU 禁止发送正响应,仅更新内部定时器。这能减少总线负载。
2.2 正响应格式
仅当请求子功能的 suppressPosRspMsgIndicationBit = 0 时,ECU 返回正响应:
字节位置
参数名称
值(hex)
描述
0
Response SID
0x7E
SID + 0x40
1
subFunction
0x00 或 0x80
与请求中的子功能值相同
2.3 负响应格式
当 ECU 无法处理该服务时,返回负响应:
字节位置
参数名称
值(hex)
描述
0
Negative SID
0x7F
负响应标识符
1
SID
0x3E
产生错误的服务 ID
2
NRC
0xXX
负响应码(Negative Response Code)
常见 NRC:
0x11– 不支持该服务(未实现 0x3E)0x12– 不支持子功能(例如请求了非 0x00/0x80 的值)0x13– 消息长度错误0x22– 条件不满足(例如当前会话模式不允许该操作)0x7E– 当前会话模式下子功能不支持0x7F– 服务请求序列错误
Tester ECU
| |
|--[ 0x3E 0x00 ] - - - - - - - - - - - - ->|
| | 重置 S3Server 定时器
|<- - - - - - - - - [ 0x7E 0x00 ] -- -- --| (当未抑制)
| |
| (延迟 < S3Server) |
| |
|--[ 0x3E 0x80 ] - - - - - - - - - - - - ->|
| | 重置 S3Server 定时器
| | (无正响应)
| |
4. C++ 代码示例以下代码提供了一个轻量级的 UDS 消息封装类,包含 TesterPresent 请求的构造、发送(模拟 CAN/DoIP 传输),以及响应解析。实际工程中需要替换底层通信接口(如 SocketCAN、Vector、或串口)。
5. 工程注意事项#include
#include
#include
#include
#include
// 诊断消息最大长度(UDS over CAN 通常 8 或 64 字节)
constexpr size_t MAX_UDS_MSG_SIZE = 64;
// 简单 UDS 消息结构
struct UdsMessage {
std::vector data;
};
// 底层通信接口(示例:模拟发送/接收,实际需替换为 CAN/以太网驱动)
class TransportLayer {
public:
// 模拟发送数据
bool send(const std::vector& rawMsg) {
std::cout << "[Tx] ";
for (auto byte : rawMsg) {
std::cout << std::hex << (int)byte << " ";
}
std::cout << std::dec << std::endl;
// 真实场景中调用 socket 或 CAN 写函数
return true;
}
// 模拟接收数据(阻塞接收,带超时)
bool receive(std::vector& buffer, int timeoutMs) {
// 此处模拟返回一个正响应
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// 若已配置模拟 ECU 响应,可返回真实数据
return false; // 示例中不实际返回,改为由测试代码直接调用解析
}
};
// UDS 服务封装
class UdsClient {
public:
UdsClient(TransportLayer& transport) : m_transport(transport) {}
// 构造 TesterPresent 请求
std::vector buildTesterPresentRequest(uint8_t subFunction) {
if (subFunction != 0x00 && subFunction != 0x80) {
throw std::invalid_argument("SubFunction must be 0x00 or 0x80");
}
return {0x3E, subFunction};
}
// 发送 TesterPresent 并等待/解析响应(仅当未抑制时)
bool sendTesterPresent(uint8_t subFunction, uint8_t& responseSubFunc, uint8_t& nrc) {
auto request = buildTesterPresentRequest(subFunction);
if (!m_transport.send(request)) {
std::cerr << "发送失败" << std::endl;
return false;
}
// 如果抑制正响应,不等待响应
if ((subFunction & 0x80) != 0) {
std::cout << "正响应被抑制,无需等待 ECU 回复。" << std::endl;
return true; // 认为发送成功,但不校验 ECU 状态
}
// 否则等待正响应或负响应
std::vector response;
if (!waitForResponse(response, 200)) { // 200ms 超时
std::cerr << "等待 ECU 响应超时" << std::endl;
return false;
}
// 解析响应
if (response.size() < 2) {
std::cerr << "响应长度无效" << std::endl;
return false;
}
uint8_t respSid = response[0];
if (respSid == 0x7E) { // 正响应
responseSubFunc = response[1];
std::cout << "收到 TesterPresent 正响应,子功能 = " << std::hex << (int)responseSubFunc << std::dec << std::endl;
return true;
}
else if (respSid == 0x7F) { // 负响应
if (response.size() >= 3) {
uint8_t reqSid = response[1];
nrc = response[2];
std::cerr << "负响应: SID=0x" << std::hex << (int)reqSid
<< ", NRC=0x" << (int)nrc << std::dec << std::endl;
} else {
std::cerr << "不完整的负响应" << std::endl;
}
return false;
}
else {
std::cerr << "未知响应 SID: 0x" << std::hex << (int)respSid << std::dec << std::endl;
return false;
}
}
private:
TransportLayer& m_transport;
// 阻塞接收直到收到完整 CAN/UDS 帧或超时
bool waitForResponse(std::vector& outBuf, int timeoutMs) {
// 模拟:实际中需调用 m_transport.receive()
// 为示例完整,手工构造一个正响应(模拟 ECU 回复)
// 真实项目中请替换为真实总线读取
std::this_thread::sleep_for(std::chrono::milliseconds(10));
outBuf = {0x7E, 0x00}; // 模拟正响应
return true;
}
};
// 模拟 ECU 侧的处理函数(用于理解内部逻辑)
class EcuSimulator {
public:
// ECU 处理 TesterPresent 请求,返回响应(若未抑制)
std::vector handleTesterPresent(const std::vector& request) {
if (request.size() < 2) {
return {0x7F, 0x3E, 0x13}; // 消息长度错误
}
uint8_t subFunc = request[1];
bool suppress = (subFunc & 0x80) != 0;
bool validSubFunc = (subFunc == 0x00 || subFunc == 0x80);
if (!validSubFunc) {
return {0x7F, 0x3E, 0x12}; // 不支持子功能
}
// 重置内部 S3Server 定时器(模拟动作)
resetS3Timer();
// 如果抑制正响应,返回空
if (suppress) {
return {};
} else {
return {0x7E, subFunc}; // 正响应
}
}
private:
void resetS3Timer() {
std::cout << "[ECU] S3Server 定时器已重置" << std::endl;
}
};
// ---------- 主程序演示 ----------
int main() {
TransportLayer transport;
UdsClient client(transport);
EcuSimulator ecu; // 仅用于演示 ECU 内部行为,实际通过总线通信
std::cout << "=== TesterPresent 服务演示 ===" << std::endl;
// 场景1:正常发送,不抑制正响应
uint8_t respSub = 0;
uint8_t nrc = 0;
bool success = client.sendTesterPresent(0x00, respSub, nrc);
if (success) {
std::cout << "场景1 成功,ECU 保持在线状态。" << std::endl;
} else {
std::cout << "场景1 失败,NRC=" << std::hex << (int)nrc << std::dec << std::endl;
}
// 场景2:抑制正响应
success = client.sendTesterPresent(0x80, respSub, nrc);
if (success) {
std::cout << "场景2 成功(无响应),ECU 定时器仍被重置。" << std::endl;
}
// 场景3:周期性保持会话
std::cout << "\n--- 启动周期性 TesterPresent(周期 3000ms,抑制响应)---" << std::endl;
for (int i = 0; i < 3; ++i) {
success = client.sendTesterPresent(0x80, respSub, nrc);
if (!success) {
std::cerr << "周期性发送失败,退出" << std::endl;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
}
std::cout << "周期发送结束,ECU 若未收到请求将在 S3Server 超时后退回默认会话。" << std::endl;return 0;
}
周期与超时 :TesterPresent 的发送周期必须小于 ECU 的
S3Server参数。可通过ReadDataByIdentifier (0x22)读取 DID0xF186(S3Server 时间)获取 ECU 实际值,若无则需参考 ECU 规范(典型 5s)。抑制正响应 :在正常周期保持中强烈推荐使用
0x80(抑制正响应),可降低总线负载。仅在调试或需要确认 ECU 存活性时使用0x00。会话切换 :TesterPresent 不会切换会话,仅维持当前会话。若要切换会话需使用
DiagnosticSessionControl (0x10)。安全访问 :某些 ECU 在非默认会话下可能要求先通过
SecurityAccess (0x27)服务,否则 TesterPresent 可能返回 NRC 0x22(条件不满足)。
TesterPresent(0x3E)是 UDS 中维持长连接的关键服务。诊断仪通过周期发送该请求(通常抑制正响应)来阻止 ECU 超时退回到默认会话。正确实现该服务对于执行编程、例程控制或扩展诊断操作至关重要。本文提供的 C++ 示例封装了请求构造、响应解析和周期性发送逻辑,可直接集成到实际的 UDS 诊断工具中。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.