4. UDS协议栈集成

任务目标

在Bootloader中集成UDS(ISO 14229)协议栈,实现两种基础诊断能力:会话控制(0x10服务)和安全访问(0x27服务)。Bootloader通过CAN总线接收UDS请求,完成会话模式切换及安全解锁验证,为后续刷写流程提供前置条件。

实现方案

采用轻量级状态机方式实现UDS协议核心,不依赖第三方协议栈,便于在Bootloader环境中直接编译运行。CAN驱动层通过回调函数接收报文,UDS层解析并响应。安全访问算法使用简单的种子-密钥机制(实际项目中可替换为AES或自定义算法)。

关键代码

/* uds_stack.h */
#ifndef UDS_STACK_H
#define UDS_STACK_H

#include <stdint.h>

/* 诊断会话类型 */
#define SESSION_DEFAULT   0x01
#define SESSION_PROGRAM   0x02

/* 安全访问状态 */
#define SEC_LOCKED        0x00
#define SEC_UNLOCKED      0x01

/* UDS服务ID */
#define SID_SESSION       0x10
#define SID_SECURITY      0x27

/* 响应码 */
#define RESP_OK           0x00
#define RESP_SFNS         0x12  /* 服务不支持 */
#define RESP_ISNS         0x13  /* 无效长度 */
#define RESP_COND_NOT_OK  0x22
#define RESP_SEC_ACCESS   0x33  /* 安全访问被拒绝 */

typedef struct {
    uint8_t session;      /* 当前会话 */
    uint8_t sec_status;   /* 安全状态 */
    uint8_t sec_seed[4];  /* 种子值 */
} UdsContext;

/* CAN报文回调接口(由底层驱动调用) */
void Uds_OnCanMessage(uint32_t id, uint8_t* data, uint8_t len);

/* 发送CAN响应(由底层驱动实现) */
extern void Can_SendMessage(uint32_t id, uint8_t* data, uint8_t len);

#endif
/* uds_stack.c */
#include "uds_stack.h"
#include <string.h>

static UdsContext ctx;

/* 安全密钥生成算法(示例:种子取反+固定偏移) */
static void GenerateKey(const uint8_t* seed, uint8_t* key) {
    for (int i = 0; i < 4; i++) {
        key[i] = (uint8_t)(~seed[i] + 0x5A);
    }
}

static void SendResponse(uint8_t sid, uint8_t* data, uint8_t len) {
    uint8_t resp[8];
    resp[0] = (uint8_t)(sid + 0x40);  /* 肯定响应SID */
    memcpy(&resp[1], data, len);
    Can_SendMessage(0x7E8, resp, (uint8_t)(len + 1));
}

static void SendNrc(uint8_t sid, uint8_t nrc) {
    uint8_t resp[3];
    resp[0] = 0x7F;
    resp[1] = sid;
    resp[2] = nrc;
    Can_SendMessage(0x7E8, resp, 3);
}

/* 会话控制服务 (0x10) */
static void HandleSession(uint8_t* data, uint8_t len) {
    if (len != 2) {
        SendNrc(SID_SESSION, RESP_ISNS);
        return;
    }
    uint8_t new_session = data[1];
    if (new_session != SESSION_DEFAULT && new_session != SESSION_PROGRAM) {
        SendNrc(SID_SESSION, RESP_SFNS);
        return;
    }
    ctx.session = new_session;
    /* 切换会话时锁定安全状态 */
    ctx.sec_status = SEC_LOCKED;
    uint8_t resp_data[1] = { new_session };
    SendResponse(SID_SESSION, resp_data, 1);
}

/* 安全访问服务 (0x27) */
static void HandleSecurity(uint8_t* data, uint8_t len) {
    if (ctx.session != SESSION_PROGRAM) {
        SendNrc(SID_SECURITY, RESP_COND_NOT_OK);
        return;
    }
    if (len < 2) {
        SendNrc(SID_SECURITY, RESP_ISNS);
        return;
    }
    uint8_t sub_func = data[1];

    if (sub_func == 0x01) {  /* 请求种子 */
        if (ctx.sec_status == SEC_UNLOCKED) {
            SendNrc(SID_SECURITY, RESP_COND_NOT_OK);
            return;
        }
        /* 生成随机种子(示例用固定值,实际应使用随机数) */
        ctx.sec_seed[0] = 0x12;
        ctx.sec_seed[1] = 0x34;
        ctx.sec_seed[2] = 0x56;
        ctx.sec_seed[3] = 0x78;
        uint8_t resp_data[5];
        resp_data[0] = sub_func;
        memcpy(&resp_data[1], ctx.sec_seed, 4);
        SendResponse(SID_SECURITY, resp_data, 5);
    }
    else if (sub_func == 0x02) {  /* 发送密钥 */
        if (len != 6) {
            SendNrc(SID_SECURITY, RESP_ISNS);
            return;
        }
        uint8_t expected_key[4];
        GenerateKey(ctx.sec_seed, expected_key);
        if (memcmp(&data[2], expected_key, 4) == 0) {
            ctx.sec_status = SEC_UNLOCKED;
            uint8_t resp_data[1] = { sub_func };
            SendResponse(SID_SECURITY, resp_data, 1);
        } else {
            ctx.sec_status = SEC_LOCKED;
            SendNrc(SID_SECURITY, RESP_SEC_ACCESS);
        }
    }
    else {
        SendNrc(SID_SECURITY, RESP_SFNS);
    }
}

/* CAN报文入口 */
void Uds_OnCanMessage(uint32_t id, uint8_t* data, uint8_t len) {
    if (id != 0x7E0 || len < 2) return;  /* 仅处理物理请求 */
    uint8_t sid = data[0];
    switch (sid) {
        case SID_SESSION:
            HandleSession(data, len);
            break;
        case SID_SECURITY:
            HandleSecurity(data, len);
            break;
        default:
            SendNrc(sid, RESP_SFNS);
            break;
    }
}

/* 初始化 */
void Uds_Init(void) {
    ctx.session = SESSION_DEFAULT;
    ctx.sec_status = SEC_LOCKED;
}

集成与运行

依赖说明:需要底层CAN驱动提供Can_SendMessage()发送函数,并在收到CAN报文时调用Uds_OnCanMessage()。初始化时调用Uds_Init()。

编译:将uds_stack.c加入工程,包含头文件路径,与CAN驱动一起编译。

测试序列:

  1. 发送10 02 → 切换到编程会话,预期响应50 02
  2. 发送27 01 → 请求种子,预期响应67 01 12 34 56 78
  3. 计算密钥:对12 34 56 78取反加0x5A → ED A9 A3 21
  4. 发送27 02 ED A9 A3 21 → 预期响应67 02
  5. 发送错误密钥27 02 00 00 00 00 → 预期响应7F 27 33

验证方法:使用CAN工具(如PCAN、周立功CANTest)连接ECU,按上述序列发送报文,观察CAN总线上的响应帧ID为0x7E8。若响应与预期一致,且错误密钥被拒绝,则判定通过。

常见问题

  • 响应超时:确认CAN波特率匹配、ID过滤配置正确(接收0x7E0,发送0x7E8)。
  • 安全访问被拒:确认已先切换到编程会话(0x10 02),且种子与密钥计算逻辑一致。
  • 多帧传输:本实现仅支持单帧(≤8字节),UDS超过8字节的请求需扩展ISO-TP层,当前案例不涉及。