您的位置 首页 知识

怎么设置STM32单片机非初始化数据变量不被零初始化

如何设置STM32单片机非初始化数据变量不被零初始化-一些产品,当系统复位后(非上电复位),可能要求保持住复位前RAM中的数据,用来快速恢复现场,或者不至于因瞬间复位而重启现场设备。而keil mdk在默认情况下,任何形式的复位都会将RAM区的非初始化变量数据清零。

一些产品,当体系复位后(非上电复位),或许要求坚持住复位前RAM中的数据,用来快速康复现场,或许不至于因瞬间复位而重启现场设备。而keil mdk在默许情况下,任何方式的复位都会将RAM区的非初始化变量数据清零。

在给出办法之前,先来了解一下代码和数据的寄存规矩、特点,以及复位后为何默许非初始化变量地点RAM都被初始化为零了呢。

什么是初始化数据变量,什么又对错初始化数据变量?(由于我的文字描述不必定精确,所以喜爱举一些例子来辅佐了解文字。)

界说一个变量:int nTImerCount=20;变量nTImerCount便是初始化变量,也便是现已有初值;

假设界说变量:int nTImerCount;变量nTImerCount便是一个非赋值的变量,Keil MDK默许将它放到特点为ZI的输入节。

那么,什么是“ZI”,什么又是“输入节”呢?这要了解一下ARM映像文件(image)的组成了,这部分内容略显无聊,但我以为这对错常有必要把握的。

怎样设置STM32单片机非初始化数据变量不被零初始化

ARM映像文件的组成:

一个映像文件由一个或多个域(region,也有译为“区”)组成

每个域包含一个或多个输出段(section,也有译为“节”)

每个输出段包含一个或多个输入段

各个输入段包含了方针文件中的代码和数据

输入段中包含了四类内容:代码、现已初始化的数据、未通过初始化的存储区域、内容初始化为零的存储区域。每个输入段有相应的特点:只读的(RO)、可读写的(RW)以及初始化成零的(ZI)。

一个输出段中包含了一些列具有相同的RO、RW和ZI特点的输入段。输出段特点与其间包含的输入段特点相同。

一个域包含一到三个输出段,各个输出段的特点各不相同:RO特点、RW特点和ZI特点

到这儿咱们就能够知道,一般情况下,代码会被放到RO特点的输入节,现已初始化的变量会被分配到RW特点输入区,而“ZI”特点输入节能够了解为是初始化成零变量的调集。

现已初始化变量的初值,会被放到硬件的哪里呢?(比方界说int nTimerCount=20;那么初始值20被放到哪里呢?),我觉得这是个风趣的问题,比方keil在编译完结后,会给出编译文件巨细的信息,如下所示:

Total RO Size (Code + RO Data) 54520 ( 53.24kB)

Total RW Size (RW Data + ZI Data) 6088 ( 5.95kB)

Total ROM Size (Code + RO Data + RW Data) 54696 ( 53.41kB)

很多人不知道这是怎样核算的,也不知道终究放入ROM/Flash中的代码有多少。其实,那些现已初始化的变量,是被放入RW特点的输入节中,而这些变量的初值,是被放入ROM/Flash中的。有时候这些初值的量比较大,Keil还会将这些初值紧缩后再放入ROM/Flash以节约存储空间。那这些初值是谁在何时将它们康复到RAM中的?ZI特点输入节中的变量地点RAM又是谁在何时给用零初始化的呢?要了解这些东西,就要看默许设置下,从体系复位,到履行C代码中你编写的main函数,Keil帮你做了些什么。

硬件复位后,榜首步是履行复位处理程序,这个程序的进口在发动代码里(默许),摘抄一段cortex-m3的复位处理进口代码:

1:Reset_HandlerPROC;PROC等同于FUNCTION,表明一个函数的开端,与ENDP相对?

2:

3:EXPORTReset_Handler[WEAK]

4:IMPORTSystemInit

5:IMPORT__main

6:LDRR0,=SystemInit

7:BLXR0

8:LDRR0,=__main

9:BXR0

10:ENDP

初始化仓库指针、履行完用户界说的底层初始化代码(SystemInit函数)后,接下来的代码调用了__main函数,这儿__main函数会调用一些列的C库函数,完结代码和数据的仿制、解紧缩以及ZI数据的零初始化。数据的解紧缩和仿制,其间就包含将储存在ROM/Flash中的已初始化变量的初值仿制到相应的RAM中去。关于一个变量,它或许有三种特点,用const润饰符润饰的变量最或许放在RO特点区,现已初始化的变量会放在RW特点区,那么剩余的变量就要放到ZI特点区了。默许情况下,ZI数据的零初始化会将一切ZI数据区初始化为零,这是每次复位后程序履行C代码的main函数之前,由编译器“自作主张”完结的。所以咱们要在C代码中设置一些变量在复位后不被零初始化,那必定不能任由编译器“肆无忌惮”,咱们要用一些规矩,束缚一下编译器。

涣散加载文件关于连接器来说至关重要,在涣散加载文件中,运用UNINIT来润饰一个履行节,能够防止__main对该区节的ZI数据进行零初始化。这是要解决非零初始化变量的要害。因而咱们能够界说一个UNINIT润饰的数据节,然后将期望非零初始化的变量放入这个区域中。所以,就有了榜首种办法:

1. 修正涣散加载文件,添加一个名为MYRAM的履行节,该履行节开端地址为0x1000A000,长度为0x2000字节(8KB),由UNINIT润饰:

1:LR_IROM10x000000000x00080000{;loadregionsize_region

2:ER_IROM10x000000000x00080000{;loadaddress=executionaddress

3:*.o(RESET,+First)

4:*(InRoot$$Sections)

5:.ANY(+RO)

6:}

7:RW_IRAM10x100000000x0000A000{;RWdata

8:.ANY(+RW+ZI)

9:}

10:MYRAM0x1000A000UNINIT0x00002000{

11:.ANY(NO_INIT)

12:}

13:}

那么,假设在程序中有一个数组,你不想让它复位后零初始化,就能够这样来界说变量:

unsignedcharplc_eu_backup[PLC_EU_BACKUP_BUF/8]__attribute__((at(0x1000A000)));

变量特点润饰符__attribute__((at(adder)))用来将变量强制定位到adder地点地址处。由于地址0x1000A000开端的8KB区域ZI变量不会被零初始化,所以处在这一区域的数组plc_eu_backup也就不会被零初始化了。

这种办法的缺陷是清楚明了的:要自己分配变量的地址,假设非零初始化数据比较多,这将是件不可思议的大工程(今后的保护、添加、修正代码等等)。所以要找到一种办法,让编译器去主动分配这一区域的变量。

2. 涣散加载文件同办法1,假设仍是界说一个数组,能够用下面办法:

unsignedcharplc_eu_backup[PLC_EU_BACKUP_BUF/8]__attribute__((section(“NO_INIT”),zero_init));

变量特点润饰符__attribute__((section(“name”),zero_init))用于将变量强制界说到name特点数据节中,zero_init表明将未初始化的变量放到ZI数据节中。由于“NO_INIT”这显性命名的自界说节,具有UNINIT特点。(强烈推荐最简略的办法)

3. 怎样将一个模块内的非初始化变量都非零初始化?

假设该模块姓名为test.c,修正涣散加载文件如下所示:

1:LR_IROM10x000000000x00080000{;loadregionsize_region

2:ER_IROM10x000000000x00080000{;loadaddress=executionaddress

3:*.o(RESET,+First)

4:*(InRoot$$Sections)

5:.ANY(+RO)

6:}

7:RW_IRAM10x100000000x0000A000{;RWdata

8:.ANY(+RW+ZI)

9:}

10:RW_IRAM20x1000A000UNINIT0x00002000{

11:test.o(+ZI)

12:}

13:}

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/346128.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部