单片机 Flash 指定地址存储常量字符串调试笔记

张开发
2026/4/10 20:56:19 15 分钟阅读

分享文章

单片机 Flash 指定地址存储常量字符串调试笔记
一、基本信息单片机型号华大 HC32F460开发环境KeilFlash规格: 512K (0x00000000 ~ 0x0007FFFF)扇区大小8K目标功能将常量字符串编译保存到Flash的指定绝对地址二、问题现象使用 __attribute__((at())) 直接指定字符串地址const char myFixedString[] __attribute__((at(0x00078000))) Hello, HC32F460!;将上述代码编译后Map文件异常。原代码 RO Data 约66KB加入上述代码后Total ROM Size 膨胀至505KB生成的烧录文件体积巨大烧录缓慢且浪费 Flash 空间。Map 文件对比项目修改前修改后RO Size66372 (64.82kB)516116 (504.02kB)ROM Size68344 (66.74kB)518088 (505.95kB)三、原因分析__attribute__((at(0x00078000)))要求链接器将变量放置到该绝对地址。代码原有结束地址约0x00010800而目标地址0x0007F000相距约 442KB。Keil 链接器为了保证地址连续性从代码结束处到目标地址之间的所有空白区域都被填充为 0导致生成的映像文件包含大量无用数据ROM Size 急剧增大。四、解决方法使用分散加载文件 独立加载域原理为固定数据创建一个独立的加载域Load Region起始地址直接设为所需地址。链接器不会在两个加载域之间插入填充数据。步骤打开 Keil 工程选项 → Linker → 取消勾选 “Use Memory Layout from Target Dialog”点击 Edit 编辑分散加载.sct文件内容如下; 主加载域代码区占用 Flash 前半部分结束于数据区之前 LR_IROM1 0x00000000 0x00078000 { ; 480KB结束于 0x77FFF ER_IROM1 0x00000000 0x00078000 { *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) .ANY (XO) } RW_IRAM1 0x1FFF8000 0x0002F000 { .ANY (RW ZI) } } ; 独立数据加载域固定字符串放在最后 8KB 扇区0x78000 ~ 0x79FFF LR_ROM_DATA 0x00078000 0x00002000 { ER_ROM_DATA 0x00078000 0x00002000 { *(.my_fixed_data) ; 收集所有标记为 .my_fixed_data 的段 } }注意主加载域大小0x78000必须保证实际代码RO Data 不超过该值否则会与数据域重叠。当前代码仅 66KB安全。使用独立加载域时主加载域必须缩小到数据区起始地址之前否则地址重叠会导致链接失败。在 C 代码中定义变量到自定义段并编译。const char myFixedString[] __attribute__((section(.my_fixed_data))) Hello, HC32F460!;编译后下载代码到单片机通过调试模式查看Flash0x00078000地址并无指定数据。查看Map文件发现以下链接报告字符串段被链接器优化移除最终 Flash 中并无数据。Removing apl_application.o(.my_fixed_data), (17 bytes).原因分析链接器默认开启“死代码消除”Dead Code Elimination虽然通过.sct文件为.my_fixed_data段分配了空间但 C 代码中定义的字符串变量没有被任何地方引用未读取其地址或内容链接器认为该段是未使用的输入段因此在最终链接阶段将其移除。解决方法使用__attribute__((used))强制保留自定义段// 字符串将被放置在 Flash 地址 0x00078000 const char myFixedString[] __attribute__((section(.my_fixed_data), used)) Hello, HC32F460!;五、验证步骤重新编译工程观察编译输出无Removing ...警告。查看 Map 文件搜索myFixedString确认其地址为0x00078000。检查Total ROM Size代码实际大小 字符串长度约 66KB 几十字节。烧录并运行在代码中通过指针读取该地址的数据打印确认内容正确。六、关键注意事项注意点说明地址冲突指定的 Flash 地址必须在芯片有效范围内且不能与代码、中断向量表重叠。建议放在 Flash 尾部扇区。主加载域大小使用独立加载域时主加载域必须缩小到数据区起始地址之前否则地址重叠会导致链接失败。const 修饰只读字符串建议定义为const数据直接放在 Flash 中不占用 RAM。运行时修改如需运行时修改 Flash 中的字符串不能使用const且必须调用 Flash 擦写函数需在 RAM 中执行

更多文章