设计基于IAP功能的Flash双Bank热切换回滚机制,关键在于实现安全切换、状态管理和故障恢复。以下是具体设计步骤和代码框架:
/* STM32F4 Flash 划分示例 (1MB) */
#define BANK_SIZE (512 * 1024) // 每Bank 512KB
#define BANK0_START 0x08000000 // Bank0起始地址
#define BANK1_START 0x08080000 // Bank1起始地址
#define STATUS_SECTOR 0x0800F000 // 状态标志扇区(独立4KB扇区)使用独立扇区存储状态,防止意外擦除:
typedef struct {
uint8_t current_bank; // 当前运行Bank (0/1)
uint8_t update_status; // 升级状态标志
uint32_t new_fw_checksum; // 新固件校验和
uint32_t reserved; // 对齐保留
} SystemStatus;
// 状态枚举定义
typedef enum {
APP_OK = 0, // 运行正常
UPDATE_PENDING, // 新固件待验证
UPDATE_SUCCESS, // 新固件验证成功
UPDATE_FAILED // 新固件验证失败
} UpdateStatus;void bootloader_main() {
// 1. 读取状态标志
SystemStatus status = read_status_flags();
// 2. 处理新固件验证
if (status.update_status == UPDATE_PENDING) {
if (validate_new_firmware(status.new_bank)) {
status.update_status = UPDATE_SUCCESS;
status.current_bank = status.new_bank; // 切换Bank
} else {
status.update_status = UPDATE_FAILED;
// 自动回滚:保持原Bank
}
save_status_flags(status);
}
// 3. 跳转到当前Bank的APP
jump_to_app(get_bank_address(status.current_bank));
}void iap_update_firmware(uint8_t *new_fw_data, uint32_t size) {
SystemStatus status = read_status_flags();
uint8_t target_bank = !status.current_bank; // 目标Bank=非当前Bank
// 1. 设置状态为"升级中"
status.update_status = UPDATE_PENDING;
status.new_bank = target_bank;
save_status_flags(status);
// 2. 擦除目标Bank并写入新固件
flash_erase_bank(target_bank);
for (int i = 0; i < size; i += 256) {
flash_write(target_bank_addr + i, &new_fw_data[i], 256);
}
// 3. 计算并存储校验和
status.new_fw_checksum = crc32(new_fw_data, size);
save_status_flags(status);
// 4. 触发重启
NVIC_SystemReset();
}// 在bootloader中验证新固件
int validate_new_firmware(uint8_t bank) {
uint32_t *app_addr = get_bank_address(bank);
// 检查栈指针是否合法(第一个字是SP)
if ((*app_addr & 0x2FFE0000) != 0x20000000)
return 0;
// CRC校验
uint32_t calc_crc = crc32(app_addr, BANK_SIZE);
if (calc_crc != status.new_fw_checksum)
return 0;
// 关键函数地址校验(如中断向量表)
if (((*(app_addr + 1)) & 0xFF000000) != 0x08000000)
return 0;
return 1; // 验证通过
}// APP检测到异常时调用
void request_rollback() {
SystemStatus status = read_status_flags();
status.update_status = UPDATE_FAILED; // 标记失败
save_status_flags(status);
NVIC_SystemReset(); // 重启触发回滚
}current_bank → update_status → checksumUPDATE_PENDING但无有效校验和,视为升级中断,触发回滚Bank切换原子性
看门狗联动
// APP初始化时开启独立看门狗
IWDG_Enable(2.0); // 2秒超时
// 在关键循环中喂狗
void main_loop() {
while(1) {
IWDG_Refresh();
// ... 业务逻辑
}
}版本元数据嵌入
在固件开头嵌入版本信息结构体:
__attribute__((section(".app_header")))
const struct {
char version[16];
uint32_t timestamp;
uint32_t crc;
} app_metadata;graph TD
A[上电启动] --> B{Bootloader检查状态}
B -- UPDATE_PENDING --> C[验证新固件]
C -- 成功 --> D[切换Bank标志]
C -- 失败 --> E[保持原Bank]
B -- UPDATE_FAILED --> E
D & E --> F[跳转APP执行]
G[APP运行中] -- 检测到故障 --> H[标记UPDATE_FAILED]
H --> I[复位设备]
J[远程升级] --> K[写新固件到非活动Bank]
K --> L[设置UPDATE_PENDING]
L --> M[复位设备]模拟异常测试:
性能指标:
Flash锁机制
__disable_irq();
FLASH_ProgramWord(address, data);
__enable_irq();中断向量表重映射
SCB->VTOR = FLASH_BASE | (current_bank * BANK_SIZE);避免跨Bank函数调用
通过以上设计,可实现安全的双Bank热切换回滚,确保设备在升级失败时10ms内自动恢复至可用状态,同时满足工业级可靠性要求。
举报
更多回帖