3. UDS Bootloader刷写流程

UDS Bootloader刷写流程:请求下载与数据传输 学习卡片

任务目标

实现基于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性能决定。