如何在STM32单片机bootloader与app两个程序间共享单个Flash扇区
字体常量共享解决方案
你之前的写法会占用RAM是因为定义了可修改的全局指针变量,该变量本身会被分配到RAM空间,可通过以下两种无RAM占用的方式实现访问:
宏定义直接映射地址
无需定义变量,直接将固定地址强转为const类型指针,使用时和普通数组完全一致,不会占用任何RAM资源:#define Terminal6x8 ((const unsigned char *)0x08011298) #define Courier_New_Bold_20 ((const unsigned char *)0x08010000) // 和你ld定义的起始地址对应调用示例:
uint8_t data = Terminal6x8[offset];固定地址const数组声明
如果你需要让编译器做类型检查,可声明为外部const数组,编译器会直接识别为Flash常量,不会生成RAM变量:extern const unsigned char Terminal6x8[] __attribute__((at(0x08011298))); extern const unsigned char Courier_New_Bold_20[] __attribute__((at(0x08010000)));
建议你在bootloader和app的ld脚本中统一定义地址常量,避免代码中硬编码地址,后续修改更方便:
/* 两个项目的ld脚本头部统一添加,地址和你实际分配的Flash扇区对应即可 */ FONT_SECTOR_BASE = 0x08010000; Terminal6x8_ADDR = 0x08011298; Courier_New_Bold_20_ADDR = FONT_SECTOR_BASE;
C代码中可直接引用ld中定义的常量:
extern const unsigned char Terminal6x8[] __attribute__((absolute_address(Terminal6x8_ADDR)));
函数共享解决方案
推荐使用跳转表方案,扩展性强,不需要为每个函数单独硬编码地址,避免bootloader修改后函数地址变动导致app调用失败:
步骤1:bootloader侧配置
- 首先在ld脚本中定义共享跳转表的固定存储区域:
.shared_table 0x08011000 : { /* 地址选在你预留的共享Flash扇区内即可 */ KEEP(*(.shared_table_section)) } > FLASH - 定义共享资源结构体,将所有要共享的字体、函数指针都放到结构体中:
// 该结构体定义要在boot和app中保持完全一致 typedef struct { // 共享字体资源 const uint8_t* Courier_New_Bold_20; const uint8_t* Terminal6x8; // 共享函数指针 int32_t (*lcd_draw_point)(int16_t x, int16_t y, uint16_t color); void (*lcd_fill_rect)(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); } SharedResourceTable; // 将跳转表放到固定的Flash段 const SharedResourceTable sh_resource_table __attribute__((section(".shared_table_section"))) = { .Courier_New_Bold_20 = Courier_New_Bold_20, .Terminal6x8 = Terminal6x8, .lcd_draw_point = lcd_draw_point, .lcd_fill_rect = lcd_fill_rect };
步骤2:app侧配置
直接声明相同的结构体,将固定地址强转为结构体指针即可访问所有共享资源:
// 和bootloader侧完全一致的结构体定义 typedef struct { const uint8_t* Courier_New_Bold_20; const uint8_t* Terminal6x8; int32_t (*lcd_draw_point)(int16_t x, int16_t y, uint16_t color); void (*lcd_fill_rect)(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); } SharedResourceTable; // 映射到跳转表的固定地址 #define SH_RES ((const SharedResourceTable*)0x08011000)
调用示例:
// 访问字体 uint8_t font_data = SH_RES->Terminal6x8[10]; // 调用共享函数 SH_RES->lcd_draw_point(20, 20, 0xFFFF);
注意事项
- 所有共享资源、跳转表、共享函数都要放在单独的Flash扇区,确保bootloader或app更新擦除Flash时不会覆盖该区域
- 共享函数建议设计为纯函数,不要使用全局静态变量,如果必须使用全局变量,要将变量也分配到固定的共享RAM/Flash区域
- bootloader和app的编译选项要保持一致,包括结构体对齐规则、浮点ABI配置、参数传递规则,避免函数调用时参数解析错误
内容的提问来源于stack exchange,提问作者Massimo M
相关产品推荐
相关产品推荐

