有人咨询如何基于 STM32 芯片实现将常量存放到片内 Flash 指定地址。这里结合ARM Keil uVision v5.43、Arm Compiler 6,简单整理常见做法,供参考。
本文讨论的是编译期已经确定的常量通过链接定位放到指定 Flash 地址,而不是程序运行过程中动态改写Flash内容。如果你的需求是“运行时动态修改参数并掉电保存”,那还需要额外实现 Flash 擦写流程,这里不展开。
一.最简单的做法,在 Flash 末尾预留空间
如果只是想把少量常量放到 Flash 末尾固定位置,最方便最简单的做法就是在KEIL Target选项卡里适当减小 IROM1 的 Size,给主程序少分配一点 Flash,把末尾剩余空间预留出来存放常量。比如芯片有 1MB Flash,原范围是:0x08000000 ~ 0x080FFFFF。可以把 IROM1 Size 改小一些,例如只给主程序分配:0x000FFF00。
注意,此时我们必须使用target dialog的内存布局。下图中红框内勾选必须有效。
然后,我们就可以针对刚才被划分出来的那块FLASH空间做常量定义。如下图所示:
const uint32_t variable __attribute__((section(".ARM.__at_0x080FFF00"))) = 0xA5A5A5A5;const uint32_t variable1 __attribute__((section(".ARM.__at_0x080FFF04"))) = 0x12345678;。
这种方式适合固件版本号,产品标识,校验值,少量默认参数的存放。
2.如果要放到 Flash 中间位置,建议使用 scatter file
如果不希望把常量放在末尾,而是想放到 Flash 中间某个固定位置,比如:常量 variable 放在 0x0800FC00处,或者把某个函数放在 0x0800FD00开始的地方,这时就适合使用 scatter file(分散加载文件)。此时一般不再依赖 Target Dialog 默认布局,而是用 scatter file 自己定义存储分布。【取消Linker栏里使用Target Dialog内存布局的勾选】
适当修改分散加载文件,为指定存储设置相应的section。一般在默认的分散加载文件里调整即可。下面是实现目前需求的分散加载文件,可供参考。
具体的实现代码写法如下:
const uint32_t variable __attribute__((used, section(".usrdata.ARM.__at_0x0800FC00")))= 0xA5A5A5A5;__attribute__((used, section(".ARM.__at_0x0800FD00"))) void MX_SPI1_Init(void){}
这里的section(".ARM.__at_地址") 用来指定绝对地址,used 用来防止对象因未引用被优化删除。下面是实际效果截图,可以通过调试工具和阅读MAP文件确认最终实现。
这种方式更适合Flash 中间地址定位,多个固定区域统一管理,Bootloader / App 分区场景等。
三.几点提醒
- 本文方法本质上是链接定位,数据会随固件一起写进 Flash,不是程序运行时针对FLASH动态编程。指定地址必须合法。位于芯片有效 Flash 范围内,不和主程序区冲突,最好按 Flash 页/扇区边界规划。建议检查 map 文件确认常量或函数确实被放到了预期地址。
- 注意覆盖问题。如果 IAP、量产烧录或下载脚本会擦除到指定存储区域,那么即使你做了指定存放,数据后续也可能丢失。其它特定注意事项, 比方ECC方面的问题,视具体芯片灵活应对使用。
四.小结
如果只是想在 STM32 片内 Flash 中放少量固定常量,优先推荐缩小 IROM1,在末尾预留空间。如果需要更复杂的布局:推荐使用 scatter file。以上内容仅供参考。更详细的约定和语法,建议结合 Arm Compiler 6 编译手册一起阅读。
522