当前位置: 首页 > news >正文

单片机 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 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!";

编译后下载代码到单片机,通过调试模式查看,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 中执行)
http://www.jsqmd.com/news/620020/

相关文章:

  • Qwen3-ASR-1.7B效果实测:1.7B参数量带来的上下文联想能力提升验证
  • FPGA架构解析:LUT与MUX在数字电路设计中的核心作用
  • AI代码生成暗藏哪些致命陷阱?
  • 手机号查QQ号终极指南:5分钟快速找回遗忘账号
  • 将盾CDN:API安全的攻击面管理与威胁防护
  • socket套接字程序是什么
  • Kaggle电信用户流失预测实战:从数据清洗到模型调优,一份避坑指南
  • 【计算机基础】三步搞定机械硬盘升级固态硬盘的实战指南
  • 002、OpenClaw TTS 项目初探:架构概览与核心设计思想
  • Navigating MongoDB 5.0+ AVX CPU Requirements: Solutions for Unsupported Systems
  • 为多IP地址生成自签名证书的完整指南
  • 关于鸿蒙纯血连接Windows系统探讨
  • 收藏!写了五年useEffect,今年突然慌了|小白程序员必看的大模型学习指南
  • 使用Git-RSCLIP进行卫星图像变化检测
  • ES集群-提高分片恢复的速度
  • FlashAttention实战:如何在A100上实现3倍加速的Transformer训练(附代码)
  • 企业必看!私有化即时通讯的核心优势
  • 2026年200元内蓝牙耳机推荐:入耳降噪半入耳开放式怎么选?
  • Intellij怎么打jar包
  • 将盾CDN:备份与灾备建设的企业级实践
  • CSS如何监控样式表的加载状态_通过JS监听onload与onerror事件
  • 南开计算机复试C/C++编程能力测试怎么考?手把手教你用Code::Blocks/Dev-C++准备(附真题思路)
  • 机器人流程自动化(RPA)与AI结合,会取代哪些岗位?
  • 2026奇点大会闭门报告首度流出(仅限前500名开发者):AI原生移动端的LLM轻量化部署、实时推理压缩与端侧Agent编排全栈实践
  • 保姆级教程:用ROS 2 Humble + Python搞定CiA 402伺服电机的速度控制(附完整代码)
  • 将盾CDN:安全自动化响应的工作流设计
  • 小程序开发失败的常见原因有哪些?
  • 从金融交易到工业控制:NTP和PTP到底该怎么选?一张图看懂时间同步协议选型
  • 双膜储气柜的选择指南建议
  • 模糊综合评价法:如何量化那些“说不清”的复杂决策问题