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,构造以下输入并检查响应:

  1. 会话切换:发送 {0x10, 0x02}(切换到编程会话),预期响应 {0x50, 0x02, 0x00},且内部状态变为SESSION_PROGRAMMING。
  2. 默认会话读取:在默认会话下发送 {0x22, 0xF1, 0x90},预期响应 {0x62, 0xF1, 0x90, 0x11, 0x22, 0x33}。
  3. 默认会话写入:发送 {0x2E, 0xF1, 0x90, 0xAA, 0xBB, 0xCC},预期响应 {0x6E, 0xF1, 0x90},随后读取DID 0xF190应返回新数据。
  4. 不支持服务:发送 {0x31, 0x01}(例程控制),预期否定响应 {0x7F, 0x31, 0x11}。

通过条件:上述4项测试的响应与预期完全一致,且会话切换后再次发送0x22服务应收到否定响应(0x7F),证明会话状态已正确迁移。

默认会话 ID: 0x01 编程会话 ID: 0x02 扩展会话 ID: 0x03 默认会话允许的服务 ✓ 0x10 会话切换 ✓ 0x22 读取数据 ✓ 0x2E 写入数据 ✗ 其他服务 → 0x7F + NRC 0x11 0x10 02 0x10 03 切换后0x22/0x2E 返回NRC 0x7F

关键说明:上述SVG展示了默认会话的状态位置、允许的服务范围以及通过0x10服务切换到其他会话的路径。切换后,默认会话下的诊断服务(0x22/0x2E)将不再可用,需在对应会话中重新实现或返回否定响应。