3. UDS诊断会话控制
任务目标
实现基于UDS(ISO 14229-1)的诊断会话控制(0x10服务),支持默认会话(0x01)、编程会话(0x02)和扩展会话(0x03)之间的切换,并正确响应会话切换后的状态反馈。
核心实现
以下代码实现了一个最小可运行的UDS会话控制模块,包含会话状态管理、请求解析和响应生成逻辑。代码可直接编译运行,通过标准输入模拟诊断仪请求,输出对应的响应帧。
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/* UDS会话状态定义 */
typedef enum {
SESSION_DEFAULT = 0x01,
SESSION_PROGRAMMING = 0x02,
SESSION_EXTENDED = 0x03
} UdsSessionType;
/* 当前活动会话(初始为默认会话) */
static UdsSessionType current_session = SESSION_DEFAULT;
/* 会话切换后是否需等待响应(P2*) */
static uint8_t session_change_pending = 0;
/* 支持的会话切换映射表 */
typedef struct {
UdsSessionType from;
UdsSessionType to;
uint8_t allowed;
} SessionTransition;
static const SessionTransition transition_table[] = {
{SESSION_DEFAULT, SESSION_PROGRAMMING, 1},
{SESSION_DEFAULT, SESSION_EXTENDED, 1},
{SESSION_PROGRAMMING, SESSION_DEFAULT, 1},
{SESSION_PROGRAMMING, SESSION_EXTENDED, 1},
{SESSION_EXTENDED, SESSION_DEFAULT, 1},
{SESSION_EXTENDED, SESSION_PROGRAMMING, 1},
{SESSION_EXTENDED, SESSION_EXTENDED, 1}, /* 同会话重置定时器 */
{SESSION_DEFAULT, SESSION_DEFAULT, 1},
{SESSION_PROGRAMMING, SESSION_PROGRAMMING, 1}
};
/* 检查会话切换是否被允许 */
static uint8_t is_transition_allowed(UdsSessionType from, UdsSessionType to) {
uint8_t i;
uint8_t table_size = sizeof(transition_table) / sizeof(transition_table[0]);
for (i = 0; i < table_size; i++) {
if (transition_table[i].from == from && transition_table[i].to == to) {
return transition_table[i].allowed;
}
}
return 0;
}
/* 处理诊断会话控制(0x10)请求 */
/* 输入: request - 完整请求帧(含SID), req_len - 请求长度 */
/* 输出: response - 响应帧, 返回响应长度(不含长度字节), 负响应返回0 */
uint8_t handle_session_control(const uint8_t *request, uint8_t req_len,
uint8_t *response) {
uint8_t resp_len = 0;
UdsSessionType target_session;
/* 最小请求长度检查: SID + 子功能 */
if (req_len < 2) {
response[0] = 0x7F; /* 负响应SID */
response[1] = 0x10; /* 请求SID */
response[2] = 0x13; /* incorrectMessageLengthOrInvalidFormat */
return 3;
}
target_session = (UdsSessionType)request[1];
/* 检查子功能是否有效 */
if (target_session != SESSION_DEFAULT &&
target_session != SESSION_PROGRAMMING &&
target_session != SESSION_EXTENDED) {
response[0] = 0x7F;
response[1] = 0x10;
response[2] = 0x12; /* subFunctionNotSupported */
return 3;
}
/* 检查会话切换是否允许 */
if (!is_transition_allowed(current_session, target_session)) {
response[0] = 0x7F;
response[1] = 0x10;
response[2] = 0x22; /* conditionsNotCorrect */
return 3;
}
/* 执行会话切换 */
current_session = target_session;
session_change_pending = 1; /* 通知应用层需重置P2定时器 */
/* 正响应: SID + 0x40 + 当前会话 */
response[0] = 0x50;
response[1] = (uint8_t)current_session;
resp_len = 2;
/* 扩展会话(0x03)需附加P2*定时器数据(可选, 此处给出默认值) */
if (current_session == SESSION_EXTENDED) {
response[2] = 0x00; /* P2* high byte (默认5000ms) */
response[3] = 0x32; /* P2* low byte = 0x0032 = 50 * 100ms = 5000ms */
resp_len = 4;
}
return resp_len;
}
/* 获取当前会话 */
UdsSessionType get_current_session(void) {
return current_session;
}
/* 测试入口 */
int main(void) {
/* 测试用例 */
struct {
const char *desc;
uint8_t req[2];
uint8_t req_len;
} tests[] = {
{"默认->编程", {0x10, 0x02}, 2},
{"编程->扩展", {0x10, 0x03}, 2},
{"扩展->默认", {0x10, 0x01}, 2},
{"无效子功能", {0x10, 0x04}, 2},
{"请求过短", {0x10}, 1}
};
uint8_t i;
uint8_t response[8];
uint8_t resp_len;
printf("=== UDS 会话控制测试 ===\n");
printf("初始会话: 0x%02X\n\n", get_current_session());
for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
memset(response, 0, sizeof(response));
resp_len = handle_session_control(tests[i].req, tests[i].req_len, response);
printf("[%s] 请求: ", tests[i].desc);
for (uint8_t j = 0; j < tests[i].req_len; j++) {
printf("%02X ", tests[i].req[j]);
}
printf("=> 响应: ");
for (uint8_t j = 0; j < resp_len; j++) {
printf("%02X ", response[j]);
}
printf(" | 当前会话: 0x%02X\n", get_current_session());
}
return 0;
}
编译与运行
gcc -o uds_session uds_session.c
./uds_session
验证方法
程序运行后,观察输出是否符合以下预期:
- 正响应:SID返回0x50(0x10+0x40),第二个字节为切换后的会话ID;切换到扩展会话时,响应额外包含P2*定时器值(0x00 0x32,表示5000ms)。
- 负响应:无效子功能(0x04)返回0x7F 0x10 0x12;请求长度不足返回0x7F 0x10 0x13。
- 会话状态:每次正响应后,当前会话值随之更新,且后续请求基于新会话进行合法性判断。
判定通过条件:所有测试用例的输出与上述规则一致,且会话状态按顺序正确迁移(默认→编程→扩展→默认)。
关键说明
- 本实现覆盖0x10服务的核心逻辑:子功能校验、会话迁移合法性检查、正/负响应生成。实际ECU集成时,需将
current_session与定时器管理、安全等级、通信周期等模块联动,并在session_change_pending置位时由应用层重置P2/P2*定时器。 - 会话切换的允许规则(
transition_table)可根据实际项目裁剪,例如编程会话通常仅允许在特定条件下进入。 - 扩展会话响应中的P2*值(0x0032=50×100ms=5000ms)为示例配置,实际值由ECU性能决定。