news 2026/5/2 11:08:56

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
单片机 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 中执行)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/10 20:56:08

狗狗牵绳没带嘴套遛狗规范检测数据集VOC+YOLO格式1728张3类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数):1728标注数量(xml文件个数):1728标注数量(txt文件个数):1728标注类别…

作者头像 李华
网站建设 2026/4/10 20:56:07

工单管理系统能解决哪些问题,主流平台功能对比

工单管理系统是用于创建、分配、跟踪和管理各类服务请求的数字化工具,广泛应用于IT服务、后勤维修、客户支持、设备巡检等场景。它帮助组织将分散的电话、邮件、口头任务转化为标准化工单,实现全流程可视化、SLA时效管控和数据分析。以下对四款主流的工单…

作者头像 李华
网站建设 2026/4/10 20:56:06

手把手教你用C++实现OSPF路由模拟器(附Dijkstra算法详解)

用C构建OSPF路由模拟器的实战指南 计算机网络的世界里,路由协议扮演着交通警察的角色,而OSPF(Open Shortest Path First)无疑是其中最优雅的调度员之一。想象一下,你正在设计一个城市的地铁系统,需要计算从…

作者头像 李华
网站建设 2026/4/10 20:52:59

如何安全快速移除USB设备:USB-Disk-Ejector终极完整指南

如何安全快速移除USB设备:USB-Disk-Ejector终极完整指南 【免费下载链接】USB-Disk-Ejector A program that allows you to quickly remove drives in Windows. It can eject USB disks, Firewire disks and memory cards. It is a quick, flexible, portable alter…

作者头像 李华
网站建设 2026/4/10 20:47:33

敏捷开发中的职业阶梯:如何快速升职

在当今快速迭代的软件开发环境中,敏捷开发已成为主流方法论。它强调跨职能协作、持续交付和快速响应变化,为软件测试从业者提供了独特的职业发展机遇。然而,随着技术演进和市场需求的不断变化,职业阶梯结构正经历深刻变革。AI技术…

作者头像 李华
网站建设 2026/4/10 20:43:16

SpringBoot3与OAuth2.1实战:从零搭建授权服务器

1. 为什么需要自己搭建OAuth2.1授权服务器? 最近几年,随着微服务架构的流行,系统间的安全认证变得越来越重要。想象一下,你开发了一个电商平台,里面有用户中心、订单服务、支付服务等多个子系统。如果每个服务都要自己…

作者头像 李华