文章目录
引言
在嵌入式系统的固件更新策略中,双分区设计因其可靠性和安全性成为行业标准做法。在这种架构中,芯片保留两个独立固件分区:一个当前运行分区(Active)和一个备份分区(Backup)。当需要更新固件时,系统会将新固件写入非活动分区,然后通过安全切换机制交换分区角色。但应用程序如何知道自己运行在哪个分区中?本文将探讨几种优雅的解决方案。
双分区基础方案
典型的双分区布局如下(以512KB Flash为例):
分区A:
ORIGIN: 0x00000000
SIZE: 256KB
分区B:
ORIGIN: 0x00040000
SIZE: 256KB
方法一:链接脚本定义分区变量(硬件级别)
// 分区A的链接脚本
.partition_info :
{
. = ORIGIN(m_partition_flag);
__partition_id = .;
LONG(0x55AAAA55); // A分区魔法值
. = ALIGN(4);
} > m_partition_flag
// 应用程序使用
__attribute__((used, section(".partition_info")))
extern const uint32_t __partition_id;
int get_current_partition(void) {
return (__partition_id == 0x55AAAA55) ? PARTITION_A : PARTITION_B;
}
优点:
-
无运行时开销
-
内存地址固定在Flash中
-
无法被意外修改
缺点:
- 需要为每个分区单独编译固件
方法二:PC指针范围检测(通用方法)
typedef enum {
PARTITION_A = 0,
PARTITION_B,
PARTITION_UNKNOWN
} PartitionID;
PartitionID get_current_partition(void) {
uint32_t pc = (uint32_t)__builtin_return_address(0);
#define PARTITION_A_START 0x00000000
#define PARTITION_A_END 0x0003FFFF
#define PARTITION_B_START 0x00040000
#define PARTITION_B_END 0x0007FFFF
if(pc >= PARTITION_A_START && pc <= PARTITION_A_END) {
return PARTITION_A;
}
else if(pc >= PARTITION_B_START && pc <= PARTITION_B_END) {
return PARTITION_B;
}
else {
return PARTITION_UNKNOWN;
}
}
优点:
-
无需特殊硬件支持
-
单固件适配双分区
-
简单可靠
缺点:
-
轻微运行时开销(约10 CPU周期)
-
依赖编译器内置函数
方法三:Bootloader分区信令(协作式)
核心原理
Bootloader在加载并跳转到应用程序时,通过特定的RAM区域传递分区信息,如下图所示:
[Bootloader启动]
│
▼
[验证分区完整性]
│
▼
[设置分区标识到RAM]
│
▼
[跳转到应用程序]
│
▼
[应用读取RAM标识]
详细实现
1. 内存规划 (链接脚本)
MEMORY {
/* ... 其他区域定义 ... */
m_shared_ram (RW) : ORIGIN = 0x2000F000, LENGTH = 0x00000100
}
SECTIONS {
.shared_data : {
_shared_data_start = .;
*(.shared_data)
_shared_data_end = .;
} > m_shared_ram
}
2. Bootloader关键代码
// 定义共享数据结构体
typedef struct {
uint32_t partition_id;
uint32_t crc_checksum; // 可选校验值
uint32_t boot_timestamp;
} SharedBootInfo;
#define SHARED_DATA_ADDR 0x2000F000
void launch_app(uint32_t app_addr, PartitionID partition) {
volatile SharedBootInfo* info = (SharedBootInfo*)SHARED_DATA_ADDR;
// 设置分区信息
info->partition_id = partition;
info->boot_timestamp =