# UDS CAN Bootloader 工程功能实现详解

> 芯片：云途 YTM32B1MD2（Cortex-M33）
> 编译工具链：GCC arm-none-eabi-gcc（CMake + Ninja 构建系统）
> 协议：ISO 15765-2 CAN-TP + ISO 14229 UDS 诊断协议

---

## 1. 工程整体架构

### 1.1 目录结构分层

| 层级 | 目录 | 功能说明 |
|------|------|----------|
| **应用层** | `app/` | 主入口、Boot跳转逻辑、UDS服务回调、密钥生成算法 |
| **板级层** | `board/` | 时钟、引脚、CAN、DMA、LPTMR、UART等外设板级配置 + 链接脚本 |
| **中间件层** | `middleware/cantp/` | ISO 15765-2 CAN传输协议栈（SF/FF/CF/FC 帧处理） |
| **中间件层** | `middleware/uds/` | ISO 14229 UDS 诊断服务协议栈（服务路由、会话/安全管理） |
| **中间件层** | `middleware/utility_print/` | 串口打印输出模块 |
| **驱动层** | `platform/drivers/` | 云途 SDK 官方外设驱动（FlexCAN、DMA、LPTMR、UART等） |
| **设备层** | `platform/devices/YTM32B1MD2/` | 芯片寄存器定义、启动文件、系统初始化 |
| **构建系统** | `cmake/` | CMake 工具链配置（GCC/AC5/AC6/IAR/GHS） |

### 1.2 软件分层架构图

```
┌──────────────────────────────────────────────────┐
│                  Application (app/)               │
│  main.c │ boot_jump.c │ Uds_Service.c │ KeyGen   │
├──────────────────────────────────────────────────┤
│           UDS Service Callback (uds_ip)          │
│  0x10会话 │ 0x27安全 │ 0x31例程 │ 0x34~0x37读写  │
├──────────────────────────────────────────────────┤
│         UDS Protocol Stack (middleware/uds)      │
│  服务匹配路由 │ 会话管理 │ S3超时 │ 安全等级     │
├──────────────────────────────────────────────────┤
│       CAN-TP Stack (middleware/cantp)            │
│  SF/FF/CF/FC 帧处理 │ FIFO缓冲 │ N_As/N_Bs等超时 │
├──────────────────────────────────────────────────┤
│         FlexCAN Driver (platform/drivers)        │
│  MB（邮箱）配置 │ 发送/接收 │ 中断回调          │
├──────────────────────────────────────────────────┤
│              YTM32B1MD2 Hardware                 │
└──────────────────────────────────────────────────┘
```

---

## 2. 内存布局与地址划分

> 定义文件：[boot_jump.h](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/boot_jump.h#L9-L12)

| 区域 | 起始地址 | 大小 | 说明 |
|------|----------|------|------|
| **Bootloader 区** | `0x00000000` | ~68KB (0x11000) | 当前工程代码运行区域 |
| **APP 标记扇区** | `0x00010E00` | 512B (0x200) | APP 有效性标记（mark_data[0]=1 表示正在升级） |
| **APP 镜像区** | `0x00011000` | ~452KB (至 0x7FFFF) | 应用程序存放位置 |
| **Flash 驱动 RAM** | `0x1FFFB000` | - | Flash 驱动 bin 下载到 RAM 的运行地址（`fls_drv_bin.h`） |
| **RAM 区域** | `0x1FFF0000` | 64KB | 通用 RAM |

关键宏定义：
```c
#define  MCU_SECTOR_SIZE    0x200       // Flash 扇区大小 512B
#define  APP_IMAGE_START    0x11000     // APP 起始地址
#define  APP_IMAGE_LEN_MAX  (0x7FFFF + 1 - APP_IMAGE_START)
```

---

## 3. 启动流程与主循环

### 3.1 启动初始化流程

> 代码位置：[main.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/main.c#L69-L117)

```
复位
 │
 ├─ RCU->RSSR = 0x1                 // 清除复位状态标志
 ├─ 配置 SysTick（1ms 时基，用于随机数种子）
 │
 ├─ Board_Init()
 │   ├─ CLOCK_SYS_Init()           // 时钟初始化（PLL/分频）
 │   ├─ PINS_DRV_Init()            // 引脚复用配置
 │   ├─ UTILITY_PRINT_Init()       // 调试串口初始化
 │   ├─ FLEXCAN_DRV_Init(CAN0)     // CAN 外设初始化（波特率、邮箱）
 │   ├─ lpTMR_DRV_Init(LPTMR0)     // 低功耗定时器（1ms周期中断）
 │   └─ INT_SYS_ConfigInit()       // 中断控制器配置
 │
 ├─ CanTp_Init(&CanTp_Config)      // CAN-TP 协议栈初始化
 ├─ Uds_Init()                     // UDS 协议栈初始化
 ├─ StayInBoot_Init()              // 判断是否强制停留在 Bootloader
 ├─ 使能 LPTMR 中断 + 启动计数
 └─ while(1) 主循环
```

### 3.2 主循环（轮询调度，无 RTOS）

> 代码位置：[main.c#L93-L100](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/main.c#L93-L100)

```c
while (1)
{
    CanTp_MainFunction();    // CAN-TP 状态机推进（收发帧、超时处理）
    Uds_MainFunction();      // UDS 状态机推进（解析请求、调用回调、发送响应）
    StayInBoot_Task();       // 驻留/跳转 APP 状态机
}
```

**调度策略**：裸机轮询（Bare-metal），无 FreeRTOS。所有协议栈非阻塞，依赖 `MainFunction` 周期性调用。

### 3.3 LPTMR 1ms 中断（时间基准）

> 代码位置：[main.c#L123-L133](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/main.c#L123-L133)

```c
void lpTMR0_IRQHandler(void)
{
    CanTp_TimeService();            // 更新 CAN-TP 所有定时器（N_As/N_Bs/N_Cr/STmin）
    Uds_TimeService(1);             // 更新 UDS S3 服务器超时
    lpTMR_DRV_ClearCompareFlag(0);
    ECU_m_num_SysTick++;            // 全局 1ms 计数
    if (ECU_m_num_SysTick % 100 == 0)
        PINS_DRV_TogglePins(GPIOB, 1 << 4);  // 100ms 翻转一次 LED（心跳）
}
```

---

## 4. 驻留/跳转 APP 逻辑（Boot-Jump 状态机）

### 4.1 进入 Boot 时的初始化判断

> 代码位置：[boot_jump.c#L80-L95](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/boot_jump.c#L80-L95)

**StayInBoot_Init() 决策树**：

```
上电进入 Bootloader
 │
 ├─ 读 Flash 标记扇区 (APP_IMAGE_START - 0x200) 的 mark_data[0]
 │   │
 │   ├─ mark_data[0] == 1  ──► 上次升级未完成，强制进入编程会话
 │   │
 │   └─ mark_data[0] != 1  ──► 检查 RAM 保持变量 KeepInBootVar
 │          │
 │          ├─ KeepInBootVar == 1  ──► 强制进入编程会话
 │          └─ KeepInBootVar != 1  ──► 默认会话（等待超时后跳 APP）
```

说明：
- `KeepInBootVar` 放在 `.fbl_bss` section，软复位时 RAM 内容保持，可用于 APP 请求跳回 Boot 后识别。

### 4.2 运行时驻留/跳转任务（StayInBoot_Task）

> 代码位置：[boot_jump.c#L99-L141](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/boot_jump.c#L99-L141)

#### 状态一：默认会话 (UDS_SESSION_DEFAULT)

```
默认会话
 │
 ├─ mark_data[0] != 0? ──是──► 切到编程会话（升级未完成）
 │
 └─ 无 UDS 通信超时 >= DCM_C_T_FBL_POLLING (20ms)?
        └─ 是 ──► 清 KeepInBootVar ──► JumpTo_Application()
```

#### 状态二：编程会话 (UDS_SESSION_PROGRAMMING)

```
编程会话
 │
 ├─ mark_data[0] == 0? （升级标记已清除 = APP 已校验通过）
 │   │
 │   └─ 是：无 UDS 通信超时 >= DCM_C_T_FBL_STAYINBOOT (500ms)?
 │          └─ 是 ──► 清 KeepInBootVar ──► JumpTo_Application()
 │
 └─ mark_data[0] == 1  ──► 保持在 Bootloader，等待继续升级
```

**UDS 通信检测机制**：每次 UDS 收到一帧，`udsRxCallback` 会重置：
- `DCM_m_t_PollingTimer = ECU_m_num_SysTick`（默认会话超时基准）
- `DCM_m_t_StayInTimer = ECU_m_num_SysTick`（编程会话超时基准）

### 4.3 跳转 APP 实现细节

> 代码位置：[boot_jump.c#L44-L73](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/boot_jump.c#L44-L73)

```c
void JumpTo_Application(void)
{
    shutdown_drivers();                         // 1. 关闭外设+关总中断
    appStack = *(volatile uint32_t*)APP_IMAGE_START;       // 2. 读 APP MSP 初始值（向量表第0项）
    appEntry = *(volatile uint32_t*)(APP_IMAGE_START + 4); // 3. 读 APP Reset_Handler（向量表第1项）
    bootup_application(appEntry, appStack);
}

void bootup_application(uint32_t appEntry, uint32_t appStack)
{
    SCB->VTOR = APP_IMAGE_START;                // 4. 重定向向量表到 APP
    __set_PSP(stack_pointer);                   // 5. 设置进程栈指针
    __set_MSP(stack_pointer);                   // 6. 设置主栈指针
    INT_SYS_EnableIRQGlobal();                  // 7. 开总中断（关键！否则APP中断不工作）
    jump_to_application();                      // 8. 跳转至 APP Reset_Handler
}
```

**关键注意点**：
1. `shutdown_drivers()` 关闭了 LPTMR、FlexCAN，并 `DisableIRQGlobal`
2. 跳转前**必须重新开中断**，否则 APP 的 CAN 接收中断不触发
3. 向量表偏移 `SCB->VTOR` 必须设置为 APP 起始地址

---

## 5. CAN-TP 传输层（ISO 15765-2）

### 5.1 CAN-TP 模块分层

| 层 | 文件 | 功能 |
|----|------|------|
| **接口层** | [cantp.h](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/cantp/inc/cantp.h) | 用户 API：Init/Transmit/MainFunction/TimeService |
| **FIFO 层** | `cantp_fifo.c` | 收发环形缓冲（总 2048B，2 个 FIFO） |
| **核心层** | [cantp_core.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/cantp/src/cantp_core.c) | SF/FF/CF/FC 四种帧的状态机 |
| **HAL 层** | `cantp_hal.h` → FlexCAN 驱动 | 实际调用 SDK 的 FLEXCAN_DRV 收发邮箱 |

### 5.2 CAN-TP 帧类型（PCI 字节）

| PCI 高4位 | 帧类型 | 说明 |
|-----------|--------|------|
| `0x0` | SF (Single Frame) | 单帧，数据长度 ≤ 7B（经典 CAN）或 ≤ 62B（CAN FD） |
| `0x1` | FF (First Frame) | 首帧，携带总长度（12bit，最大 4095B） |
| `0x2` | CF (Consecutive Frame) | 连续帧，SN 序号 0~15 循环 |
| `0x3` | FC (Flow Control) | 流控帧：FS(CTS/WAIT/OVFLW) + BS(块大小) + STmin(间隔) |

### 5.3 接收状态机（CAN-TP Core）

> 状态枚举：[cantp_core.c#L11-L32](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/cantp/src/cantp_core.c#L11-L32)

```
CANTP_CORE_RX_WAIT
    │
    ├─ 收到 SF ──► CanTp_Core_RxSF ──► 数据完整 ──► RxIndication（上交 UDS）
    │
    └─ 收到 FF ──► CanTp_Core_RxFF
                     │
                     ├─ 解析总长度，分配 FIFO
                     ├─ 发送 FC(CTS, BS, STmin) ──► CANTP_CORE_TX_FC_WAIT
                     │
                     └─ CANTP_CORE_RX_CF 循环收 CF
                          │
                          ├─ SN 检查 (cnt%16 == SN)
                          ├─ 累计 BS 帧后重新发 FC
                          └─ 收完 ──► RxIndication（上交 UDS）
```

### 5.4 发送状态机（CAN-TP Core）

```
CANTP_CORE_TX_WAIT
    │
    ├─ 数据 < SF_DL ──► CanTp_Core_TxSF ──► TxConfirmation
    │
    └─ 数据 >= SF_DL ──► CanTp_Core_TxFF（发 FF）
                            │
                            └─ CANTP_CORE_RX_FC ──► 等待对端 FC
                                 │
                                 ├─ FS=CTS ──► CanTp_Core_TxCF 循环发 CF
                                 │               ├─ STmin 帧间隔
                                 │               └─ 每发 BS 帧等待下一个 FC
                                 └─ FS=WAIT/OVFLW ──► 超时或错误
```

### 5.5 CAN-TP 关键时间参数（N_xxx）

| 参数 | 含义 | 超时结果 |
|------|------|----------|
| `N_As` | 发送方：发一帧 CAN 帧的超时 | `CANTP_CORE_N_TIMEOUT_A` |
| `N_Ar` | 接收方：发一帧 CAN 帧的超时 | `CANTP_CORE_N_TIMEOUT_A` |
| `N_Bs` | 发送方：等待 FC 帧的超时 | `CANTP_CORE_N_TIMEOUT_Bs` |
| `N_Br` | 接收方：发 FC 帧前的内部超时 | - |
| `N_Cr` | 接收方：等待 CF 帧的超时 | `CANTP_CORE_N_TIMEOUT_Cr` |
| `N_Cs` | 发送方：等待下一个 CF 的内部超时 | - |
| `STmin` | 发送方：两帧 CF 之间最小间隔 | 由 FC 帧携带 |

---

## 6. UDS 诊断服务协议栈

### 6.1 UDS 核心数据结构

> 定义位置：[uds_types.h#L173-L188](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/uds/inc/uds_types.h#L173-L188)

```c
typedef struct {
    uds_u32_t   channel;            // 通道号
    uds_u32_t   channelId;          // 对应 CAN-TP 的 SDU ID
    uds_u32_t   sessionMode;        // 当前会话（位图）
    uds_u32_t   secureLevel;        // 当前安全等级（位图）
    uds_bool_t  serverTimerEnable;  // S3 服务器超时使能
    uds_s32_t   curServerTime;      // S3 当前剩余时间
    uds_state_t channelState;       // IDLE/RECEIVING/PROCESSING/SENDING
    uds_u8_t   *receivedData;       // 接收缓冲区
    uds_u8_t   *transmitData;       // 发送缓冲区
    void       *pendingParam;       // 回调参数（如上传下载状态 udState）
} uds_state_t;
```

### 6.2 UDS 主循环处理流程

> 实现文件：[uds.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/uds/src/uds.c)

```
Uds_MainFunction()
 │
 ├─ 检查 CAN-TP 是否有完整帧（RxIndication 置位）
 │   ├─ YES → 复制数据到 receivedData → 状态 = PROCESSING
 │   │        │
 │   │        ├─ Uds_MatchData() ──► 按 ruleConfig 表匹配 SID+MASK
 │   │        │     │
 │   │        │     ├─ 检查会话权限（sessionMask）
 │   │        │     ├─ 检查安全等级（secureLevelMask）
 │   │        │     └─ 命中 → 调用注册的 callback()
 │   │        │           │
 │   │        │           ├─ Positive → Uds_SendPositiveResponse
 │   │        │           └─ Negative → Uds_SendNegativeResponse
 │   │        │
 │   │        └─ 未匹配/权限不足 → NRC 0x11/0x12/0x33/0x7F
 │   │
 │   └─ NO → 检查 S3 会话超时
 │            └─ 超时 → 切回默认会话，清安全等级
 │
 └─ 发送响应（调用 CAN-TP CanTp_Transmit）
```

### 6.3 UDS 服务路由配置表

> 定义位置：[uds_config.c#L45-L129](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/board/uds_config.c#L45-L129)

| 序号 | 匹配模式 | SID | 会话权限 | 安全等级 | 回调函数 |
|------|----------|-----|----------|----------|----------|
| 0 | `0x10` | **DiagnosticSessionControl** | ALL | ALL | `UDS_IP_SessionA` |
| 1 | `0x3E` | **TesterPresent** | ALL | ALL | `UDS_IP_TesterPresentA` |
| 2 | `0x31 01 FF 00` | **RoutineControl - 擦除Flash** | PROGRAMMING | L1 | `UDS_IP_RoutineControlEraseFlashMemoryA` |
| 3 | `0x34` | **RequestDownload** | PROGRAMMING | L1 | `UDS_IP_RequestDownloadA` |
| 4 | `0x36` | **TransferData** | PROGRAMMING | L1 | `UDS_IP_TransferDataA` |
| 5 | `0x37` | **RequestTransferExit** | PROGRAMMING | L1 | `UDS_IP_RequestTransferExitA` |
| 6 | `0x31 01 02 02` | **RoutineControl - CRC校验** | PROGRAMMING | L1 | `UDS_IP_RoutineControlCrcCheckA` |
| 7 | `0x27` | **SecurityAccess** | PROGRAMMING | ALL | `UDS_IP_SecurityAccessA` |
| 8 | `0x11` | **ECUReset** | DEFAULT/PROGRAMMING/EXTENDED | ALL | `UDS_IP_ECUResetSoftResetA` |

---

## 7. UDS 核心服务详解（uds_ip 层实现）

### 7.1 0x10 DiagnosticSessionControl（会话控制）

> 代码：[Uds_Service.c#L456-L469](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L456-L469)

**请求格式**：`10 XX`（XX = 子功能：01=默认，02=编程，03=扩展）

**响应格式**：`50 XX 00 00 00 00`
- P2_Server_Max = 50ms（默认值，代码硬编码为 0）
- P2*_Server_Max = 5000ms（代码硬编码为 0，需实际使用时补充）

**行为**：调用 `Uds_SetSession()`，启动/重置 S3 服务器超时（5秒）。

### 7.2 0x27 SecurityAccess（安全访问）

> 代码：[Uds_Service.c#L471-L565](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L471-L565)

#### Seed-Key 算法流程

```
客户端                            服务器
  │                                 │
  │── 27 01 (请求种子, 奇数子功能) ──►│
  │                                 │  seed = rand() 基于 SysTick 当前值
  │◄── 67 01 Seed[4] ──────────────│
  │                                 │
  │  本地计算 Key = f(Seed)         │  本地计算 Key = GenerateKeyEx(Seed)
  │                                 │  GenerateKeyEx 实际实现：按位取反 ~Seed
  │                                 │
  │── 27 02 Key[4] (发送密钥) ─────►│
  │                                 │  比较 Key 匹配
  │                                 │  ├─ 匹配：安全等级 L1 解锁
  │◄── 67 02 (正响应) ─────────────│  └─ 不匹配：NRC 0x35 (InvalidKey)，尝试计数+1
  │                                 │     尝试次数 > 5 → NRC 0x36 (ExceedAttempts)
```

**关键细节**：
- **随机种子生成**：使用 `SysTick->VAL` 当前值作为 srand 种子，避免需要真随机数发生器（TRNG）
- **密钥算法**：[GenerateKeyExImpl.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/GenerateKeyExImpl.c#L11-L28) 实现为简单的**按位取反**（`Key[i] = ~Seed[i]`），属于**示例级算法，量产必须更换为 AES-128 或其他强加密算法**
- **尝试次数保护**：连续失败 5 次（`ATTEMPT_CNT_MAX`），返回 NRC 0x36，禁止继续尝试
- **序列检查**：必须先 requestSeed（奇数子功能），才能 sendKey（偶数子功能），否则 NRC 0x24 (RequestSequenceError)

### 7.3 0x31 RoutineControl - 擦除 Flash（例程 0xFF00）

> 代码：[Uds_Service.c#L567-L617](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L567-L617)

**请求**：`31 01 FF 00`（启动例程，ID = 0xFF00）

**执行流程**：
```
1. 检查/写入 升级标记 mark_data[0] = 1 到 APP_IMAGE_START - 0x200 扇区
   （防止升级中途掉电后直接跳到 APP）

2. 按扇区 (512B) 循环擦除 APP 区（0x11000 ~ 0x7FFFF）：
   ┌─────────────────────────────────┐
   │ 每次擦除 MCU_SECTOR_SIZE (512B) │
   │ 返回 NRC 0x78 (RCRRP) 表示未完成│
   │ 客户端需要用 0x31 轮询直到完成  │
   └─────────────────────────────────┘

3. 全部擦完 → 正响应：71 01 FF 00
```

**RCRRP（0x78）机制**：单次擦除耗时较长，Bootloader 不在中断中阻塞等待，而是通过多次返回 0x78 告知客户端"正在执行，稍后重试"，这是 UDS 标准的长耗时服务模式。

### 7.4 0x34 RequestDownload（请求下载）

> 代码：[Uds_Service.c#L619-L694](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L619-L694)

**请求格式**：
```
34 DFI (ALF=nnss) [Address n bytes] [Size s bytes]
│    │   │
│    │   └─ addrLen = s&0xF, sizeLen = (s>>4)&0xF
│    └─ DataFormatIdentifier = 00（无加密无压缩）
└─ SID
```

**校验步骤**：
1. DFI 必须 = 0x00（不支持加密/压缩）
2. 地址+长度格式符：addrLen/sizeLen 最多各 4 字节
3. 地址范围合法性检查：调用 `getMemoryTypeA()`
   - YTM32B1MD2 Flash：0x00000000 ~ 0x0007FFFF
   - YTM32B1MD2 RAM：0x1FFF0000 ~ 0x1FFFFFFF
4. 保存状态到 `udState[channel]`：address/size/leftSize/bsc=1/started=true

**响应格式**：`74 40 00 00 00 82`
- 0x40 = 长度格式符（maxNumberOfBlockLength = 4 字节）
- 0x00000082 = UDS_MAX_BLOCK_SIZE = 130 字节（1B BSC + 129B 数据）

### 7.5 0x36 TransferData（数据传输）

> 代码：[Uds_Service.c#L698-L832](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L698-L832)

**请求格式**：`36 BSC [Data...]`

**下载写 Flash 流程**：
```
收到 TransferData
  │
  ├─ 参数检查：udState.started / BSC 匹配 / 长度合法
  │
  ├─ 内存类型 = FLASH?
  │   └─ YES：
  │      ┌──────────────────────────────────────────┐
  │      │ writeFlashA(address, data, len)          │
  │      │   ├─ 8B 对齐检查（EFM 写入粒度）         │
  │      │   ├─ 分块写入，每次 UDS_PROGRAM_CHUNK_SIZE│
  │      │   │   (默认 256B)                         │
  │      │   ├─ 每块内按 8B(2个uint32) 循环写入     │
  │      │   ├─ 写前关中断，写后开中断               │
  │      │   └─ 单次未写完 → 返回 0x78 RCRRP        │
  │      └──────────────────────────────────────────┘
  │
  ├─ 内存类型 = RAM?
  │   └─ YES：直接 memcpy（实时完成，无 RCRRP）
  │
  ├─ 成功：76 BSC ──► bsc++，leftSize -= len
  └─ 失败：7F 36 NRC（GPF/WBSC/RSE/ROOR 等）
```

**Flash 写入函数分两种模式**（条件编译）：
1. `UDS_BUILD_IN_FLASH_ENABLE`：使用 SDK 内置 `FLASH_DRV_Program()`
2. **默认模式**（当前工程）：使用 `fls_drv_tbl_t` 函数表，调用**下载到 RAM 中的 Flash 驱动 bin**（地址 `0x1FFFB000`）。这是云途系列芯片为规避 Flash 擦写"自修改"问题的标准做法：Bootloader 本身在 Flash 中运行，擦写 Flash 时需要将驱动搬到 RAM 执行。

### 7.6 0x37 RequestTransferExit（传输退出）

> 代码：[Uds_Service.c#L837-L886](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L837-L886)

检查条件：
- `udState.started == true`
- `udState.leftSize == 0`（全部数据传输完毕）

通过 → `77`（正响应），清理 udState。

### 7.7 0x31 RoutineControl - CRC 校验（例程 0x0202）

> 代码：[Uds_Service.c#L889-L967](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L889-L967)

**请求**：`31 01 02 02 [CRC期望值 2B]`

**执行流程**：
```
1. 使用 RequestDownload 时保存的 udState.address / udState.size
2. crcCheck() 计算 CRC-16/MODBUS (多项式 0xA001，初值 0xFFFF)
3. 与请求末尾携带的 CRC 期望值比较
4. 匹配成功：
   ├─ 清除升级标记 mark_data[0] = 0（允许后续跳 APP）
   └─ 返回 71 01 02 02
5. 不匹配：返回 NRC 0x72 (GPF)
```

**重要意义**：CRC 校验通过 = APP 镜像完整性验证通过，此时才清除 `mark_data[0]` 标记，确保只有完整且正确的 APP 才会被执行。

### 7.8 0x11 ECUReset（ECU 复位）

> 代码：[Uds_Service.c#L971-L989](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L971-L989)

当前实现：仅返回正响应 `51 01`，未实际执行复位。代码中 `resetCommand = uds_true` 被注释，实际使用时需要在主循环中检测该变量并调用 `SystemSoftwareReset()`。

### 7.9 0x3E TesterPresent（测试设备在线）

> 代码：[Uds_Service.c#L445-L451](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L445-L451)

标准心跳保活服务。请求 `3E 00/80`，响应 `7E 00/80`。子功能最高位=1 表示无响应（仅保活不回复）。

---

## 8. Flash 操作底层实现

### 8.1 函数表结构（RAM 驱动模式）

> 定义：[fls_drv_bin.h](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/uds/inc/fls_drv_bin.h)

```c
typedef struct {
    uint32_t init;         // FLASH_Init 函数偏移
    uint32_t eraseSector;  // FLASH_EraseSector 函数偏移
    uint32_t program;      // FLASH_Program 函数偏移
} fls_drv_tbl_t;

static fls_drv_tbl_t *pfls_drv_tbl = (fls_drv_tbl_t *)0x1FFFB000;
```

调用时使用函数指针 + 偏移跳转：
```c
status = ((FLASH_Init_t)((uint32_t)(pfls_drv_tbl->init) + (uint32_t)pfls_drv_tbl))();
```

### 8.2 Flash 写入 8B 对齐要求

云途 EFM（嵌入式 Flash 控制器）的写入单位是 8 字节，要求：
- 地址 8B 对齐
- 长度 8B 倍数

不满足条件时直接返回错误（`UDS_NRC_GPF`）。

### 8.3 临界区处理

擦写操作前后：
```c
INT_SYS_DisableIRQGlobal();   // 关总中断
FLASH_DRV_Program(...);
INT_SYS_EnableIRQGlobal();    // 开总中断
```

原因：Flash 擦写期间读取 Flash 会导致总线阻塞（stall），中断如果在此时触发访问 Flash（如取指/查表）会导致异常。

---

## 9. 升级完整流程（上位机视角）

```
┌─────────────────────────────────────────────────────────────────────┐
│                     完整 UDS OTA 升级流程                            │
└─────────────────────────────────────────────────────────────────────┘

1. [握手] 上电或复位 → Bootloader → 20ms 无通信 → 尝试跳 APP
                     │
                     └─ 如果需要升级：在 20ms 内发送
                        10 02  ──► 切换到编程会话

2. [解锁] 27 01           ──► 请求种子
           ◄── 67 01 SEED
         27 02 KEY         ──► 发送密钥（Seed按位取反）
           ◄── 67 02              解锁安全等级 L1

3. [擦除] 31 01 FF 00      ──► 启动 Flash 擦除例程
           ◄── 7F 31 78          （循环多次 RCRRP：擦除耗时较长）
           ◄── ...
           ◄── 71 01 FF 00        擦除完成

4. [下载] 34 00 44 00 01 10 00 06 EF 00  ──► 请求下载（地址0x11000，大小0x6EF00）
           ◄── 74 40 00 00 00 82                  块大小 130B

         循环发送 TransferData：
         36 01 DATA[0..128]  ──►
           ◄── 76 01
         36 02 DATA[129..257] ──►
           ◄── 76 02
         ...
         （直到 leftSize == 0）

5. [退传输] 37              ──► 请求退出传输
           ◄── 77

6. [校验] 31 01 02 02 CRC_H CRC_L  ──► CRC16 校验
           ◄── 71 01 02 02                 校验通过，清除升级标记

7. [可选复位] 11 01        ──► 软复位
           ◄── 51 01

8. [跳 APP] 编程会话 500ms 无通信 → 自动跳转至 APP（0x11000）
```

---

## 10. 关键超时参数汇总

| 参数 | 值 | 文件位置 | 含义 |
|------|-----|----------|------|
| `DCM_C_T_FBL_POLLING` | 20 ms | [Uds_Service.h#L7](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.h#L7) | 默认会话超时，无通信后跳 APP |
| `DCM_C_T_FBL_STAYINBOOT` | 500 ms | [Uds_Service.h#L8](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.h#L8) | 编程会话超时，无通信后跳 APP |
| `DCM_C_T_P2SERVER_MAX` | 50 ms | [Uds_Service.h#L5](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.h#L5) | UDS P2 服务器响应超时 |
| `DCM_C_T_P2_STAR_MAX` | 5000 ms | [Uds_Service.h#L6](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.h#L6) | UDS P2* 扩展响应超时 |
| S3 服务器超时 | 5000 ms | [uds_config.c#L138](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/board/uds_config.c#L138) | 非默认会话自动退回超时 |
| `ATTEMPT_CNT_MAX` | 5 次 | [Uds_Service.c#L35](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c#L35) | 安全访问最大尝试次数 |

---

## 11. 安全风险与改进建议

### ⚠️ 当前实现中的安全问题（必须注意）

| 问题 | 位置 | 风险 | 建议 |
|------|------|------|------|
| **密钥算法过弱** | `GenerateKeyExImpl.c` 按位取反 | 逆向即可获取 Key，无安全性 | 量产更换为 AES-128-CMAC 或 HMAC-SHA256 |
| **种子可预测** | `SysTick->VAL` 作为 srand 种子 | 上电初期 SysTick 值范围有限，可爆破 | 使用芯片 TRNG（真随机数发生器）生成种子 |
| **无 Secure Boot** | `JumpTo_Application` 直接跳转 | APP 被恶意篡改仍可执行 | 启用云途 HCU 安全启动，校验 APP 签名 |
| **无回滚保护** | CRC 仅校验完整性，不比对版本 | 可刷入旧版本有漏洞的 APP | 在镜像头部增加版本号，Bootloader 比较拒绝降级 |
| **无通信加密** | CAN 帧明文传输 | 升级数据可被嗅探/篡改 | 使用 SecOC（Security on-board Communication）加 MAC |
| **Flash 驱动地址硬编码** | `0x1FFFB000` | 若 RAM 布局变化，跳转至非法地址崩溃 | 改为链接脚本符号或运行时地址传递 |

### 性能/稳定性建议

1. **Flash 擦写失败重试**：当前擦写一次失败直接返回 NRC，建议增加 3 次重试
2. **擦除/写入后 verify**：写完后回读对比，防止写入不完全
3. **APP 向量表校验**：跳转前检查 MSP 值是否在合法 RAM 范围，避免野指针
4. **看门狗**：当前无看门狗喂狗逻辑，长操作（整片擦除）可能触发 WDT 复位

---

## 12. 编译与构建

### 12.1 构建命令

```powershell
cd uds_can_bootloader
rmdir /s /q build
mkdir build && cd build
cmake -DCMAKE_PREFIX_PATH="." ^
      -DCMAKE_TOOLCHAIN_FILE="../cmake/gcc.cmake" ^
      -DARM_CPU="cortex-m33" ^
      -G "Ninja" ..
ninja
ninja genhex      # 生成 .hex
ninja genlist     # 生成 .lst 反汇编
```

### 12.2 工具链优化选项

文件：[GCC.cmake](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/cmake/Toolchain/GCC.cmake#L33-L52)
- 默认为 `-O1` 优化
- 调试时可改为 `-O0`（用户已在 CMakeLists.txt USER CODE 区域追加 `-O0` 覆盖）
- 强制开启 `-Wall -Werror=all`（警告变错误，但部分警告白名单豁免，如 unused 类）

---

## 13. 工程源文件索引

| 功能模块 | 主要文件 |
|----------|----------|
| 启动与主循环 | [main.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/main.c) |
| Boot 跳转逻辑 | [boot_jump.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/boot_jump.c) / [boot_jump.h](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/boot_jump.h) |
| UDS 服务回调 | [Uds_Service.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.c) / [Uds_Service.h](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/Uds_Service.h) |
| 密钥生成算法 | [GenerateKeyExImpl.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/app/GenerateKeyExImpl.c) |
| UDS 服务路由 | [uds_config.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/board/uds_config.c) |
| CAN-TP 配置 | [cantp_cfg.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/board/cantp_cfg.c) |
| UDS 协议核心 | [uds.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/uds/src/uds.c) |
| UDS IP 接口 | [uds_ip.h](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/uds/inc/uds_ip.h) |
| CAN-TP 核心 | [cantp_core.c](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/cantp/src/cantp_core.c) |
| CAN-TP 接口 | [cantp.h](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/middleware/cantp/inc/cantp.h) |
| 链接脚本 | [yt_linker.ld](file:///c:/Users/song3/Desktop/UDS/uds_can_fbl_md24_release_20260828/uds_can_bootloader/board/yt_linker.ld) |
