单片机内存分配基础:SCT文件的基本概念

1、链接脚本的概念

  链接脚本(Linker Script)和链接器(Linker)是嵌入式开发中非常重要的概念,尤其是在像 Keil 这样的集成开发环境(IDE)中,它们帮助决定程序在目标设备内存中的布局。

  在 Keil MDK(μVision)中,链接器脚本文件(分散加载文件,Scatter-Loading File)用于控制代码和数据在内存中的分布。它通常以 .sct 为扩展名,由 ARM 链接器 (armlink) 使用。

  C语言经典的“Hello World”小程序几乎是每个程序员闭着眼睛都能写出来的,基本成了入门教程和开发环境的默认标准,代码如下:

#include <stdio.h>
int main()
{
  printf("Hello World\n");
  return 0;
}

  如果在 Windows 下使用 Visual Studio 来编译,那么可以直接点击运行(Run)按钮或者构建(Build)按钮,在工程目录下就会看到生成的 .exe 程序。

  如果在 Linux 下使用 GCC 来编译,使用最简单的$gcc demo.c命令,就可以在当前目录下看到 a.out。

  事实上,从源代码生成可执行文件可以分为四个步骤,分别是预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。下图是 GCC 生成 a.out 的过程:

  编译流程:https://www.cnblogs.com/The-explosion/articles/13622629.html

  下面是对它们的详细介绍:
  (1)链接器(Linker)是什么?
  链接器(Linker)是将编译好的目标文件(object files)和库文件合并 在一起,生成最终可执行文件(如 .elf、.bin 或 .hex)的工具。它负责将程序中的不同部分(如代码、数据、堆栈、堆等)放置到目标设备的内存空间中,并处理以下工作:
    符号解析:链接器会解析并解决程序中的符号,如函数和变量的地址,确保所有调用的函数和引用的全局变量在程序中能找到其定义。
    地址分配:将程序的不同部分分配到目标设备的内存空间中,确保代码和数据都放在合适的地址。
    段的合并:将编译生成的多个目标文件中相同类型的段(如代码段 .text,数据段 .data,未初始化数据段 .bss)合并到一起,形成一个统一的可执行文件。
    链接器最终会生成一个可执行文件,这个文件可以烧录到嵌入式设备中运行。
  (2)链接脚本(Linker Script)是什么?
  链接脚本是链接器用来指导如何把目标文件、库文件中的代码和数据放置到内存的各个部分中的一份文本文件。对于 ARM Cortex-M 系列嵌入式开发而言,链接脚本通常会规定哪些代码和数据应该被放在闪存(Flash)中,哪些应该放在 SRAM(RAM)中,以及堆栈和堆的大小和位置。
  简单来说,链接脚本控制了程序在物理内存中的布局,是内存映射(Memory Mapping)的关键部分。
  链接脚本的作用:
    定义内存布局:指定闪存、RAM 等不同内存区域的起始地址和大小。
    指定段的放置:规定哪些部分(代码、全局数据、常量等)放在程序的哪些内存区域中。
    设置堆栈和堆的地址:为堆栈(stack)和堆(heap)指定内存地址和大小。
    控制初始化数据的放置:在设备启动时,决定如何初始化全局和静态变量。

2、KEIL中的链接脚本文件

  在 Keil MDK(μVision)中,链接器脚本文件(分散加载文件,Scatter-Loading File)用于控制代码和数据在内存中的分布。它通常以 .sct 为扩展名,由 ARM 链接器 (armlink) 使用。

image


  对于Keil,先是armcc编译器对每个单独的.c文件编译生成.o文件,然后使用armlink链接器组合所有的.o文件,即结合分散加载文件即SCT文件,为其各自分配存储地址。SCT文件用户可自己编辑,从而指定代码和数据的存储地址。

  2.1、找到链接器脚本文件
  方法 1:通过 Keil 工程选项查看
    打开 Keil 工程(.uvprojx 或 .uvmpw),点击菜单栏 Options for Target(或按 Alt+F7),切换到 Linker 选项卡,在 Use Memory Layout from Target Dialog 下方,可以看到:

image

  Scatter File 选项(默认可能未启用)。
  如果勾选了 Use Memory Layout from Target Dialog,则内存分布由 Target 选项卡中的设置决定。如果取消勾选,可以手动指定 .sct 文件路径。

  方法 2:在工程目录中查找
  默认情况下,Keil 会自动生成 .sct 文件,存放在 Objects 或 Listings 目录下,文件名通常为 工程名.sct。你也可以手动创建 .sct 文件并指定路径。

  2.2、修改内存分布
  方法 1:通过 Target 选项卡修改(简单方式)
  进入 Options for Target → Target 选项卡。在 Read/Write Memory Areas 和 Code Memory Areas 中,可以添加、修改 RAM 和 Flash 的地址范围,修改后,Keil 会自动更新 .sct 文件。

image

  方法 2:手动编辑 .sct 文件(高级方式)
  取消勾选 Use Memory Layout from Target Dialog,点击 Scatter File 旁边的 ... 按钮,选择或创建 .sct 文件,编辑 .sct 文件,调整内存布局。

  2.3、 链接器脚本(.sct 文件)基础解读
  基本结构:.sct 文件由 加载域(Load Region) 和 执行域(Execution Region) 组成:

    加载域:定义代码和数据的存储位置(如 Flash)。
    执行域:定义代码和数据在运行时的位置(如 RAM)。

image

    简单示例 .sct 文件:

LR_IROM1 0x08000000 0x00080000 
 {    ; 加载域:Flash(起始地址 0x08000000,大小 512KB)

  ER_IROM1 0x08000000 0x00080000  
  {   ; 执行域:Flash(代码运行位置)
  
           *.o (RESET, +First)  ; 首先存放中断向量表
         *(InRoot$$Sections)    ; 系统初始化代码
           .ANY (+RO)           ; 所有只读数据(代码、常量)
  }
  
  RW_IRAM1 0x20000000 0x00010000 
   {   ; 执行域:RAM(起始地址 0x20000000,大小 64KB)
         
         .ANY (+RW +ZI)     ; 可读写数据(全局变量、堆栈等)
  }
}

  关键语法解析

image

  2.4、常见修改场景
  (1)调整 Flash 和 RAM 大小

LR_IROM1 0x08000000 0x00100000  {    
; 修改 Flash 大小为 1MB
  ...
}

RW_IRAM1 0x20000000 0x00020000  {    
; 修改 RAM 大小为 128KB
  ...
}

  可以调整这个段的起始地址和空间大小,可以划分成多个区域段
  (2)将部分代码或数据放入指定 RAM

RW_IRAM2 0x20010000 0x00008000  {    ; 新增 RAM 区域
   my_library.o (+RW +ZI)            ; 指定某个库的变量放在这里
   *(my_library_section)             ; 划分出一个段
}

//变量user指定存放到my_library_section段
__attribute__((section("my_library_section"))) unsigned char user[10]; 

  先定义出一个区域段即RW_IRAM2 ,然后在此区域段里定义具体的存储段或者指定某些变量或代码放到这里。
  (3)使用外部 RAM(如 SDRAM)

RW_ESDRAM 0xC0000000 0x01000000  {   ; 外部 SDRAM
   .ANY (EXTERNAL_RAM)               ; 自定义段
}

  2.5、综合例子
  综合的使用例子

image

  2.6、 注意事项

  修改 .sct 后必须重新编译,否则不会生效。
  变量地址检查:可以使用 map 文件(工程名.map)查看变量和函数的实际地址。
  避免冲突:确保不同执行域不重叠。
  特殊段:
    RESET:存放中断向量表。
    InRoot$$Sections:存放 C 库初始化代码。
    Heap 和 Stack:可在 .sct 中指定大小。

 

posted @ 2026-03-27 17:24  孤情剑客  阅读(11)  评论(0)    收藏  举报