【STM32】sct 分散加载文件的格式与应用

news2024/11/25 6:35:06

简介

当工程按默认配置构建时,MDK 会根据我们选择的芯片型号,获知芯片的内部FLASH 及内部 SRAM 存储器概况,自动生成一个以工程名命名的后缀为*.sct 的分散加载文件(Linker Control File,scatter loading),链接器根据该文件的配置分配各个节区地址,生成分散加载代码,因此我们通过修改该文件可以定制具体节区的存储位置

一般情况下,不需要编写分散加载文件,由编译器自动生成,将某些数据放到默认位置。但是在某些场合,希望将某些数据放在指定的位置。

例如可以设置源文件中定义的所有变量自动按地址分配到外部 SRAM,这样就不需要再使用关键字“attribute”按具体地址来指定了;

复杂内存映射:如果必须将代码和数据放在多个不同的内存区域中,则需要使用详细指令指定将哪些数据放在哪个内存空间中。

不同类型的内存:许多系统都包含多种不同的物理内存设备,如闪存、 ROM、 SDRAM 和快速 SRAM。分散加载描述可以将代码和数据与最适合的内存类型相匹配。例如,可以将中断代码放在快速 SRAM 中以缩短中断等待时间,而将不经常使用的配置信息放在较慢的闪存中。

位于固定位置的函数:可以将函数放在内存中的固定位置,即使已修改并重新编译周围的应用程序。

使用符号标识堆和堆栈:链接应用程序时,可以为堆和堆栈位置定义一些符号。

利用它还可以控制代码的加载区与执行区的位置,例如可以把程序代码存储到单位容量价格便宜的 NAND-FLASH 中,但在NAND-FLASH 中的代码是不能像内部 FLASH 的代码那样直接提供给内核运行的,这时可通过修改分散加载文件,把代码加载区设定为 NAND-FLASH 的程序位置,而程序的执行区设定为 SRAM 中的位置,这样链接器就会生成一个配套的分散加载代码,该代码会把NAND-FLASH 中的代码加载到 SRAM 中,内核再从 SRAM 中运行主体代码,大部分运行Linux 系统的代码都是这样加载的

sct文件的格式

在这里插入图片描述
在默认的 sct 文件配置中仅分配了 Code、RO-data、RW-data 及 ZI-data 这些大区域的地址,链接时各个节区(函数、变量等)直接根据属性排列到具体的地址空间。

sct 文件中主要包含描述加载域及执行域的部分,一个文件中可包含有多个加载域,而一个加载域可由多个部分的执行域组成。同等级的域之间使用花括号“{}”分隔开,最外层的是加载域第二层“{}”内的是执行域

在这里插入图片描述
在这里插入图片描述

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
 
LR_IROM1 0x08000000 0x00020000  {    
	//定义一个加载域,域地址0x08000000,域大小为0x00020000
    //load region size_region 所有代码需要下载到0x08000000 开始的区域中,且这个区域大小只有0x00020000 
  ER_IROM1 0x08000000 0x00020000  {  
	//load address = execution address 第一个运行时域必须和加载域起始地址相同,其大小一般也相同
    //只能是只读的代码段和只读数据段
   *.o (RESET, +First)        //启动代码的首次执行地址,RO执行域名称为ER_IROM1, 
    //将 RESET 段最先加载到本域的起始地址外
    //首次执行的地址为RESET标号所表示的地址,RESET 存储的是向量表
    //对应启动文件中的AREA    RESET, CODE, READONLY
   *(InRoot$$Sections)     //稍后文件中会单独讲到
   .ANY (+RO) //加载所有匹配目标文件的只读属性数据,包含:Code、 RW-Code、 RO-Data。
  }
  RW_IRAM1 0x20000000 0x00005000  {  
    //再定义一个运行时域,域基址0x20000000 
	//RW data 执行域是以0x20000000 开始的长度为0x00004000  一段区域
   .ANY (+RW +ZI)    //其中包括的是哪些文件
  }
}

加载区描述

加载域名 (基地址 | ("+" 地址偏移)) [属性列表] [最大容量]
{
	执行区域描述
}
LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
......
}

加载域名:名称,在 map 文件中的描述会使用该名称来标识空间。如本例中只有一个加载域,该域名为 LR_IROM1。
基地址+地址偏移:这部分说明了本加载域的基地址,可以使用+号连接一个地址偏移,算进基地址中,整个加载域以它们的结果为基地址。如本例中的加载域基地址为 0x08000000,刚好是 STM32 内部 FLASH 的基地址。
属性列表:属性列表说明了加载域的是否为绝对地址、N 字节对齐等属性,该配置是可选的。本例中没有描述加载域的属性。
最大容量:最大容量说明了这个加载域可使用的最大空间,该配置也是可选的,如果加上这个配置后,当链接器发现工程要分配到该区域的空间比容量还大,它会在工程构建过程给出提示。本例中的加载域最大容量为 0x00080000,即 512KB,正是本型号 STM32 内部 FLASH 的空间大小。

执行域

执行域名 (基地址 | "+" 地址偏移) [属性列表] [最大容量 ]
{
///输入节区描述
}

执行域的格式与加载域是类似的,区别只是输入节区的描述有所不同

输入节区描述 .o .ANY (+RO)等的用法

配合加载域及执行域的配置,在相应的域配置“输入节区描述”即可控制该节区存储到域中

 //除模块选择样式部分外,其余部分都可选选填
 模块选择样式"("输入节区样式",""+"输入节区属性")"
 模块选择样式"("输入节区样式",""+"节区特性")"

 模块选择样式"("输入符号样式",""+"节区特性")"
 模块选择样式"("输入符号样式",""+"输入节区属性")"
 -----------------------------------------------------------------------------
    *.o (RESET, +First)        //启动代码的首次执行地址,RO执行域名称为ER_IROM1, 
    //将 RESET 段最先加载到本域的起始地址外
    //首次执行的地址为RESET标号所表示的地址,RESET 存储的是向量表
    //对应启动文件中的AREA    RESET, CODE, READONLY
   *(InRoot$$Sections)     //稍后文件中会单独讲到
   .ANY (+RO) //加载所有匹配目标文件的只读属性数据,包含:Code、 RW-Code、 RO-Data。
  ------------------------------------------------------------------------------

模块选择样式:模块选择样式可用于选择 o 及 lib 目标文件作为输入节区,它可以直接使用目标文件名或“*”通配符,也可以使用“.ANY”。
“.o”可以选择所有 o 文件.
使用“.lib”可以选择所有 lib 文件.
使用“”或“.ANY”可以选择所有的 o 文件及 lib 文件
其中“.ANY”选择语句的优先级是最低的,所有其它选择语句选择完剩下的数据才会被“.ANY”语句选中。

示例文件中“(RESET,+First)”语句的 RESET 就是输入节区样式,它选择了名为 RESET 的节区,并使用后面介绍的节区特性控制字“+First”表示它要存储到本区域的第一个地址。示例文件中的“(InRoot$$Sections)”是一个链接器支持的特殊选择符号,它可以选择所有标准库里要求存储到 root 区域的节区,如__main.o、__scatter.o 等内容。

输入符号样式:同样地,使用输入符号样式可以选择要控制的符号,符号样式需要使用“:gdef:”来修饰。例如可以使用“*(:gdef:Value_Test)”来控制选择符号“Value_Test”。

输入节区属性:通过在模块选择样式后面加入输入节区属性,可以选择样式中不同的内容,每个节区属性描述符前要写一个“+”号,使用空格或“,”号分隔开.

节区属性描述符说明
RO-CODE 及 CODE只读代码段
RO-DATA 及 CONST只读数据段
RO 及 TEXT包括 RO-CODE 及 RO-DATA
RW-DATA可读写数据段
RW-CODE可读写代码段
RW 及 DATA包括 RW-DATA 及 RW-CODE
ZI 及 BSS初始化为 0 的可读写数据段
XO只可执行的区域
ENTRY节区的入口点

例如,示例文件中使用“.ANY(+RO)”选择剩余所有节区 RO 属性的内容都分配到执行域ER_IROM1中,使用“.ANY(+RW +ZI)”选择剩余所有节区 RW及 ZI属性的内容都分配到执行域 RW_IRAM1 中。

节区特性:节区特性可以使用“+FIRST”或“+LAST”选项配置它要存储到的位置,FIRST 存储到区域的头部,LAST 存储到尾部。通常重要的节区会放在头部,而CheckSum(校验和)之类的数据会放在尾部。

例如示例文件中使用“(RESET,+First)”选择了 RESET 节区,并要求把它放置到本区域第一个位置,而 RESET 是工程启动代码中定义的向量表

    *.o (RESET, +First)        //启动代码的首次执行地址,RO执行域名称为ER_IROM1, 
    //将 RESET 段最先加载到本域的起始地址外
    //首次执行的地址为RESET标号所表示的地址,RESET 存储的是向量表

如图,启动文件中该向量表中定义的堆栈顶和复位向量指针必须要存储在内部 FLASH 的前两个地址,这样 STM32 才能正常启动,所以必须使用 FIRST 控制它们存储到首地址。

; Vector Table Mapped to Address 0 at Reset
 AREA RESET, DATA, READONLY
 EXPORT __Vectors
 EXPORT __Vectors_End
 EXPORT __Vectors_Size

 __Vectors DCD __initial_sp; Top of Stack
 DCD Reset_Handler; Reset Handler
 DCD NMI_Handler; NMI Handler

通过 MDK 配置选项来修改 sct 文件

.sct文件一般在project文件夹中。

选择 sct 文件的产生方式

首先需要选择 sct 文件产生的方式,选择使用 MDK 生成还是使用用户自定义的 sct 文件。在 MDK 的“Options for Target->Linker->Use Memory Layout from Target Dialog”选项可配置该选择。
在这里插入图片描述
1.勾选时,由keil根据配置生成sct文件。keil会根据“Options for Target”对话框中的选项生成 sct 文件,这种情况下,即使我们手动打开它生成的 sct文件编辑也是无效的,因为每次构建工程的时候,MDK 都会生成新的 sct 文件覆盖旧文件。该选项在 MDK 中是默认勾选的,若希望 MDK 使用我们手动编辑的 sct 文件构建工程,需要取消勾选,并通过 Scatter File 框中指定 sct 文件的路径。

由keil生成时

勾选Use Memory Layout from Target Dialog,由keil根据配置生成sct文件。keil会根据“Options for Target”对话框中的选项生成 sct 文件。

MDK 选项中的内部 ROM 选项最多只可以填充两个选项位置,若想把内部 ROM 分成多片地址管理就无法实现了;另外 MDK 配置可控的最小粒度为文件,若想控制特定的节区需要直接编辑 sct 文件

配置keil选项 改变sct文件

1.在 Device 标签页中选择芯片的类型
在这里插入图片描述
2.选定了芯片的型号为 STM32F103ZE后,在 Target 标签页中的存储器信息会根据芯片更新。
在 Target 标签页中存储器信息分成只读存储器(Read/Only Memory Areas)和可读写存储器(Read/Write Memory Areas)两类,即 ROM 和 RAM,而且它们又细分成了片外存储器(off-chip)和片内存储器(on-chip)两类。

修改 Target标签页中的这些存储信息,例如,把 STM32内部的 SRAM分成两等份
在这里插入图片描述
修改了 IRAM1 基地址后的 sct 文件内容

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00080000 { ; load region size_region
	ER_IROM1 0x08000000 0x00080000 { ; load address = execution address
		*.o (RESET, +First)
		*(InRoot$$Sections)
		.ANY (+RO)
	 }
	 RW_IRAM1 0x20000000 0x00008000 { ; RW data
	 .ANY (+RW +ZI)
	 }
	 RW_IRAM2 0x20008000 0x00008000 {
	 .ANY (+RW +ZI)
	 }
}

控制文件分配到指定的存储空间

设定好存储器的信息后,可以控制各个源文件定制到哪个部分存储器,,在 MDK 的工程文件栏中,选中要配置的文件,右键,并在弹出的菜单中选择“Options for File xxxx”即可弹出一个文件配置对话框,在该对话框中进行存储器定制。
在这里插入图片描述
对话框中有一个“Memory Assignment”区域(存储器分配),在该区域中可以针对文件的各种属性内容进行分配,如 Code/Const 内容(RO)、Zero Initialized Data 内容(ZIdata)以及 Other Data 内容(RW-data),IROM1、IRAM1、IRAM2 等存储器。

例如图中我们把这个 bsp_led.c 文件的 Other Data 属性的内容分配到了 IRAM2 存储器(在 Target 标签页中我们勾选了 IRAM1 及 IRAM2),当在bsp_led.c 文件定义了一些 RW-data 内容时(如初值非 0 的全局变量),该变量将会被分配到IRAM2 空间,配置完成后点击 OK,然后编译工程,查看到的 sct 文件内容。

LR_IROM1 0x08000000 0x00080000{ ; load region size_region
	ER_IROM1 0x08000000 0x00080000{ ; load address = execution address
		*.o (RESET, +First)
		*(InRoot$$Sections)
		.ANY (+RO)
	}
	RW_IRAM1 0x20000000 0x00008000 { ; RW data
		.ANY (+RW +ZI)
	}
	 RW_IRAM2 0x20008000 0x00008000 { 
	 bsp_led.o (+RW)
	 .ANY (+RW +ZI)
	}
 }

可以看到在 sct 文件中的 RW_IRAM2 执行域中增加了一个选择 bsp_led.o 中 RW 内容的语句。

手动修改sct文件

虽然 MDK 的这些存储器配置选项很方便,但有很多高级的配置还是需要手动编写 sct文件实现的,例如 MDK 选项中的内部 ROM 选项最多只可以填充两个选项位置,若想把内部 ROM 分成多片地址管理就无法实现了;另外 MDK 配置可控的最小粒度为文件,若想控制特定的节区也需要直接编辑 sct 文件

补充“_ _attribute __”关键字的说明

当需要指定某个变量的内存地址时, MDK提供了 一 个关键字“_ _ attribute _ _”实现该功能。

/*要指定的地址*/
 #define USER_ADDR((uint32_t)0x20005000)
 
 uint8_t testValue __attribute__((at(USER_ADDR)));/*使用 atribute 指定该变量存储到 USER_ADDR,这种方式必须定义成全局变量*/
 testValue = 0xDD;

式使用 “attribute((at()))”来指定变量的地址,代码中指定 testValue 存储到USER_ADDR地址 0x20005000中.若把该地址改为外部存储器 SRAM的地址,变量就会被存储到外部 SRAM 了,因而利用该关键字在一定程度上可以定制各种存储器的空间分配。要注意使用这种方法定义变量时,必须在函数外把它定义成全局变量,才可以存储到指定地址上。

当有多个这样的变量时,为了防止变量占用的空间重叠,或减少碎片空间进行充分利用,就需要手动计算各个变量的地址了,非常麻烦,利用sct文件让链接器自动分配全局变量到指定的存储区域并进行管理,可以使得利用指定存储区域时就跟普通的变量定义一样简单。

例:自动分配变量到指定的 SRAM 空间

取消勾选Options for Target->Linker->Use Memory Layout from Target Dialog

(1) 修改启动文件,在__main 执行之前初始化“指定的存储空间”的硬件
(2) 在 sct 文件中增加“指定的存储空间”对应的执行域;
(3) 使用节区选择语句选择要分配到“指定的存储空间”的内容;
(4) 编写测试程序,编译正常后,查看 map 文件的空间分配情况。

1.修改启动文件 在__main 之前初始化外部“指定的存储空间”的硬件

芯片启动后,ResetHandler中会通过__main 函数调用分散加载代码__scatterload,分散加载代码会把存储在 FLASH 中的 RW-data 复制到 RAM中,然后在 RAM 区开辟一块 ZI-data 的空间,并将其初始化为0值。

因此,为了保证在程序中定义到“指定的存储空间”中的变量能被正常初始化,我们需要在系统执行分散加载代码之前使该空间使用的存储器正常运转,使它能够正常保存数据。如果把存储器的初始化放到 C 语言的 main 函数才执行,那么由于__scatterload 时存储器没有正常工作,所以拷贝过程无效,那么 RW-data 类型变量的初值都会不正常,这会导致程序运行不符合预期。

所以需要修改 启动文件
在这里插入图片描述

; Reset handler//注释
Reset_Handler 	PROC //Reset_Handler程序开始
				EXPORT Reset_Handler[WEAK]
				IMPORT SystemInit//导入  SystemInit函数
				IMPORT __main
// 从外部文件引入声明, 格式:IMPORT 要调用的初始化函数名
// 以下语句仅作演示,使用外部存储器时请去掉注释用的“; ”号,本工程使用内部 SRAM,无需初始化
				; IMPORT FSMC_SRAM_Init//使用外部RAM的时候,需要初始化FSMC这个外设
				 LDR R0, =SystemInit//将 SystemInit函数地址赋值到R0 执行R0 也就是说执行 SystemInit函数,初始化时钟等
				 BLX R0

				; 在__main 之前调用 FSMC_SRAM_Init 进行初始化
//以下语句仅作演示,使用外部存储器时请去掉注释用的“; ”号,本工程使用内部 SRAM,无需初始化
				; LDR R0, =FSMC_SRAM_Init//使用外部RAM的时候,需要初始化FSMC这个外设
				; BLX R0
		
		 		LDR R0, =__main
				 BX R0
		 		ENDP

如果需要使用到扩充SRAM,那么需要在进入__main函数前启动FSMC外设,使得芯片可以操作外置的SRAM。STM32外设FSMC没有使用到外置的SRAM就不需要这么操作了。

引入用户在其它 C 语言文件中定义的名为 FSMC_SRAM_Init 的函数,接着使用 LDR 指令加载函数的代码地址到寄存器 R0,最后使用BLX R0 指令跳转到 FSMC_SRAM_Init 的代码地址执行。

2.修改sct文件 在 sct 文件中增加“指定的存储空间”对应的执行域

来修改 sct 文件,控制使得在 C 源文件中定义的全局变量都自动由链接器分配到“指定的存储空间”(这里的例子是0x20005000 至 0x2000C000 地址的内部 SRAM 空间)

 *************************************************************
 *** Scatter - Loading Description File generated by uVision ***
 *************************************************************

LR_IROM1 0x08000000 0x00080000{; 加载域
	ER_IROM1 0x08000000 0x00080000{; 加载地址 = 执行地址
		* .o(RESET, +First)
		* (InRoot$$Sections)
		ANY(+RO)
	 }
	 RW_IRAM1 0x20000000 0x00005000 {; 内部 SRAM
		 * .o(STACK); 选择 STACK 节区,栈
		 stm32f10x_rcc.o(+RW); 选择 stm32f10x_rcc 的 RW 内容
		.ANY(+RW + ZI); 其余的 RW / ZI - data 都分配到这里
	 }
	 RW_ERAM1 0x20005000 0x00007000{; “指定的存储空间”
		.ANY(+RW + ZI); 其余的 RW / ZI - data 都分配到这里
 		}
 }

有两个注意点:
1.RW_ERAM1 0x20005000 0x00007000{}

RW_ERAM1 是我们配置的“指定的存储空间”的执行域,该执行域的名字是可以随便取的,最重要的是它的基地址及空间大小,在这个案例中,起始地址和偏移之后都在片上RAM之内。

在 RW_ERAM1 执行域内部,它使用“.ANY(+RW +ZI)”语句,选择了所有的 RW/ZI 类型的数据都分配到这个“指定的存储空间”,所以我们在工程中的 C 文件定义全局变量时,它都会被分配到该区域,若使用外扩存储器,这些数据就会存储到相应的存储器中。

2.RW_IRAM1 执行域
简单来说使用到了外部存储器SRAM需要加一句 “ .o(STACK) 及stm32f10x_rcc.o(+RW)”语句到RW_IRAM1 执行域(接下来的大段是理由,可以不看)
若“指定的存储空间”若是属于内部 SRAM,“.o(STACK)及stm32f10x_rcc.o(+RW)”语句不添加也是没有问题

这是原本sct文件中的执行域,在它的执行域配置中增加了 “ *.o(STACK) 及stm32f10x_rcc.o(+RW)”语句

本来上面配置外部 SRAM 执行域后已经达到使全局变量分配的目的,为何还要修改原内部 SRAM 的执行域呢?

当使用的是外部挂载的SRAM时。

这是由于如果在__main 之前调用的 FSMC_SRAM_Init 外部存储器初始化函数调用了很多库函数,且这些函数内部定义了一些局部变量,而函数内的局部变量是需要分配到“栈”空间(STACK),所以在 FSMC_SRAM_Init 函数执行之前,栈空间必须要被准备好,然而在 FSMC_SRAM_Init 函数执行之前,外部存储器却并未正常工作,这样的矛盾导致栈空间不能被分配到外部存储器区域。

虽然内部 SRAM 的执行域 RW_IRAM1 及“指定的存储空间”执行域 RW_ERAM1中都使用“.ANY(+RW +ZI)”语句选择了所有 RW 及 ZI 属性的内容,但对于符合两个相同选择语句的内容,链接器会优先选择使用空间较大的执行域,即这种情况下只有当“指定的存储空间”执行域的空间使用完了,RW/ZI 属性的内容才会被分配到内部SRAM。

所以在大部分情况下,内部 SRAM 执行域中的“.ANY(+RW +ZI)”语句是不起作用的,而栈节区(STACK)又属于 ZI-data 类,如果我们的内部 SRAM 执行域还是按原来的默认配置的话,栈节区会被分配到“指定的存储空间”,若此时“指定的存储空间”使用的是外部存储器,将会导致出错。为了避免这个问题,我们把栈节区使用“*.o(STACK)”语句分配到内部 SRAM 的执行域。

增加“stm32f10x_rcc.o(+RW)”语句是因为初始化外部存储器的 FSMC_SRAM_Init函数可能会调用stm32f10x_rcc.c文件中的RCC_AHBPeriphClockCmd函数,而查看map文件后了解到 stm32f10x_rcc.c定义了一些 RW-data类型的变量。不管这些数据是否在 FSMC_SRAM_Init 调用过程中使用到,保险起见,我们直接把这部分内容也分配到内部 SRAM 的执行区。

例 优先使用内部 SRAM 并把堆区分配到指定空间

配置 sct 文件,使得默认情况下优先使用内部 SRAM 空间,在需要的时候使用一个关键字指定变量存储到“指定的存储空间”,另外,我们还把系统默认的堆空间(HEAP)映射到“指定的存储空间”,从而可以使用 C 语言标准库的 malloc 函数动态从中分配变量,利用标准库对该空间内存管理,这在外部内存管理中非常有用。

1.修改启动文件 在__main 之前初始化外部“指定的存储空间”的硬件

修改工程 startup_stm32f10x.s 启动文件中的 Reset_handler 函数,在__main 函数之前调用该存储器的初始化函数使硬件正常运转

; Reset handler//注释
Reset_Handler 	PROC //Reset_Handler程序开始
				EXPORT Reset_Handler[WEAK]
				IMPORT SystemInit//导入  SystemInit函数
				IMPORT __main
// 从外部文件引入声明, 格式:IMPORT 要调用的初始化函数名
// 以下语句仅作演示,使用外部存储器时请去掉注释用的“; ”号,本工程使用内部 SRAM,无需初始化
				; IMPORT FSMC_SRAM_Init//使用外部RAM的时候,需要初始化FSMC这个外设
				 LDR R0, =SystemInit//将 SystemInit函数地址赋值到R0 执行R0 也就是说执行 SystemInit函数,初始化时钟等
				 BLX R0

				; 在__main 之前调用 FSMC_SRAM_Init 进行初始化
//以下语句仅作演示,使用外部存储器时请去掉注释用的“; ”号,本工程使用内部 SRAM,无需初始化
				; LDR R0, =FSMC_SRAM_Init//使用外部RAM的时候,需要初始化FSMC这个外设
				; BLX R0
		
		 		LDR R0, =__main
				 BX R0
		 		ENDP

2.修改sct文件 在 sct 文件中增加“指定的存储空间”对应的执行域

 *************************************************************
 *** Scatter - Loading Description File generated by uVision ***
 *************************************************************

LR_IROM1 0x08000000 0x00080000{; 加载域
	ER_IROM1 0x08000000 0x00080000{; 加载地址 = 执行地址
		* .o(RESET, +First)
		* (InRoot$$Sections)
		ANY(+RO)
	 }
	 RW_IRAM1 0x20000000 0x00005000 {; 内部 SRAM
		 * .o(STACK); 选择 STACK 节区,栈
		 stm32f10x_rcc.o(+RW); 选择 stm32f10x_rcc 的 RW 内容
		.ANY(+RW + ZI); 其余的 RW / ZI - data 都分配到这里
	 }
	 RW_ERAM1 0x20005000 0x00007000{; “指定的存储空间”
	 	*.o(HEAP) ;选择堆区
		.ANY (EXRAM) ;选择 EXRAM 节区
 		}
 }

使用了 “ *.o(HEAP) ” 把所有堆区分配到了RW_ERAM1 ,使用“.ANY(EXRAM)”语句把名为“EXRAM”的节区分也配到 RW_ERAM1。

“EXRAM”节区是由我们自定义的的,在语法上就跟在 C 文件中定义全局变量类似,只要它跟工程中的其它原有节区名不一样即可。有了这个节区选择配置,我们需要定义变量到“指定的存储空间”时,只需要指定该变量分配到该节区,它就会被分配到该空间中。

3.指定变量分配到节区

之前1.2两点已经设置完毕,接下来就是使用了。

使用方式1

//使用 __attribute__ 关键字定义指定变量定义到某节区

//语法: 变量定义 __attribute__ ((section ("节区名"))) = 变量值; 
uint32_t testValue __attribute__((section("EXRAM"))) = 7;

“变量定义 attribute ((section (“节区名”))) = 变量值;”,它的主体跟普通的 C 语言变量定义语法无异.
在赋值“=”号前(可以不赋初值),加了个“__attribute__ ((section ("节区名")))”描述它要分配到的节区。本例中的节区名为“EXRAM”,即我们在 sct 文件中选择分配到“指定的存储空间”执行域的节区,所以该变量就被分配到该存储器中了。

使用方式2

//使用宏封装 
//设置变量定义到“EXRAM”节区的宏
#define __EXRAM __attribute__((section("EXRAM")))

//使用该宏定义变量到“指定的存储空间”
 uint32_t testValue __EXRAM = 7;

由于“__attribute__”关键字写起来比较繁琐,我们可以使用宏定义把它封装起来,简化 代 码 。 本 例 中 我 们 把 指 定 到 “ EXRAM ” 的 描 述 语 句 “__attribute__ ((section ("EXRAM")))”封装成了宏“ __EXRAM”,应用时只需要使用宏的名字替换原来“attribute”关键字的位置即可,如“uint32_t testValue __EXRAM =7 ;”。有 51 单片机使用经验的读者会发现,这种变量定义方法就跟使用 keil 51 特有的关键字“xdata”定义变量到外部 RAM 空间差不多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1048682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

巨人互动|Google海外户Google SEO工作为何要把控细节

Google SEO(搜索引擎优化)是一项为了提高网站在Google搜索结果中的排名和可见性的策略和技术。在进行SEO工作时,把控细节非常重要,本文小编讲讲关于为何要把控细节的原因。 巨人互动|Google海外户&Google内容定位介绍&#xf…

当当网商品详情数据接口

当当网商品详情数据接口可以通过当当网的开放平台获取相关信息。您可以注册当当开放平台账号,并按照要求提交申请获取API接口的调用凭证。获得授权后,您将会收到一组AccessKey和SecretKey。使用编程语言(如Java)调用API接口&#…

聊一聊JDK21-虚拟线程

目录 前言 Virtual Threads的开始 为什么需要Virtual Threads JDK19 预览版初次出现 JDK21 Virtual Threads的正式发布 Virtual Threads 该怎么使用 简单聊聊Virtual Threads的实现 使用时候的注意事项 本地尝鲜一下JDK21及Virtual Threads 结语 前言 2023年9月19日…

26272-2010 地面数字电视调谐器基本性能要求和测量方法.

声明 本文是学习GB-T 26272-2010 地面数字电视调谐器基本性能要求和测量方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了地面数字电视接收设备用特高频/超高频(VHF/UHF) 电子式调谐器基本性能要求 和测量方法。 本标准适用…

数据结构与算法(C语言版)P8---树、二叉树、森林

【本节目标】 树概念及结构。二叉树概念及结构。二叉树常见OJ题练习。 1、树概念及结构 1.1、树的概念 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树&#xf…

再看dockerfile指令用法:80分钟一口气学完docker+k8s!带你掌握docker+k8s所有核心知识点,全程干货,无废话!

dockerfile实践 需求:通过dockerfile,构建nginx镜像,且容器运行后,生成的页面显示“书季,要攒劲哦~” #1、穿件dockerfile,注意文件名必须是这个。 [rootHadoop2 learn_docker]# cat dockerfile FROM nginx RUN echo…

linux opensuse使用mtk烧录工具flashtool

环境 linux发行版:opensuse leap 15.5 工具:SP_Flash_Tool_Selector_exe_Linux_v1.2316.00.100.rar 或其他版本 目标:mtk设备 下载链接 https://download.csdn.net/download/zmlovelx/88382784 或网络搜索。 使用 opensuse可直接解压后使…

HOMER7配置告警

概述 HOMER是一款100%开源的针对SIP/VOIP/RTC的抓包工具和监控工具。 HOMER是一款强大的、运营商级、可扩展的数据包和事件捕获系统,是基于HEP/EEP协议的VoIP/RTC监控应用程序,并可以使用即时搜索、处理和存储大量的信令、RTC事件、日志和统计信息。 …

【数据仓库设计基础(二)】维度数据模型

文章目录 一. 概述二. 维度数据模型建模过程三. 维度规范化四. 维度数据模型的特点五. 维度数据模型1. 星型模式1.1.事实表1.2.维度表1.3.优点1.4.缺点1.5.示例 2. 雪花模式2.1.数据规范化与存储2.2&#x…

PreMaint设备管理系统:实现制药企业的CSV合规性

在当今数字化时代,制药企业越来越依赖计算机化系统来支持其各个方面的运营,从研发到生产再到质量控制。然而,这些系统的使用不仅需要高效性和可靠性,还需要符合法规要求,尤其是药品生产质量管理规范(Good M…

新零售革命:可视化助力零售业焕发新生

一、什么是新零售? 新零售是一种融合了传统零售业和数字科技的商业模式,旨在提升零售业的效率、便捷性和个性化。它将线上和线下的零售渠道结合在一起,通过数字技术、大数据分析、人工智能等手段,实现了以下几个主要特征和目标&a…

图像语义分割 FCN图像分割网络详解

图像语义分割 FCN图像分割网络详解 0、介绍1、VGG16网络结构2、转置卷积3、FCN-32S、FCN-16S,FCN-8S网络结构4、损失函数5、膨胀卷积6、FCN(Backbone-ResNet-50)6.1 项目框架6.2 ResNet50网络结构6.3 FCN(Backbone-ResNet-50)网络结构6.4 FCN(Backbone-ResNet-50)模…

基于SpringBoot的大学生就业招聘系统的设计与实现

目录 前言 一、技术栈 二、系统功能介绍 求职信息管理 首页 招聘信息管理 岗位申请管理 岗位分类 企业管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息互联网信息的飞速发展,大学生就业成为一个难题,好多公司都舍不…

【RV1103】Luckfox Pico RV1103 开发记录

文章目录 对比uboot的差别Linux的差别其他差别编译命令对比板级配置选择spi-nand flashemmc/SD 卡spinand flash烧录差别由于没有原理图--引脚分析 对比 linux defconfiglinux dtsuboot defconfiguboot fragmentluckfox-picosd/tf (emmc)luckfox_rv1106_linux_defconfigrv1103…

澳大利亚海运价格下半年走势

随着全球疫情的逐渐缓解,国际贸易开始逐步恢复。在这个过程中,澳大利亚作为全球重要的贸易伙伴,其海运价格也成为了市场关注的焦点。本文将从下半年的市场预期、影响因素以及行业动态等方面,对澳大利亚海运价格走势进行分析展望。…

祝贺莱佛士学生在ASDA2023设计大赛中获得最高奖项

莱佛士一直主张学生们积极参与各种国际知名的设计大赛,也会竭尽所能为学生们的参赛提供途径与指导,本次的American Standard Design Award(ASDA)2023设计大赛也不例外。 ASDA2023设计大赛,推广以用户为中心的设计理念…

极简非凡react hooks+arcoDesign+vite后台管理模板

最近捣鼓了一个vite4搭建react18后台模板,搭载了字节团队react组件库Arco Design,整体编译运行顺滑衔接。支持多种模板布局、暗黑/亮色模式、国际化、权限验证、多级路由菜单、tabview标签栏快捷菜单、全屏控制等功能。 使用技术 "arco-design/web…

攀登数字化高峰,中小企业如何找“搭子”?

相信大多数人都认可,中小企业数字化,是一条充满未知和艰辛的征程。 这个过程,不是租几台云服务器、开发几个APP那么简单,而是一个对组织架构、业务环节、基础设施、商业模式等进行量身定制、长期迭代的体系化工程,需要…

船用白炽照明灯具

声明 本文是学习GB-T 3027-2012 船用白炽照明灯具. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了船用白炽照明灯具(以下简称灯具)的要求、试验方法、检验规则、标识、包装和储 存等。 本标准适用于电源电压在250V 以下的交流…

uni-app使用iconfont字体图标

先iconfont选择好自己需要的图标 添加至项目 下载字体文件到本地 将下载的文件解压缩到工程目录static文件夹下 定义好iconfont.css文件的font-face声明,修改好引入的url地址 打开App.vue文件 ,引入static下刚才修改的iconfont.css字体图标文件 完成上线的步骤后就可以全局使用…