You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在STM32单片机bootloader与app两个程序间共享单个Flash扇区

字体常量共享解决方案

你之前的写法会占用RAM是因为定义了可修改的全局指针变量,该变量本身会被分配到RAM空间,可通过以下两种无RAM占用的方式实现访问:

  1. 宏定义直接映射地址
    无需定义变量,直接将固定地址强转为const类型指针,使用时和普通数组完全一致,不会占用任何RAM资源:

    #define Terminal6x8 ((const unsigned char *)0x08011298)
    #define Courier_New_Bold_20 ((const unsigned char *)0x08010000) // 和你ld定义的起始地址对应
    

    调用示例:uint8_t data = Terminal6x8[offset];

  2. 固定地址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侧配置

  1. 首先在ld脚本中定义共享跳转表的固定存储区域:
    .shared_table 0x08011000 : { /* 地址选在你预留的共享Flash扇区内即可 */
        KEEP(*(.shared_table_section))
    } > FLASH
    
  2. 定义共享资源结构体,将所有要共享的字体、函数指针都放到结构体中:
    // 该结构体定义要在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 02:06:03