简介
在使用ARM Keil
工具链的项目中,分散加载文件是编译过程中必不可少的一个文件,它在代码编译的链接期间起作用,链接器根据sct文件的配置分配各个节区地址,生成分散加载代码。就是.sct
文件(Scatter-Loading File
)。
其实在Linux环境下用gcc
编译时也需要用到类似的文件,叫做链接脚本文件,就是.ld
文件,例如在使用STM32CubeIDE
工具的项目中。
如何查看和生成链接脚本
以Keil
项目为例,我们使用的是STM32F407ZGT6
的MCU。STM32F407ZGT6
的存储器资源是分析链接脚本的基础:
- Flash (ROM): 1 MB,起始地址
0x0800 0000
- SRAM (RAM): 192 KB,分为:
- 112KB SRAM1 + 16KB SRAM2,起始地址
0x2000 0000
(CPU和DMA均可访问) - 64KB CCMRAM,起始地址
0x1000 0000
(仅CPU可访问,DMA不能访问)
- 112KB SRAM1 + 16KB SRAM2,起始地址
在魔术棒的target页中,我们已经配置了Flash和SRAM的地址和大小。(这里ROM的起始地址修改了,是因为我们的项目中有bootloader,并不影响本篇文章的介绍内容)
在Linker页中,有一个选项Use Memory Layout from Target Dialog
,这个如果勾选后,就不允许点击Edit
按钮了。也就是说.sct
文件是根据target dialog
中的配置自动生成的,我们可以到.\Haptic\Haptic.sct
查看该文件。
LR_IROM1 0x08020000 0x00100000 { ; 加载区域(Load Region),名为 LR_IROM1
ER_IROM1 0x08020000 0x00100000 { ; 第一个执行区域(Execution Region),名为 ER_IROM1
*.o (RESET, +First) ; 向量表必须放在最开头
*(InRoot$$Sections) ; 特殊的库段(如 __main.o, __scatter*.o)
.ANY (+RO) ; 所有只读数据(代码和常量)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00020000 { ; 第二个执行区域,名为 RW_IRAM1
.ANY (+RW +ZI) ; 所有可读写数据(全局/静态变量)和未初始化数据
}
RW_IRAM2 0x10000000 0x00010000 { ; 第二个执行区域,名为 RW_IRAM2
.ANY (+RW +ZI) ; 所有可读写数据(全局/静态变量)和未初始化数据
}
}
LR_IROM1 定义了加载区域(Load Region)位于 Flash(起始地址 0x08020000,大小 0x00100000)。
ER_IROM1 定义了执行区域(Execution Region)位于 Flash,并指定了哪些代码段放置在此区域。
RW_IRAM1 定义了可读写区域(Read-Write Region)位于 SRAM(起始地址 0x20000000,大小 0x00020000),用于存放数据。
RW_IRAM2 定义了可读写区域(Read-Write Region)位于 SRAM(起始地址 0x10000000,大小 0x00010000),用于存放数据。
修改SCT文件
一般我们修改.sct
文件是为了把函数放到SRAM中运行,或者把变量放在指定位置。我们使用自己修改的.sct
文件,就需要这样配置:
在这里插入图片描述
这样配置后不再根据target dialog
中的配置自动生成的.sct
文件,并且我们可以直接Edit
。
将变量指定到某一块RAM中
在代码中定义数组时,使用 __attribute__((section("your_section_name")))
来指定一个自定义的段名。例如在F407MCU中,DMA不能访问CCMRAM中的内存,但是可以访问其他地方的RAM。
/* 将数组放置在 SRAM1 的区域 */
uint8_t my_array_sram1[1024] __attribute__((section(".sram1_section")));
然后修改.sct
文件
LR_IROM1 0x08020000 0x00100000 { ; 加载区域(Load Region),名为 LR_IROM1
ER_IROM1 0x08020000 0x00100000 { ; 第一个执行区域(Execution Region),名为 ER_IROM1
*.o (RESET, +First) ; 向量表必须放在最开头
*(InRoot$$Sections) ; 特殊的库段(如 __main.o, __scatter*.o)
.ANY (+RO) ; 所有只读数据(代码和常量)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00020000 { ; 第二个执行区域,名为 RW_IRAM1
.ANY (+RW +ZI) ; 所有可读写数据(全局/静态变量)和未初始化数据
*(.sram1_section) ; 将自定义的 sram1_section 段也放在这里
}
RW_IRAM2 0x10000000 0x00010000 { ; 第二个执行区域,名为 RW_IRAM2
.ANY (+RW +ZI) ; 所有可读写数据(全局/静态变量)和未初始化数据
}
}