3. 默认会话处理
任务目标
实现UDS诊断中的默认会话(Default Session,会话ID 0x01)处理逻辑:当ECU处于默认会话时,能够正确响应会话切换请求(0x10服务)和基本诊断服务(本文以0x22读取数据、0x2E写入数据为例),并验证会话状态机转换的正确性。
核心实现
#include <stdint.h>
#include <string.h>
/* UDS会话状态 */
typedef enum {
SESSION_DEFAULT = 0x01,
SESSION_PROGRAMMING = 0x02,
SESSION_EXTENDED = 0x03
} UdsSessionType;
/* 诊断请求/响应结构 */
typedef struct {
uint8_t data[4096];
uint16_t len;
} UdsMessage;
/* 当前会话状态(初始为默认会话) */
static UdsSessionType current_session = SESSION_DEFAULT;
/* 模拟ECU数据存储(用于0x22/0x2E服务) */
static uint8_t ecu_data[4] = {0x11, 0x22, 0x33, 0x44};
/* 发送诊断响应(实际工程中对接CAN驱动) */
static void send_response(const uint8_t* data, uint16_t len) {
/* 在真实ECU中,此处将数据通过CAN/DoIP发送给Tester */
(void)data;
(void)len;
}
/* 发送否定响应 */
static void send_negative_response(uint8_t service_id, uint8_t nrc) {
uint8_t resp[3] = {0x7F, service_id, nrc};
send_response(resp, 3);
}
/* 处理会话切换服务 (0x10) */
static void handle_session_control(const UdsMessage* req) {
uint8_t resp[3];
if (req->len < 2) {
send_negative_response(0x10, 0x13); /* incorrectMessageLengthOrInvalidFormat */
return;
}
uint8_t target_session = req->data[1];
/* 仅允许切换到编程会话和扩展会话 */
if (target_session != SESSION_PROGRAMMING &&
target_session != SESSION_EXTENDED) {
send_negative_response(0x10, 0x12); /* subFunctionNotSupported */
return;
}
/* 执行会话切换 */
current_session = (UdsSessionType)target_session;
/* 肯定响应:SID + 子功能 + P2*10ms */
resp[0] = 0x50;
resp[1] = target_session;
resp[2] = 0x00; /* P2 timing: 0 means default 50ms */
send_response(resp, 3);
}
/* 处理读取数据服务 (0x22) */
static void handle_read_data(const UdsMessage* req) {
if (req->len < 2) {
send_negative_response(0x22, 0x13);
return;
}
uint16_t did = (req->data[1] << 8) | req->data[2];
/* 仅支持DID 0xF190 */
if (did != 0xF190) {
send_negative_response(0x22, 0x31); /* requestOutOfRange */
return;
}
uint8_t resp[6];
resp[0] = 0x62;
resp[1] = 0xF1;
resp[2] = 0x90;
memcpy(&resp[3], ecu_data, 3); /* 返回3字节数据 */
send_response(resp, 6);
}
/* 处理写入数据服务 (0x2E) */
static void handle_write_data(const UdsMessage* req) {
if (req->len < 5) {
send_negative_response(0x2E, 0x13);
return;
}
uint16_t did = (req->data[1] << 8) | req->data[2];
if (did != 0xF190) {
send_negative_response(0x2E, 0x31);
return;
}
/* 写入3字节数据 */
memcpy(ecu_data, &req->data[3], 3);
uint8_t resp[4];
resp[0] = 0x6E;
resp[1] = 0xF1;
resp[2] = 0x90;
send_response(resp, 3);
}
/* 主诊断入口:根据当前会话分发请求 */
void uds_process_message(const UdsMessage* req) {
if (req->len < 1) return;
uint8_t sid = req->data[0];
/* 默认会话下允许的服务 */
if (current_session == SESSION_DEFAULT) {
switch (sid) {
case 0x10: /* 会话切换 */
handle_session_control(req);
break;
case 0x22: /* 读取数据 */
handle_read_data(req);
break;
case 0x2E: /* 写入数据 */
handle_write_data(req);
break;
default:
send_negative_response(sid, 0x11); /* serviceNotSupported */
break;
}
} else {
/* 非默认会话:本文仅演示默认会话,其他会话返回不支持 */
send_negative_response(sid, 0x7F); /* serviceNotSupportedInActiveSession */
}
}
运行与验证
环境依赖:需要C编译器(如GCC),代码不依赖特定硬件,通过send_response回调对接实际CAN驱动。
编译与测试:
gcc -c uds_default_session.c -o uds_default_session.o
验证方法:编写测试代码调用uds_process_message,构造以下输入并检查响应:
- 会话切换:发送
{0x10, 0x02}(切换到编程会话),预期响应{0x50, 0x02, 0x00},且内部状态变为SESSION_PROGRAMMING。 - 默认会话读取:在默认会话下发送
{0x22, 0xF1, 0x90},预期响应{0x62, 0xF1, 0x90, 0x11, 0x22, 0x33}。 - 默认会话写入:发送
{0x2E, 0xF1, 0x90, 0xAA, 0xBB, 0xCC},预期响应{0x6E, 0xF1, 0x90},随后读取DID 0xF190应返回新数据。 - 不支持服务:发送
{0x31, 0x01}(例程控制),预期否定响应{0x7F, 0x31, 0x11}。
通过条件:上述4项测试的响应与预期完全一致,且会话切换后再次发送0x22服务应收到否定响应(0x7F),证明会话状态已正确迁移。
关键说明:上述SVG展示了默认会话的状态位置、允许的服务范围以及通过0x10服务切换到其他会话的路径。切换后,默认会话下的诊断服务(0x22/0x2E)将不再可用,需在对应会话中重新实现或返回否定响应。