203 lines
9.7 KiB
C
203 lines
9.7 KiB
C
#ifndef __APP_IAP_H__
|
|
#define __APP_IAP_H__
|
|
#include "main.h"
|
|
#include <stdint.h>
|
|
|
|
/* ============================================================
|
|
* STM32F103C8T6 IAP 模块 (基于 CAN + UDS 协议)
|
|
*
|
|
* Flash 分区:
|
|
* 0x08000000 - 0x08003FFF Bootloader (16KB)
|
|
* 0x08004000 - 0x0800EFFF Application (44KB)
|
|
* 0x0800F000 - 0x0800FFFF 用户参数 (4KB)
|
|
*
|
|
* 协议流程: 先握手, 再传数据
|
|
* 阶段1 握手:
|
|
* 0x10 0x02 → 0x50 0x02 进入扩展会话
|
|
* 0x22 F1 00 → 0x62 F1 00 读版本/握手
|
|
* 0x27 01 → 0x67 01 安全访问(请求Seed)
|
|
* 0x27 02 → 0x67 02 安全访问(发送Key)
|
|
* 阶段2 数据传输:
|
|
* 0x34 [size] → 0x74 请求下载(声明固件大小)
|
|
* 0x36 01 → 0x76 数据传输开始(此后每帧8字节纯数据)
|
|
* [8字节数据] → (无响应) 连续传输,直到收够size字节
|
|
* 0x37 → 0x77 传输退出
|
|
* 阶段3 校验烧写:
|
|
* 0x31 01 FF → 0x71 01 FF 触发校验+烧写
|
|
* 0x11 01 → 0x51 01 跳转执行APP
|
|
* ============================================================ */
|
|
|
|
/* ============================================================
|
|
* Flash 存储分区定义 (每页 1KB, STM32F1 标准页大小)
|
|
* Bootloader : 0x08000000 - 0x08005FFF (24KB, 24页, 不可升级)
|
|
* Application : 0x08006000 - 0x0800EFFF (36KB, 36页, IAP 目标区)
|
|
* 参数区 : 0x0800F000 - 0x0800FFFF (4KB, 4页, 升级标志+用户参数)
|
|
* ============================================================ */
|
|
#define IAP_BOOTLOADER_ADDR 0x08000000U /* Bootloader 起始地址 */
|
|
#define IAP_BOOTLOADER_SIZE (24U * 1024U) /* Bootloader 大小: 24KB */
|
|
#define IAP_APP_ADDR 0x08006000U /* 应用程序起始地址(IAP 写入起点) */
|
|
#define IAP_APP_SIZE (36U * 1024U) /* 应用程序区大小: 36KB */
|
|
#define IAP_APP_PAGE_NUM 36U /* 应用程序区页数(每页1KB) */
|
|
#define IAP_PARAM_ADDR 0x0800F000U /* 参数区起始地址 */
|
|
#define IAP_PARAM_SIZE (4U * 1024U) /* 参数区大小: 4KB */
|
|
|
|
/* ============================================================
|
|
* 设备 CAN ID 配置 (编译期固定)
|
|
* ------------------------------------------------------------
|
|
* 同一总线挂多设备升级: 每个设备必须使用唯一 ID 对, 否则
|
|
* 上位机发一条指令所有设备同时响应 → 总线冲突
|
|
*
|
|
* 设备分配建议 (RX=寻址, TX=回复):
|
|
* 设备1: RX=0x180 TX=0x181 设备2: RX=0x182 TX=0x183
|
|
* 设备3: RX=0x184 TX=0x185 设备4: RX=0x186 TX=0x187
|
|
* 设备5: RX=0x188 TX=0x189
|
|
*
|
|
* 配置方法 (无需改文件, 编译选项覆盖):
|
|
* Keil → Options → C/C++ → Define 中加:
|
|
* IAP_CAN_RX_ID=0x182,IAP_CAN_TX_ID=0x183
|
|
* 每设备一份工程配置, 编译出不同 ID 的固件
|
|
* ============================================================ */
|
|
#ifndef IAP_CAN_RX_ID
|
|
#define IAP_CAN_RX_ID 0x186U /* 接收ID: 上位机 → 板子 */
|
|
#endif
|
|
#ifndef IAP_CAN_TX_ID
|
|
#define IAP_CAN_TX_ID 0x187U /* 发送ID: 板子 → 上位机 */
|
|
#endif
|
|
|
|
/* UDS 服务标识符 (SID) - ISO 14229 标准
|
|
* 按升级阶段顺序排列: 握手 → 传输 → 校验 → 复位 */
|
|
#define UDS_SESSION_CONTROL 0x10U /* 会话控制: 进入编程会话(握手) */
|
|
#define UDS_READ_DATA 0x22U /* 读数据: 读取版本/固件信息(握手) */
|
|
#define UDS_SECURITY_ACCESS 0x27U /* 安全访问: Seed/Key 解锁(握手) */
|
|
#define UDS_REQUEST_DOWNLOAD 0x34U /* 请求下载: 声明固件大小(传输) */
|
|
#define UDS_TRANSFER_DATA 0x36U /* 传输数据: 开始数据传输(传输) */
|
|
#define UDS_TRANSFER_EXIT 0x37U /* 传输退出: 结束数据传输(传输) */
|
|
#define UDS_ROUTINE_CONTROL 0x31U /* 例程控制: CRC校验/擦除(校验) */
|
|
#define UDS_ECU_RESET 0x11U /* ECU复位: 跳转新App(完成) */
|
|
|
|
/* UDS 响应格式
|
|
* 正响应 = SID | 0x40 (如 0x10 → 0x50)
|
|
* 否定响应 = 0x7F + SID + NRC */
|
|
#define UDS_POS_RESP(sid) ((sid) | 0x40U) /* 正响应码计算 */
|
|
#define UDS_NEG_RESP 0x7FU /* 否定响应标识 */
|
|
|
|
/* NRC (否定响应码) - ISO 14229 标准 */
|
|
#define NRC_GENERAL_REJECT 0x10U /* 通用拒绝(Flash擦写失败等) */
|
|
#define NRC_SERVICE_NOT_SUPPORTED 0x11U /* 不支持的 SID */
|
|
#define NRC_REQUEST_OUT_OF_RANGE 0x31U /* 参数越界(固件大小=0或超限) */
|
|
#define NRC_CONDITIONS_NOT_CORRECT 0x22U /* 状态不满足(未解锁就请求下载) */
|
|
|
|
/* 升级标志定义(存储在参数区首字 0x0800F000)
|
|
* 用于 Bootloader 与 IAP 之间的状态传递 */
|
|
#define IAP_FLAG_NEW_FW 0xA5A55A5AU /* 有新固件待处理 */
|
|
#define IAP_FLAG_UPGRADING 0x55AA55AAU /* 升级进行中(断电续传标志) */
|
|
#define IAP_FLAG_COMPLETE 0x12345678U /* 升级完成,可跳转 App */
|
|
#define IAP_FLAG_NONE 0xFFFFFFFFU /* 空闲(Flash 擦除后默认值) */
|
|
|
|
/* ============================================================
|
|
* 参数区布局 (0x0800F000, 共4KB)
|
|
* 偏移 0x00: flag 升级标志(4字节)
|
|
* 偏移 0x04: fw_size 固件大小(4字节)
|
|
* 偏移 0x08: bitmap_lo 进度位图(块0-31, 4字节)
|
|
* 偏移 0x0C: bitmap_hi 进度位图(块32-63, 4字节)
|
|
* 偏移 0x10: prog_request 编程请求标志(4字节, APP→Bootloader通信)
|
|
*
|
|
* 编程请求机制:
|
|
* APP 收到 0x10 0x02 → 写 0x12121212 到此位置 → 软复位
|
|
* Bootloader 开机优先检查此位置 → 0x12121212 → 停留 Bootloader
|
|
* Bootloader 停留后擦除参数页 → 此位置恢复 0xFFFFFFFF
|
|
*
|
|
* 断电续传进度位图原理:
|
|
* Flash 擦除后全 1 (0xFFFFFFFF), 表示所有块未完成
|
|
* 每完成一个 1KB 块, 将对应位清 0 (1→0, 符合 Flash 物理特性)
|
|
* 上电后扫描位图, 第一个为 1 的位即为续传起点
|
|
* 优点: 每次仅写 1 个字(~40μs), 无需擦除参数页
|
|
* ============================================================ */
|
|
#define IAP_PARAM_OFF_FLAG 0U /* 升级标志偏移 */
|
|
#define IAP_PARAM_OFF_FW_SIZE 4U /* 固件大小偏移 */
|
|
#define IAP_PARAM_OFF_BITMAP_LO 8U /* 进度位图(块0-31)偏移 */
|
|
#define IAP_PARAM_OFF_BITMAP_HI 12U /* 进度位图(块32-63)偏移 */
|
|
#define IAP_PARAM_OFF_PROG_REQ 16U /* 编程请求标志偏移 */
|
|
|
|
/* 编程请求魔数(APP写入此值表示请求进入Bootloader) */
|
|
#define IAP_PROG_REQUEST_MAGIC 0x12121212U
|
|
#define IAP_PROG_CLEAR_MAGIC 0x88888888U
|
|
/* IAP 状态机: 先握手再传数据
|
|
* 转换路径: IDLE → SESSION → DOWNLOADING → DATA_MODE → TRANSFER_DONE → SESSION */
|
|
typedef enum {
|
|
IAP_STATE_IDLE = 0, /* 空闲: 仅接受 0x10 进入会话 */
|
|
IAP_STATE_SESSION, /* 会话中: 接受 0x22/0x27/0x34/0x11 */
|
|
IAP_STATE_DOWNLOADING, /* 下载等待: 已收 0x34, 等待 0x36 01 开始数据 */
|
|
IAP_STATE_DATA_MODE, /* 数据模式: 每帧8字节纯数据写入乒乓缓冲区, 不解析SID */
|
|
IAP_STATE_TRANSFER_DONE, /* 传输完成: 收够 fw_size, 等待 0x37/0x31/0x11 */
|
|
} iap_state_t;
|
|
|
|
/* IAP 运行上下文: 记录升级过程中的状态信息 */
|
|
typedef struct {
|
|
iap_state_t state; /* 当前状态机状态 */
|
|
uint32_t fw_size; /* 固件总大小(字节, 由 0x34 声明) */
|
|
uint32_t fw_received; /* 已接收字节数(达到 fw_size 时退出数据模式) */
|
|
uint32_t write_addr; /* 当前 Flash 写入地址(从 IAP_APP_ADDR 递增) */
|
|
uint8_t security_unlocked; /* 安全访问解锁标志(0x27 通过后置1) */
|
|
} iap_ctx_t;
|
|
|
|
/* ===================== API 接口 ===================== */
|
|
|
|
/* 初始化 IAP 模块, 状态置为 IDLE */
|
|
void iap_init(void);
|
|
|
|
/* 处理接收到的 CAN 帧(IAP 主入口, 在 CAN 接收任务中调用)
|
|
* id: CAN 帧 ID, data: 帧数据指针, dlc: 数据长度 */
|
|
void iap_process_frame(uint32_t id, uint8_t *data, uint8_t dlc);
|
|
|
|
/* 跳转到应用程序执行(校验栈顶合法性后跳转)
|
|
* app_addr: 应用程序起始地址(通常为 IAP_APP_ADDR) */
|
|
void iap_jump_to_app(uint32_t app_addr);
|
|
|
|
/* 读取参数区升级标志(0x0800F000 首字) */
|
|
uint32_t iap_check_flag(void);
|
|
|
|
/* 写入升级标志到参数区
|
|
* flag: IAP_FLAG_NEW_FW / IAP_FLAG_COMPLETE / IAP_FLAG_NONE
|
|
* 返回: 1=成功, 0=失败 */
|
|
uint8_t iap_write_flag(uint32_t flag);
|
|
|
|
/* 擦除应用程序区所有页(IAP_APP_ADDR 起共 IAP_APP_PAGE_NUM 页)
|
|
* 返回: 1=成功, 0=失败 */
|
|
uint8_t iap_erase_app(void);
|
|
|
|
/* 计算 CRC32 (IEEE 802.3, 多项式 0xEDB88320)
|
|
* data: 数据指针, len: 数据长度
|
|
* 返回: CRC32 校验值 */
|
|
uint32_t iap_crc32(const uint8_t *data, uint32_t len);
|
|
|
|
/* 获取 IAP 上下文指针(用于外部查询当前升级状态) */
|
|
iap_ctx_t *iap_get_ctx(void);
|
|
|
|
/* 判断当前运行在App还是Bootloader(通过链接地址区分)
|
|
* 返回: 1=App模式, 0=Bootloader模式 */
|
|
uint8_t iap_is_app_mode(void);
|
|
|
|
/* ===================== 编程请求(APP→Bootloader通信) ===================== */
|
|
|
|
/* 检查编程请求标志(直接读Flash, 不需要HAL初始化)
|
|
* 返回: 1=有编程请求, 0=无 */
|
|
uint8_t iap_check_prog_request(void);
|
|
|
|
/* 设置编程请求标志(APP收到0x10 0x02时调用)
|
|
* 写 0x12121212 到参数区偏移0x10, 然后软复位进入Bootloader
|
|
* 返回: 1=成功, 0=失败 */
|
|
uint8_t iap_set_prog_request(void);
|
|
|
|
/* 清除编程请求标志(Bootloader停留后调用)
|
|
* 擦除参数页, 所有参数区数据恢复0xFFFFFFFF
|
|
* 注意: 会同时清除flag/fw_size/bitmap, 后续IAP流程会重新初始化
|
|
* 返回: 1=成功, 0=失败 */
|
|
uint8_t iap_clear_prog_request(void);
|
|
|
|
/* 断电续传: 上电时检查是否有未完成的升级
|
|
* 返回: 续传偏移量(0=全新升级, >0=从该偏移续传) */
|
|
uint32_t iap_resume_check(void);
|
|
|
|
#endif /* __APP_IAP_H__ */
|