4. 基于UDS的Bootloader诊断会话控制

任务目标

本案例实现基于UDS(ISO 14229)的Bootloader诊断会话控制:支持默认会话(Default Session,0x01)与编程会话(Programming Session,0x02)之间的切换,并实现会话超时自动回退到默认会话的逻辑。该功能是Bootloader刷写流程中进入编程模式的前置条件。

核心实现

以下代码实现UDS 0x10(DiagnosticSessionControl)服务的完整处理逻辑,包含会话状态管理、切换合法性校验及超时定时器。代码可直接集成到AUTOSAR Bootloader的CanTp/CAN驱动上层。

/* uds_session_control.h */
#ifndef UDS_SESSION_CONTROL_H
#define UDS_SESSION_CONTROL_H

#include <stdint.h>
#include <stdbool.h>

/* UDS会话ID定义 */
#define UDS_SESSION_DEFAULT        0x01
#define UDS_SESSION_PROGRAMMING    0x02

/* 会话超时时间(毫秒),编程会话默认5秒无请求则回退 */
#define SESSION_TIMEOUT_MS         5000u

/* UDS响应码 */
#define UDS_RC_POSITIVE            0x00
#define UDS_RC_SFNS                0x12  /* 服务不支持 */
#define UDS_RC_SFNSIAS             0x13  /* 子功能不支持 */
#define UDS_RC_CNC                 0x22  /* 条件不满足 */

typedef enum {
    SESSION_STATE_DEFAULT = 0,
    SESSION_STATE_PROGRAMMING
} SessionStateType;

typedef struct {
    SessionStateType currentSession;
    uint32_t lastRequestTime;   /* 上次诊断请求时间戳(ms) */
    uint32_t sessionTimeoutMs;
} SessionControlType;

/* 初始化会话控制模块 */
void SessionControl_Init(SessionControlType* sc);

/* 处理UDS 0x10服务请求,返回响应数据长度 */
uint8_t SessionControl_HandleRequest(
    SessionControlType* sc,
    const uint8_t* requestData,
    uint8_t requestLen,
    uint8_t* responseData,
    uint8_t* responseLen);

/* 周期调用(建议1ms周期),处理会话超时 */
void SessionControl_MainFunction(SessionControlType* sc, uint32_t currentTimeMs);

#endif /* UDS_SESSION_CONTROL_H */
/* uds_session_control.c */
#include "uds_session_control.h"

void SessionControl_Init(SessionControlType* sc)
{
    sc->currentSession = SESSION_STATE_DEFAULT;
    sc->lastRequestTime = 0u;
    sc->sessionTimeoutMs = SESSION_TIMEOUT_MS;
}

uint8_t SessionControl_HandleRequest(
    SessionControlType* sc,
    const uint8_t* requestData,
    uint8_t requestLen,
    uint8_t* responseData,
    uint8_t* responseLen)
{
    uint8_t subFunc;
    uint8_t rc = UDS_RC_POSITIVE;

    /* 检查SID是否为0x10 */
    if ((requestLen < 2u) || (requestData[0] != 0x10u)) {
        return UDS_RC_SFNS;
    }

    subFunc = requestData[1];

    /* 根据当前会话状态处理子功能 */
    switch (sc->currentSession) {
    case SESSION_STATE_DEFAULT:
        if (subFunc == UDS_SESSION_PROGRAMMING) {
            /* 默认会话 -> 编程会话:允许切换 */
            sc->currentSession = SESSION_STATE_PROGRAMMING;
            sc->lastRequestTime = 0u;  /* 超时计时从切换后开始 */
        } else if (subFunc == UDS_SESSION_DEFAULT) {
            /* 默认会话请求默认会话:正响应,无状态变化 */
        } else {
            rc = UDS_RC_SFNSIAS;
        }
        break;

    case SESSION_STATE_PROGRAMMING:
        if (subFunc == UDS_SESSION_DEFAULT) {
            /* 编程会话 -> 默认会话:允许切换 */
            sc->currentSession = SESSION_STATE_DEFAULT;
        } else if (subFunc == UDS_SESSION_PROGRAMMING) {
            /* 编程会话重复请求:刷新超时计时 */
            sc->lastRequestTime = 0u;
        } else {
            rc = UDS_RC_SFNSIAS;
        }
        break;

    default:
        rc = UDS_RC_CNC;
        break;
    }

    /* 构建正响应:SID + 0x40 + 当前会话ID */
    if (rc == UDS_RC_POSITIVE) {
        responseData[0] = 0x50u;  /* 0x10 + 0x40 */
        responseData[1] = (uint8_t)sc->currentSession;
        *responseLen = 2u;
    } else {
        /* 负响应:SID + 0x7F + 响应码 */
        responseData[0] = 0x7Fu;
        responseData[1] = 0x10u;
        responseData[2] = rc;
        *responseLen = 3u;
    }

    return rc;
}

void SessionControl_MainFunction(SessionControlType* sc, uint32_t currentTimeMs)
{
    /* 仅在编程会话下检查超时 */
    if (sc->currentSession == SESSION_STATE_PROGRAMMING) {
        if (sc->lastRequestTime == 0u) {
            sc->lastRequestTime = currentTimeMs;
        } else if ((currentTimeMs - sc->lastRequestTime) >= sc->sessionTimeoutMs) {
            /* 超时:回退到默认会话 */
            sc->currentSession = SESSION_STATE_DEFAULT;
            sc->lastRequestTime = 0u;
        }
    }
}

运行与集成方式

  1. 依赖:需要底层CAN传输层(CanTp)提供接收/发送接口,本代码仅实现会话控制逻辑,不涉及具体CAN驱动。
  2. 调用路径:CanTp收到完整UDS请求后,将数据指针和长度传入SessionControl_HandleRequest(),获取响应后通过CanTp发送回Tester。
  3. 周期调度:在Bootloader主循环或定时中断中,以1ms周期调用SessionControl_MainFunction(),传入当前系统时间戳。

验证方法

使用CANoe或PCAN发送以下UDS帧序列进行验证:

// 测试序列(CAN ID 0x7E0 -> ECU,0x7E8 -> Tester)
// 1. 请求编程会话
发送: 02 10 02 00 00 00 00 00
预期: 03 50 02 00 00 00 00 00  (正响应,进入编程会话)

// 2. 等待6秒(超过5秒超时),发送默认会话请求
发送: 02 10 01 00 00 00 00 00
预期: 03 50 01 00 00 00 00 00  (正响应,已自动回退到默认会话)

// 3. 在默认会话下请求编程会话
发送: 02 10 02 00 00 00 00 00
预期: 03 50 02 00 00 00 00 00  (正响应)

// 4. 在编程会话下请求不支持的会话ID (0x03)
发送: 02 10 03 00 00 00 00 00
预期: 03 7F 10 13 00 00 00 00  (负响应,子功能不支持)

通过条件:上述4个步骤的响应与预期完全一致,且步骤2中等待6秒后ECU自动回到默认会话(可通过发送0x10 0x01请求验证当前会话为默认)。

UDS会话状态切换图 默认会话 Session 0x01 编程会话 Session 0x02 0x10 0x02 0x10 0x01 超时(5s)自动回退 0x10 0x02 刷新计时 实线:请求切换 虚线:超时回退 自循环:刷新超时计时

常见问题

  • 超时计时不准确:确认SessionControl_MainFunction()的调用周期稳定,且传入的currentTimeMs为单调递增的系统毫秒计数。
  • 编程会话下收到0x10 0x01后立即回默认:这是正确行为,Tester主动退出编程会话时需先完成刷写并发送ECU复位请求。
  • 负响应0x13:表示当前会话下不支持请求的子功能,检查是否在默认会话下请求了0x10 0x02以外的值。