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

一、基本信息

单片机型号:华大 HC32F460

开发环境:Keil

Flash规格: 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 Size 66372 (64.82kB) 516116 (504.02kB)
ROM Size 68344 (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!";

编译后下载代码到单片机,通过调试模式查看,Flash(0x00078000)地址并无指定数据。

查看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 中执行)
相关推荐
公爵爱学习14 分钟前
无人机多点导航笔记
java·前端·笔记
Capricorn198816 分钟前
跨端同步与记忆锁定排障:OpenClaw 2.0 Active Memory 云端劫持危机,知芽 Notebook Skill 单元记忆架构解析
大数据·论文阅读·人工智能·笔记·架构·论文笔记
梵亿23 分钟前
基于 STM32F103C8T6 的无人机低功耗设计与实现
stm32·嵌入式硬件·无人机·低功耗
Hammer_Hans32 分钟前
DFT笔记120
笔记
hrw_embedded40 分钟前
Keil开发与STM32Cube开发的差异点总结(未完待续)
stm32·单片机·嵌入式硬件
Brilliantwxx1 小时前
【STM32】RCC与时钟树 深度详解
stm32·单片机·嵌入式硬件·架构
Ztt6666666661 小时前
月白大竹节壶不露锋芒,竹意藏在嘴把转折里
经验分享·笔记·其他·百度·微信公众平台
XIE3921 小时前
TipKit:开源富文本编辑器套件,一套逻辑,任意风格!
前端·笔记·开源
天空'之城1 小时前
单片机基础核心知识点汇总(二十九)
单片机·嵌入式硬件
xieliyu.1 小时前
HTTPS 加密流程深度解析|从 HTTP 痛点到 TLS 握手全过程
网络·笔记·网络协议·计算机网络·http·https