5. 常规使用方法
本章给出几种典型定制场景的完整操作方法。修改脚本前请先阅读 第 3 章 输出段布局详解 了解默认段布局,并遵守以下通用约定:
.text前 512 字节为向量表预留,任何定位不得侵入;- 区域内为
.赋值必须显式加ORIGIN(...)(LLD 绝对地址语义,见 第 4 章 与 GNU ld 的行为差异); .赋值语句等号两侧必须有空格:漏空格(如.= . + 0x100;)时该语句被静默忽略——定位计数器不移动、链接器不报错,布局与不写该行完全相同;- 自定义数据段的匹配规则须以
*结尾(如KEEP (*(.ConstData*))); - 脚本未覆盖的输入段报错,按报错补全匹配规则(见 4.2 孤儿段检查);
- 修改后通过
.map文件或 HEX 验证是否生效。
5.1 定义常量到指定 Flash 地址
以把常量定位到 flash 内偏移 0x200 处(向量表之后)为例。
1. 修改脚本:在 .text 内向量表 KEEP 规则之后、*(.text*) 之前插入定位语句与段收集规则:
. = ALIGN(4);
__vec_end__ = .;
/*------------- 新增内容 -------------*/
. = ORIGIN(flash) + 0x200; /* flash 内偏移 0x200,必须带 ORIGIN 基址 */
KEEP (*(.ConstData*)) /* 指定段,须以 '*' 结尾 */
/*------------------------------------*/
*(.text*) /* default function code space */
2. 修改源码:用 section 属性把变量放入指定段:
__attribute__((section(".ConstData"))) uint8 Test[] = "12345678";
3. 编译验证:查看 .map 文件中 Test 的地址是否为 ORIGIN(flash) + 0x200。
5.2 定义变量与函数到指定 RAM 地址
RAM 上电内容随机,带初值变量由 startup 函数在 main 之前完成赋值。若需将变量定位到指定 RAM 地址(如 0x10008000 的 UserRAMData),在 .data 内插入定位语句:
1. 修改脚本:在 .data 内 *(.indata*) 系列之后插入:
. = ALIGN(4);
*(.indata*) /* server space for ram function */
*(.inrdata*)
*(.inrodata*)
/*------------- 新增内容 -------------*/
. = ORIGIN(ram) + 0x8000; /* 定位到 0x10008000(32K 保持区之后),务必加 ORIGIN(ram) */
*(.UserRAMData*)
/*------------------------------------*/
. = ALIGN(4);
*(.data*) /* global variable with initialization */
. = ALIGN(4);
__data_end__ = .;
2. 修改源码:
__attribute__((section(".UserRAMData"))) uint8 Testram[] = "12345678";
- 默认变量段为
.data、.bss或 COMMON,同属性合并连续存放;__data_end__与__bss_start__应连续,中间不应插入自定义段。 __data_start__与__data_end__之间的内容作为全局带初始化数据识别;区间内无初值的指定段默认按 0 填充,该部分内容会附加到 flash 中.text之后的装载空间。- 放在
__data_end__之后的固定地址段变量不在 startup 拷贝范围内,初值不会写入 RAM,应避免依赖初值;指定地址段应安排在.data前部顺序使用,避免因不连续填充 0 导致 flash 占用与初始化时间增长。
5.3 低功耗保持数据应用
SRAM 前 32K(LPRAM)在 STANDBY 及 STOP1 下可保持:休眠前将 PM_CTL0 的 bit19 置 1。唤醒后代码从头运行,若要保留数据需避免每次复位执行默认 startup。
1. 修改启动流程:将向量表入口改为 main,由 main 判断是冷启动还是唤醒,分别调用默认 startup 或变型 startup1。
2. 变型 startup 跳过 .lpdata 段的拷贝(保持区变量不复位初值):
extern unsigned int __lpdata_start__; /* 脚本符号 */
extern unsigned int __lpdata_end__; /* 脚本符号 */
while (begin < end)
{
if (begin == (unsigned int *)(&__lpdata_start__))
begin = (unsigned int *)(&__lpdata_end__);
*begin++ = *s++;
}
3. 脚本对应部分(位于 .data 内、__data_start__ 之后,默认脚本已内置):
__lpdata_start__ = ALIGN(4);
*(.lpdata*) /* Reset but Keep Space */
/* . = ORIGIN(ram) + 0x8000 ; */ /* Max length limit is 32K byte */
__lpdata_end__ = ALIGN(4);
4. 源码变量使用保持段属性:
__attribute__((section(".lpdata"))) uint8 KeepData[] = "keep";
- 保持数据需求接近 32K 时,去掉注释改为
. = ORIGIN(ram) + 0x8000;,强制__lpdata_end__到 32K 偏移(未用空间按 0 填充计入装载内容); - 用量明确较小时可按实际需求缩小(如 0x400),减少装载空间与拷贝时间。
具体可保持的区域范围以产品规格书为准。
5.4 修改堆与栈大小
堆、栈的大小由链接脚本顶部的两个符号控制(见 3.4 .heap 与 .stack(堆栈段)):
PROVIDE(__Heap_Size__ = 0x400 ); /* 未定义 Heap_Mem 时的堆大小 */
__Stack_Size__ = 0; /* 0:沿用 Stack_Mem(0x200);需要更大栈时把 0 改为所需大小 */
- 堆:未在源码中定义
Heap_Mem数组(.heap_mem段)时,按__Heap_Size__预留,默认 0x400;定义了Heap_Mem时以数组大小为准。 - 栈:默认沿用启动源码(默认
vector.c)中.stack_mem段的Stack_Mem数组大小(默认 0x200),修改该数组即可;也可以在脚本顶部把__Stack_Size__改为非零值,直接指定栈大小并覆盖数组大小。栈区始终锚定在 RAM 顶部,向下扩展。 - 脚本中指定的栈大小小于
Stack_Mem数组时,数组放不进预留区,链接报 region overflow;非 4 字节对齐的取值会向上取整到 4 字节。
调整后请确认 RAM 总占用(.data + .bss + 堆 + 栈)不超过 RAM 容量,链接器溢出报错(region overflow)会在超限时触发。
5.5 为 Bootloader 预留 Flash 空间(双应用)
Bootloader 双应用场景下,将 MEMORY 中 flash 区域按引导程序大小切分,应用程序从偏移地址链接:
MEMORY
{
flash : ORIGIN = 0x004000, LENGTH = 512K - 16K /* 应用区从 0x4000 开始 */
…
}
注意事项:
- 应用工程的向量表随
flash新起始地址重新摆放,Bootloader 跳转前需将向量表地址告知内核(按芯片手册操作); - Bootloader 工程单独编译,其 flash 区域为
ORIGIN = 0x00000000, LENGTH = 16K; - 两工程的 HEX 可合并烧录,注意配置字区域(
mode_mem/pro_mem)只在其中一个工程中生成。