#include "app_iap.h" #include "app_flash.h" #include "hw_can.h" #include "stm32f1xx_hal.h" #include #include "cmsis_os2.h" // ::CMSIS:RTOS2 /* ============================================================ * IAP 模块实现 * * 核心设计: 先握手再传数据 * 1. 握手阶段: 0x10进入会话 → 0x22读版本 → 0x27安全访问 * 2. 数据传输: 0x34声明大小 → 0x36 01开始 → 纯数据帧 → 0x37结束 * 3. 校验烧写: 0x31触发 → 0x11跳转APP * * 数据模式(DATA_MODE)下: * 每帧8字节全部是固件数据,无SID/序号开销 * 按固件size计数,收够自动退出数据模式 * 0x36 01命令帧不包含数据,仅触发状态切换 * ============================================================ */ /* IAP 上下文 */ static iap_ctx_t iap_ctx; /* 乒乓缓冲区: DATA_MODE下CAN数据先入缓冲区, 满后批量写Flash */ #define IAP_BUF_SIZE 1024U /* 每个缓冲区1KB(128帧×8字节) */ #define IAP_BUF_NUM 2U /* 两个缓冲区乒乓 */ static uint8_t iap_buf[IAP_BUF_NUM][IAP_BUF_SIZE]; static uint8_t iap_active_buf; /* 当前写入的缓冲区索引(0或1) */ static uint16_t iap_buf_pos; /* 当前缓冲区写入位置 */ static uint8_t iap_flush_pending; /* 有缓冲区待写入Flash标志 */ static uint8_t iap_flush_buf; /* 待写入Flash的缓冲区索引 */ static uint16_t iap_flush_len; /* 待写入Flash的数据长度 */ /* ===================== 内部辅助函数 ===================== */ /* 发送UDS正响应 */ static void iap_send_pos(uint8_t sid, uint8_t *extra, uint8_t extra_len) { uint8_t resp[8] = {0}; resp[0] = UDS_POS_RESP(sid); for (uint8_t i = 0; i < extra_len && i < 7; i++) resp[1 + i] = extra[i]; hwCanSend(IAP_CAN_TX_ID, resp, 8); } /* 发送UDS否定响应 */ static void iap_send_neg(uint8_t sid, uint8_t nrc) { uint8_t resp[8] = {0}; resp[0] = UDS_NEG_RESP; resp[1] = sid; resp[2] = nrc; hwCanSend(IAP_CAN_TX_ID, resp, 8); } /* 擦除App区所有页 */ static uint8_t iap_erase_app_pages(void) { for (uint32_t i = 0; i < IAP_APP_PAGE_NUM; i++) { if (!flash_erase_page(IAP_APP_ADDR + i * FLASH_PAGE_SIZE)) return 0; } return 1; } /* 将缓冲区数据批量写入Flash(只Unlock/Lock一次) */ static uint8_t iap_flush_buffer(uint32_t addr, const uint8_t *data, uint32_t len) { uint32_t i = 0; HAL_FLASH_Unlock(); /* 非字对齐起始: 按半字编程到对齐 */ /* STM32F1 Flash 只支持半字(16bit)编程,不支持字节编程 */ while ((addr + i) % 4 != 0 && i + 2 <= len) { uint16_t hw = *(const uint16_t *)&data[i]; if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr + i, hw) != HAL_OK) { HAL_FLASH_Lock(); return 0; } i += 2; } /* 按字(32bit)批量编程 */ while (i + 4 <= len) { uint32_t word = *(const uint32_t *)&data[i]; if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, word) != HAL_OK) { HAL_FLASH_Lock(); return 0; } i += 4; } /* 剩余不足4字节: 按半字编程 */ while (i + 2 <= len) { uint16_t hw = *(const uint16_t *)&data[i]; if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr + i, hw) != HAL_OK) { HAL_FLASH_Lock(); return 0; } i += 2; } /* 剩余1字节(奇数长度): 补0xFF组成半字编程 */ if (i < len) { uint16_t hw = (uint16_t)data[i] | 0xFF00U; if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr + i, hw) != HAL_OK) { HAL_FLASH_Lock(); return 0; } i++; } HAL_FLASH_Lock(); return 1; } /* ===================== 断电续传辅助函数 ===================== */ /* 从进度位图计算续传偏移量 * 位图为1的位表示该块未完成, 为0表示已完成 * 找到第一个为1的位, 其索引即为续传起点块号 */ static uint32_t iap_calc_offset(uint32_t fw_size) { uint32_t total_blocks = (fw_size + IAP_BUF_SIZE - 1) / IAP_BUF_SIZE; uint32_t bitmap_lo = flash_read_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_BITMAP_LO); uint32_t bitmap_hi = flash_read_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_BITMAP_HI); for (uint32_t i = 0; i < total_blocks; i++) { uint32_t bitmap = (i < 32) ? bitmap_lo : bitmap_hi; uint32_t bit = i % 32; if (bitmap & (1U << bit)) { /* 该位为1: 此块未完成, 从这里开始续传 */ return i * IAP_BUF_SIZE; } } /* 所有块都已完成 */ return fw_size; } /* 完成1KB块后更新进度位图(将对应位清0) * 利用 Flash 只能 1→0 的物理特性, 无需擦除参数页 * 每次仅写1个字(~40μs), 不阻塞CAN接收 */ static void iap_update_bitmap(uint32_t block_idx) { uint32_t addr; uint32_t bit = block_idx % 32; if (block_idx < 32) addr = IAP_PARAM_ADDR + IAP_PARAM_OFF_BITMAP_LO; else addr = IAP_PARAM_ADDR + IAP_PARAM_OFF_BITMAP_HI; uint32_t cur_val = flash_read_word(addr); uint32_t new_val = cur_val & ~(1U << bit); /* 仅当位需要变化时才写入(1→0) */ if (new_val != cur_val) { HAL_FLASH_Unlock(); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, new_val); HAL_FLASH_Lock(); } } /* 全新升级: 初始化参数区(擦除后写入 flag + fw_size) */ static uint8_t iap_param_init(uint32_t fw_size) { /* 擦除参数页(擦除后 flag/bitmap 全 0xFF, 即 UPGRADING 状态 + 所有块未完成) */ if (!flash_erase_page(IAP_PARAM_ADDR)) return 0; /* 写入升级标志 */ if (!flash_write_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_FLAG, IAP_FLAG_UPGRADING)) return 0; /* 写入固件大小 */ if (!flash_write_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_FW_SIZE, fw_size)) return 0; /* bitmap 保持擦除态 0xFFFFFFFF(所有块未完成), 无需额外写入 */ return 1; } /* ===================== 阶段1: 握手处理 ===================== */ /* 0x10 会话控制 */ static void iap_handle_session(uint8_t *data, uint8_t dlc) { if (dlc < 2) { iap_send_neg(UDS_SESSION_CONTROL, NRC_REQUEST_OUT_OF_RANGE); return; } /* data[1]=子功能: 0x01默认会话, 0x02扩展会话, 0x03编程会话 */ if (data[1] >= 0x02) { /* App模式收到0x10 0x02: 写编程请求标志, 软复位进入Bootloader */ if (iap_is_app_mode()) { iap_send_pos(UDS_SESSION_CONTROL, &data[1], 1); /* 先回响应 */ iap_set_prog_request(); /* 写0x12121212到参数区 */ HAL_Delay(50); /* 确保Flash写入和CAN发送完成 */ NVIC_SystemReset(); /* 软复位, 进入Bootloader */ } /* Bootloader模式: 进入编程会话 */ iap_ctx.state = IAP_STATE_SESSION; iap_ctx.security_unlocked = 0; } uint8_t sub = data[1]; iap_send_pos(UDS_SESSION_CONTROL, &sub, 1); } /* 0x22 读数据(版本/握手) */ static void iap_handle_read_data(uint8_t *data, uint8_t dlc) { if (dlc < 3) { iap_send_neg(UDS_READ_DATA, NRC_REQUEST_OUT_OF_RANGE); return; } /* data[1..2]=DID, 这里简化: 回 "IAP01" */ uint8_t resp[7] = {0}; resp[0] = data[1]; /* DID高字节回显 */ resp[1] = data[2]; /* DID低字节回显 */ resp[2] = 'I'; resp[3] = 'A'; resp[4] = 'P'; resp[5] = '0'; resp[6] = '1'; iap_send_pos(UDS_READ_DATA, resp, 7); } /* 0x27 安全访问(Seed/Key) */ static void iap_handle_security(uint8_t *data, uint8_t dlc) { if (dlc < 2) { iap_send_neg(UDS_SECURITY_ACCESS, NRC_REQUEST_OUT_OF_RANGE); return; } uint8_t sub = data[1]; if (sub == 0x01) { /* 请求Seed: 返回固定Seed(简化版) */ uint8_t resp[7] = {0}; resp[0] = 0x01; resp[1] = 0x12; resp[2] = 0x34; resp[3] = 0x56; resp[4] = 0x78; iap_send_pos(UDS_SECURITY_ACCESS, resp, 5); } else if (sub == 0x02) { /* 发送Key: 简化版,固定Key = Seed按位取反 */ /* 上位机应发送 0xED 0xCB 0xA9 0x87 */ if (dlc >= 6 && data[2] == 0xED && data[3] == 0xCB && data[4] == 0xA9 && data[5] == 0x87) { iap_ctx.security_unlocked = 1; uint8_t ack = 0x02; iap_send_pos(UDS_SECURITY_ACCESS, &ack, 1); } else { iap_send_neg(UDS_SECURITY_ACCESS, NRC_GENERAL_REJECT); } } else { iap_send_neg(UDS_SECURITY_ACCESS, NRC_REQUEST_OUT_OF_RANGE); } } /* ===================== 阶段2: 数据传输处理 ===================== */ /* 0x34 请求下载(支持断电续传) */ static void iap_handle_request_download(uint8_t *data, uint8_t dlc) { /* 必须在会话中且已解锁 */ if (iap_ctx.state != IAP_STATE_SESSION) { iap_send_neg(UDS_REQUEST_DOWNLOAD, NRC_CONDITIONS_NOT_CORRECT); return; } /* data[0]=0x34, data[1..4]=固件大小(大端) */ if (dlc < 5) { iap_send_neg(UDS_REQUEST_DOWNLOAD, NRC_REQUEST_OUT_OF_RANGE); return; } uint32_t fw_size = ((uint32_t)data[1] << 24) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 8) | ((uint32_t)data[4]); if (fw_size == 0 || fw_size > IAP_APP_SIZE) { iap_send_neg(UDS_REQUEST_DOWNLOAD, NRC_REQUEST_OUT_OF_RANGE); return; } /* ===== 断电续传判断 ===== */ uint32_t offset = 0; uint32_t saved_flag = flash_read_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_FLAG); uint32_t saved_size = flash_read_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_FW_SIZE); if (saved_flag == IAP_FLAG_UPGRADING && saved_size == fw_size) { /* 续传模式: 固件大小匹配, 从进度位图计算断点 */ offset = iap_calc_offset(fw_size); if (offset >= fw_size) { /* 所有块已完成但未校验, 直接进入校验阶段 */ offset = fw_size; } /* 续传模式不擦除 App 区, 保留已写入数据 */ } else { /* 全新升级: 擦除 App 区 + 初始化参数区 */ if (!iap_erase_app_pages()) { iap_send_neg(UDS_REQUEST_DOWNLOAD, NRC_GENERAL_REJECT); return; } if (!iap_param_init(fw_size)) { iap_send_neg(UDS_REQUEST_DOWNLOAD, NRC_GENERAL_REJECT); return; } offset = 0; } /* 进入下载状态, 等待0x36 01开始数据 */ iap_ctx.state = IAP_STATE_DOWNLOADING; iap_ctx.fw_size = fw_size; iap_ctx.fw_received = offset; /* 续传: 跳过已接收部分 */ iap_ctx.write_addr = IAP_APP_ADDR + offset; /* 续传: 从断点开始写 */ /* 初始化乒乓缓冲区 */ iap_active_buf = 0; iap_buf_pos = 0; iap_flush_pending = 0; iap_flush_buf = 0; iap_flush_len = 0; /* 正响应: 0x74 + 每帧字节数 + offset(4字节大端) */ /* 上位机根据 offset 决定从固件何处开始发送 */ uint8_t extra[5]; extra[0] = 0x08; /* 每帧8字节数据 */ extra[1] = (uint8_t)(offset >> 24); extra[2] = (uint8_t)(offset >> 16); extra[3] = (uint8_t)(offset >> 8); extra[4] = (uint8_t)(offset); iap_send_pos(UDS_REQUEST_DOWNLOAD, extra, 5); } /* 0x36 传输数据(命令帧: 0x36 01, 之后进入纯数据模式) */ static void iap_handle_transfer_data(uint8_t *data, uint8_t dlc) { if (iap_ctx.state != IAP_STATE_DOWNLOADING) { iap_send_neg(UDS_TRANSFER_DATA, NRC_CONDITIONS_NOT_CORRECT); return; } /* 命令帧: data[0]=0x36, data[1]=0x01(块序号) */ if (dlc < 2 || data[1] != 0x01) { iap_send_neg(UDS_TRANSFER_DATA, NRC_REQUEST_OUT_OF_RANGE); return; } /* 进入纯数据模式: 此后每帧8字节直接写Flash, 无响应 */ iap_ctx.state = IAP_STATE_DATA_MODE; /* 命令帧本身不含数据, 仅回正响应 */ iap_send_pos(UDS_TRANSFER_DATA, NULL, 0); } /* 纯数据模式处理: 乒乓缓冲区 * CAN数据写入活跃缓冲区, 满后切换到另一个缓冲区 * 上一轮满缓冲区在下一帧到来时写入Flash(利用帧间隔并行) */ static void iap_handle_data_frame(uint8_t *data, uint8_t dlc) { uint32_t remaining = iap_ctx.fw_size - iap_ctx.fw_received; uint32_t write_len = (dlc < remaining) ? dlc : remaining; /* 步骤1: 如果有待写入Flash的缓冲区, 先写入(上一轮切换遗留) * 此时CAN中断可以继续往frame_buf写入, 实现半并行 */ if (iap_flush_pending) { if (!iap_flush_buffer(iap_ctx.write_addr, iap_buf[iap_flush_buf], iap_flush_len)) { iap_ctx.state = IAP_STATE_DOWNLOADING; iap_send_neg(UDS_TRANSFER_DATA, NRC_GENERAL_REJECT); return; } iap_ctx.write_addr += iap_flush_len; iap_flush_pending = 0; /* 断电续传: 更新进度位图(将刚完成的块对应位清0) */ uint32_t completed_bytes = iap_ctx.write_addr - IAP_APP_ADDR; uint32_t block_idx = (completed_bytes - 1) / IAP_BUF_SIZE; iap_update_bitmap(block_idx); } /* 步骤2: 将当前帧数据写入活跃缓冲区 */ if (write_len > 0) { memcpy(&iap_buf[iap_active_buf][iap_buf_pos], data, write_len); iap_buf_pos += write_len; iap_ctx.fw_received += write_len; } /* 步骤3: 缓冲区满 或 固件接收完 → 切换缓冲区, 标记待写入 */ if (iap_buf_pos >= IAP_BUF_SIZE || iap_ctx.fw_received >= iap_ctx.fw_size) { iap_flush_buf = iap_active_buf; iap_flush_len = iap_buf_pos; iap_flush_pending = 1; /* 切换到另一个缓冲区 */ iap_active_buf = 1 - iap_active_buf; iap_buf_pos = 0; } /* 步骤4: 固件接收完, 将最后一块写入Flash并退出 */ if (iap_ctx.fw_received >= iap_ctx.fw_size) { if (iap_flush_pending) { if (!iap_flush_buffer(iap_ctx.write_addr, iap_buf[iap_flush_buf], iap_flush_len)) { iap_ctx.state = IAP_STATE_DOWNLOADING; iap_send_neg(UDS_TRANSFER_DATA, NRC_GENERAL_REJECT); return; } iap_ctx.write_addr += iap_flush_len; iap_flush_pending = 0; /* 断电续传: 更新最后一块的进度位图 */ uint32_t completed_bytes = iap_ctx.write_addr - IAP_APP_ADDR; uint32_t block_idx = (completed_bytes - 1) / IAP_BUF_SIZE; iap_update_bitmap(block_idx); } iap_ctx.state = IAP_STATE_TRANSFER_DONE; iap_send_pos(UDS_TRANSFER_DATA, NULL, 0); } /* 数据模式中: 不发响应, 提高传输效率 */ } /* 0x37 传输退出 */ static void iap_handle_transfer_exit(void) { if (iap_ctx.state != IAP_STATE_TRANSFER_DONE) { iap_send_neg(UDS_TRANSFER_EXIT, NRC_CONDITIONS_NOT_CORRECT); return; } iap_send_pos(UDS_TRANSFER_EXIT, NULL, 0); } /* ===================== 阶段3: 校验烧写处理 ===================== */ /* 0x31 例程控制(擦除Flash / CRC校验) */ static void iap_handle_routine_control(uint8_t *data, uint8_t dlc) { if (dlc < 4) { iap_send_neg(UDS_ROUTINE_CONTROL, NRC_REQUEST_OUT_OF_RANGE); return; } /* data[1]=子功能, data[2..3]=例程ID */ uint16_t routine_id = ((uint16_t)data[2] << 8) | data[3]; if (routine_id == 0xFF01) /* EraseFlash: 擦除App区 */ { /* 续传模式(flag=UPGRADING)不擦除, 保留已写入数据 */ uint32_t saved_flag = flash_read_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_FLAG); if (saved_flag != IAP_FLAG_UPGRADING) { if (!iap_erase_app_pages()) { iap_send_neg(UDS_ROUTINE_CONTROL, NRC_GENERAL_REJECT); return; } } iap_ctx.state = IAP_STATE_SESSION; uint8_t extra[3] = {data[1], data[2], data[3]}; iap_send_pos(UDS_ROUTINE_CONTROL, extra, 3); } else if (routine_id == 0xFF02) /* VerifyCRC: 读回Flash计算CRC32 */ { /* CRC32校验: 按字节计算(与Python binascii.crc32一致) */ uint32_t calc_crc = 0xFFFFFFFFU; uint32_t addr = IAP_APP_ADDR; while (addr < IAP_APP_ADDR + iap_ctx.fw_size) { uint8_t byte = *(volatile uint8_t *)addr; calc_crc ^= byte; for (uint8_t i = 0; i < 8; i++) { if (calc_crc & 1) calc_crc = (calc_crc >> 1) ^ 0xEDB88320U; else calc_crc >>= 1; } addr++; } calc_crc ^= 0xFFFFFFFFU; iap_ctx.state = IAP_STATE_SESSION; /* 正响应: 0x71 + sub + routine_id + CRC32(4字节大端) */ uint8_t extra[7]; extra[0] = data[1]; /* 子功能回显 */ extra[1] = data[2]; /* RoutineID高字节 */ extra[2] = data[3]; /* RoutineID低字节 */ extra[3] = (uint8_t)(calc_crc >> 24); extra[4] = (uint8_t)(calc_crc >> 16); extra[5] = (uint8_t)(calc_crc >> 8); extra[6] = (uint8_t)(calc_crc); iap_send_pos(UDS_ROUTINE_CONTROL, extra, 7); } else { iap_send_neg(UDS_ROUTINE_CONTROL, NRC_REQUEST_OUT_OF_RANGE); } } /* 0x11 ECU复位(跳转APP) */ static void iap_handle_ecu_reset(uint8_t *data, uint8_t dlc) { if (dlc < 2) { iap_send_neg(UDS_ECU_RESET, NRC_REQUEST_OUT_OF_RANGE); return; } /* 正响应 */ uint8_t sub = data[1]; iap_send_pos(UDS_ECU_RESET, &sub, 1); if (data[1] == 0x01) /* 硬复位 */ { /* CRC校验通过后上位机发0x11, 写COMPLETE标志 */ iap_write_flag(IAP_FLAG_COMPLETE); HAL_Delay(100); /* 确保CAN响应帧发送完成 */ /* 直接跳转到App, 不依赖Bootloader */ iap_jump_to_app(IAP_APP_ADDR); /* 跳转失败则复位 */ NVIC_SystemReset(); } } /* ===================== 公开函数 ===================== */ void iap_init(void) { memset(&iap_ctx, 0, sizeof(iap_ctx)); iap_ctx.state = IAP_STATE_IDLE; } /* 判断当前是否运行在App地址(同一份代码, 通过链接地址区分Bootloader/App) */ uint8_t iap_is_app_mode(void) { return ((uint32_t)&iap_process_frame >= IAP_APP_ADDR) ? 1 : 0; } void iap_process_frame(uint32_t id, uint8_t *data, uint8_t dlc) { if (id != IAP_CAN_RX_ID || dlc == 0) return; /* 数据模式: 每帧8字节纯数据, 不解析SID */ if (iap_ctx.state == IAP_STATE_DATA_MODE) { iap_handle_data_frame(data, dlc); return; } /* 命令模式: 解析SID */ uint8_t sid = data[0]; switch (sid) { case UDS_SESSION_CONTROL: /* 0x10 进入会话 */ iap_handle_session(data, dlc); break; case UDS_READ_DATA: /* 0x22 读版本/握手 */ iap_handle_read_data(data, dlc); break; case UDS_SECURITY_ACCESS: /* 0x27 安全访问 */ iap_handle_security(data, dlc); break; case UDS_REQUEST_DOWNLOAD: /* 0x34 请求下载 */ iap_handle_request_download(data, dlc); break; case UDS_TRANSFER_DATA: /* 0x36 开始传输 */ iap_handle_transfer_data(data, dlc); break; case UDS_TRANSFER_EXIT: /* 0x37 传输退出 */ iap_handle_transfer_exit(); break; case UDS_ROUTINE_CONTROL: /* 0x31 校验烧写 */ iap_handle_routine_control(data, dlc); break; case UDS_ECU_RESET: /* 0x11 跳转APP */ iap_handle_ecu_reset(data, dlc); break; default: iap_send_neg(sid, NRC_SERVICE_NOT_SUPPORTED); break; } } void iap_jump_to_app(uint32_t app_addr) { /* 检查栈顶地址是否合法 */ if ((*(volatile uint32_t *)app_addr & 0xFFFE0000) != 0x20000000) return; #if 0 // __disable_irq(); // HAL_DeInit(); // SysTick->CTRL = 0; // NVIC->ICER[0] = 0xFFFFFFFF; // NVIC->ICPR[0] = 0xFFFFFFFF; // SCB->VTOR = app_addr; // uint32_t app_sp = *(volatile uint32_t *)app_addr; // uint32_t app_pc = *(volatile uint32_t *)(app_addr + 4); // __set_MSP(app_sp); // __set_PSP(app_sp); // __enable_irq(); // ((void (*)(void))app_pc)(); #else __set_FAULTMASK(1); // 或者使用 __disable_irq(); // 3. 执行软件复位[reference:13] HAL_NVIC_SystemReset(); #endif } uint32_t iap_check_flag(void) { return *(volatile uint32_t *)IAP_PARAM_ADDR; } uint8_t iap_write_flag(uint32_t flag) { if (!flash_erase_page(IAP_PARAM_ADDR)) return 0; return flash_write_word(IAP_PARAM_ADDR, flag); } /* ===================== 编程请求(APP→Bootloader通信) ===================== */ // uint8_t iap_check_prog_request(void) // { // /* 直接读Flash, 不需要HAL初始化, 可在HAL_Init之前调用 */ // return (*(volatile uint32_t *)(IAP_PARAM_ADDR + IAP_PARAM_OFF_PROG_REQ) // == IAP_PROG_REQUEST_MAGIC) ? 1 : 0; // } uint8_t iap_set_prog_request(void) { /* APP收到0x10 0x02时调用: 写0x12121212到参数区偏移0x10 * 该位置擦除后为0xFFFFFFFF, 写0x12121212是1→0, 合法 */ return flash_write_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_PROG_REQ, IAP_PROG_REQUEST_MAGIC); } uint8_t iap_clear_prog_request(void) { /* Bootloader停留后调用: 擦除参数页清除编程请求 * Flash只能1→0, 无法直接写0xFFFFFFFF, 必须擦除 * 擦除后所有参数区数据恢复0xFFFFFFFF, 后续IAP会重新初始化 */ uint32_t prog_req = flash_read_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_PROG_REQ); if(prog_req == IAP_PROG_REQUEST_MAGIC){ flash_write_word_root(IAP_PARAM_ADDR + IAP_PARAM_OFF_PROG_REQ, IAP_PROG_CLEAR_MAGIC); osDelay(100); return 1; } return 0; } uint8_t iap_erase_app(void) { return iap_erase_app_pages(); } uint32_t iap_crc32(const uint8_t *data, uint32_t len) { uint32_t crc = 0xFFFFFFFFU; for (uint32_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 1) crc = (crc >> 1) ^ 0xEDB88320U; else crc >>= 1; } } return crc ^ 0xFFFFFFFFU; } iap_ctx_t *iap_get_ctx(void) { return &iap_ctx; } uint32_t iap_resume_check(void) { /* 上电时检查是否有未完成的升级 * 返回续传偏移量(0=无续传或全新升级, >0=从该偏移续传) */ uint32_t flag = iap_check_flag(); if (flag != IAP_FLAG_UPGRADING) return 0; uint32_t fw_size = flash_read_word(IAP_PARAM_ADDR + IAP_PARAM_OFF_FW_SIZE); if (fw_size == 0 || fw_size == 0xFFFFFFFFU || fw_size > IAP_APP_SIZE) return 0; return iap_calc_offset(fw_size); }