2. UDS协议栈初始化

任务目标

本案例完成UDS协议栈的最小可运行初始化:配置CAN控制器、注册UDS会话层回调,并验证协议栈能够正确接收和发送UDS消息。实现基于STM32F407 + CAN1外设,使用标准UDS寻址(物理请求ID 0x7E0,物理响应ID 0x7E8)。

核心实现

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

#include <stdint.h>

#define UDS_CAN_ID_REQUEST  0x7E0
#define UDS_CAN_ID_RESPONSE 0x7E8

typedef struct {
    uint8_t data[8];
    uint8_t len;
} CanFrame;

typedef void (*UdsRxCallback)(const uint8_t* data, uint8_t len);

void UDS_Init(UdsRxCallback rx_cb);
void UDS_SendResponse(const uint8_t* data, uint8_t len);
void UDS_Poll(void);  /* 在CAN接收中断或主循环中调用 */

#endif

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

/* 模拟CAN硬件层(实际工程替换为HAL_CAN_Transmit/Receive) */
static void Can_HwInit(void) {
    /* 配置CAN1 500kbps, 中断使能 */
    /* 实际接入点:HAL_CAN_Start(&hcan1); HAL_CAN_ActivateNotification(...) */
}

static void Can_SendFrame(uint32_t id, const uint8_t* data, uint8_t len) {
    /* 实际接入点:HAL_CAN_AddTxMessage(&hcan1, &txHeader, data, &mailbox) */
    (void)id; (void)data; (void)len;
}

static UdsRxCallback rx_callback = 0;
static uint8_t session_active = 0;

void UDS_Init(UdsRxCallback rx_cb) {
    rx_callback = rx_cb;
    session_active = 1;  /* 默认进入默认会话 */
    Can_HwInit();
}

void UDS_SendResponse(const uint8_t* data, uint8_t len) {
    if (len > 8) len = 8;  /* CAN FD场景可扩展 */
    Can_SendFrame(UDS_CAN_ID_RESPONSE, data, len);
}

void UDS_Poll(void) {
    CanFrame frame;
    /* 从硬件接收队列取帧(实际工程由中断回调填充队列) */
    if (Can_ReceiveFrame(&frame) && frame.id == UDS_CAN_ID_REQUEST) {
        if (rx_callback && session_active) {
            rx_callback(frame.data, frame.len);
        }
    }
}

/* 测试入口 */
#include <assert.h>

static uint8_t test_rx_data[8];
static uint8_t test_rx_len = 0;

static void TestRxHandler(const uint8_t* data, uint8_t len) {
    memcpy(test_rx_data, data, len);
    test_rx_len = len;
}

void UDS_InitTest(void) {
    UDS_Init(TestRxHandler);
    /* 模拟收到诊断请求 0x10 0x01 (会话控制) */
    uint8_t req[] = {0x10, 0x01};
    CanFrame frame = {.id = UDS_CAN_ID_REQUEST, .len = 2};
    memcpy(frame.data, req, 2);
    Can_InjectFrame(&frame);  /* 测试辅助函数,注入帧到接收队列 */
    UDS_Poll();
    assert(test_rx_len == 2);
    assert(test_rx_data[0] == 0x10 && test_rx_data[1] == 0x01);
    /* 发送响应验证 */
    uint8_t resp[] = {0x50, 0x01};
    UDS_SendResponse(resp, 2);
    /* 检查CAN发送队列中最后一帧ID为0x7E8且数据匹配 */
    assert(Can_GetLastTxId() == UDS_CAN_ID_RESPONSE);
}

运行与验证

环境依赖:STM32CubeIDE + HAL库(或任意支持CAN的MCU平台),需提供Can_ReceiveFrame、Can_InjectFrame、Can_GetLastTxId三个硬件适配函数(测试辅助用)。

编译运行:

# 在CubeIDE中编译并烧录,或使用命令行:
arm-none-eabi-gcc -I. -c uds_stack.c -o uds_stack.o
# 链接时需包含硬件抽象层文件

验证步骤:

  1. 调用UDS_InitTest()执行内置自测;
  2. 若断言全部通过,说明协议栈初始化正确,CAN ID过滤和会话层回调工作正常;
  3. 实际硬件验证:用CAN工具发送0x7E0上的10 01,观察0x7E8上是否收到响应(需在回调中实现响应逻辑)。

通过标准:自测断言全部通过,且CAN工具能收到协议栈发出的响应帧(ID=0x7E8)。