1. 为什么需要固定地址存储Flash数据
在STM32开发过程中,我们经常遇到需要将某些关键数据保存在固定Flash地址的需求。比如固件版本信息、设备序列号、校准参数、或者一些需要在生产线上烧录的配置数据。这些数据有一个共同特点:它们需要在固件升级过程中保持不变,或者需要被其他系统通过绝对地址来访问。
我刚开始做STM32项目时就遇到过这样的需求。当时我们需要在设备上存储一个版本字符串,让bootloader能够读取并显示当前固件版本。最初的做法是直接把版本信息放在代码中,但每次编译后地址都会变化,bootloader根本找不到它。后来尝试用EEPROM存储,但有些STM32型号没有EEPROM,而且增加了额外成本。
最让我头疼的是有一次生产线烧录问题。生产人员需要在校验阶段读取设备序列号,但每次编译后地址变化,校验工具都要重新配置地址参数,经常出错。这时候我才意识到固定地址存储的重要性。
使用GCC编译器的__attribute__((section))特性可以完美解决这个问题。它允许我们将变量放置在指定的Flash段中,不受代码修改和重新编译的影响。这样bootloader、调试工具甚至生产线设备都能通过固定地址访问这些关键数据。
2. attribute((section)) 原理解析
2.1 GCC属性机制概述
__attribute__是GCC编译器提供的一种强大机制,它允许开发者向编译器提供额外的信息,从而影响编译过程和结果。我把它理解为"给编译器的特别指示",就像你在包裹上贴"易碎品"标签一样,告诉搬运工需要特别小心处理。
在实际使用中,__attribute__可以修饰函数、变量和类型,实现各种高级功能。比如设置对齐方式、优化级别、段放置等。section属性只是其中的一个应用,但确实是最实用的功能之一。
2.2 section属性的工作原理
当我们使用__attribute__((section(".ARM.__AT_0x08002800")))修饰一个变量时,实际上是在告诉链接器:"请把这个变量放在名为'.ARM.__AT_0x08002800'的段中"。链接器会在链接脚本中寻找同名的段定义,如果找不到,它会自动创建一个。
这里有个很重要的理解:.ARM.__AT_0x08002800这个名字有特殊含义。GCC的ARM工具链会识别这种命名约定,将段放置在指定的绝对地址。其中的十六进制数字就是目标地址。
我在实际项目中验证过,即使链接脚本中没有明确定义这个段,链接器也会智能地处理。它会将这个段放置在指定的绝对地址,同时确保不会与其他段重叠。这种机制大大简化了我们的工作。
2.3 与链接脚本的配合
虽然GCC能自动处理段放置,但在复杂项目中,我们最好在链接脚本中明确定义这些特殊段。这样可以更好地控制内存布局,避免意外冲突。
比如在STM32的链接脚本(.ld文件)中,我们可以这样定义:
MEMORY
{
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.ARM.__AT_0x08002800 : {
KEEP(*(.ARM.__AT_0x08002800))
} > ROM
3195



被折叠的 条评论
为什么被折叠?



