test/freetest/Core/Src/app_iap.c

775 lines
23 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "app_iap.h"
#include "app_flash.h"
#include "hw_can.h"
#include "stm32f1xx_hal.h"
#include <string.h>
#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);
}