STM32 learning notes: how to initialize variables to non-zero after reset

Publisher:tau29Latest update time:2017-09-09 Source: eefocusKeywords:STM32 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

     Some products, when the system is reset (not power-on reset), may require to keep the data in RAM before reset, so as to quickly restore the site, or to avoid restarting the site equipment due to instant reset. However, by default, any form of reset in Keil MDK will clear the non-initialized variable data in RAM area. How to set non-initialized data variables not to be initialized by zero is what this article is going to discuss.

       Before giving the method, let's first understand the storage rules and properties of code and data, and why the RAM where non-initialized variables are located is initialized to zero by default after reset.

       What are initialized data variables and what are non-initialized data variables? (Because my text description may not be accurate, I like to give some examples to help understand the text.)

       Define a variable: int nTimerCount = 20; variable nTimerCount is an initialized variable, which means it has an initial value;

       If you define the variable: int nTimerCount; the variable nTimerCount is a non-assigned variable, and Keil MDK will put it in the input section with the attribute ZI by default.

       So, what is "ZI" and what is "input section"? This requires understanding the composition of ARM image files. This part is a bit boring, but I think it is very necessary to master it.

The composition of ARM image files:

  • An image file consists of one or more regions.

  • Each domain contains one or more output sections.

  • Each output section contains one or more input sections

  • Each input section contains the code and data in the target file.

       The input segment contains four types of content: code, initialized data, uninitialized storage area, and storage area with content initialized to zero. Each input segment has corresponding attributes: read-only (RO), readable and writable (RW), and initialized to zero (ZI).

       An output segment contains a series of input segments with the same RO, RW, and ZI attributes. The output segment attributes are the same as the input segment attributes contained in it.

       A domain contains one to three output segments, and the attributes of each output segment are different: RO attribute, RW attribute and ZI attribute

       At this point we can know that, in general, the code will be placed in the input section of the RO attribute, the initialized variables will be assigned to the RW attribute input area, and the "ZI" attribute input section can be understood as a collection of variables initialized to zero.

       Where will the initial value of the initialized variable be placed in the hardware? (For example, if we define int nTimerCount = 20, where will the initial value 20 be placed?) I think this is an interesting question. For example, after Keil is compiled, it will give information about the size of the compiled file, as shown below:

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)

       Many people don't know how this is calculated, nor do they know how much code is actually put into ROM/Flash. In fact, those initialized variables are put into the input section of the RW attribute, and the initial values ​​of these variables are put into ROM/Flash. Sometimes these initial values ​​are large, and Keil will compress them before putting them into ROM/Flash to save storage space. Who restores these initial values ​​to RAM and when? Who initializes the RAM where the variables in the ZI attribute input section are located with zeros and when? To understand these things, you need to see what Keil does for you under the default settings, from system reset to executing the main function you wrote in C code.

       After the hardware is reset, the first step is to execute the reset handler. The entry of this program is in the startup code (by default). Here is an excerpt of the reset processing entry code of cortex-m3:

   1: Reset_Handler PROC; PROC is equivalent to FUNCTION, indicating the beginning of a function, as opposed to ENDP?
   2:
   3: EXPORT Reset_Handler [WEAK]
   4: IMPORT SystemInit
   5: IMPORT __main
   6: LDR R0, =SystemInit
   7: BLX R0
   8: LDR R0, =__main
   9: BX R0
  10: ENDP

       After initializing the stack pointer and executing the user-defined low-level initialization code (SystemInit function), the next code calls the __main function, which calls a series of C library functions to complete the copying, decompression and zero initialization of code and data. Data decompression and copying include copying the initial values ​​of initialized variables stored in ROM/Flash to the corresponding RAM. For a variable, it may have three attributes. Variables modified with the const modifier are most likely to be placed in the RO attribute area, initialized variables will be placed in the RW attribute area, and the remaining variables will be placed in the ZI attribute area. By default, the zero initialization of ZI data will initialize all ZI data areas to zero. This is done by the compiler "on its own" before the program executes the main function of the C code after each reset. So if we want to set some variables in the C code not to be zero-initialized after reset, we must not let the compiler "do whatever it wants". We need to use some rules to constrain the compiler.

       Scatter-loading files are crucial for linkers. In scatter-loading files, using UNINIT to modify an execution section can prevent __main from zero-initializing the ZI data of the section. This is the key to solving non-zero initialized variables. Therefore, we can define a data section modified by UNINIT, and then put the variables that we want to initialize to non-zero in this area. So, we have the first method:

1. Modify the scatter-loading file and add an execution section named MYRAM. The execution section starts at 0x1000A000 and is 0x2000 bytes (8KB) ​​long. It is modified by UNINIT:

   1: LR_IROM1 0x00000000 0x00080000 { ; load region size_region
   2: ER_IROM1 0x00000000 0x00080000 { ; load address = execution address
   3: *.o (RESET, +First)
   4: *(InRoot$$Sections)
   5: .ANY (+RO)
   6: }
   7: RW_IRAM1 0x10000000 0x0000A000 { ; RW data
   8: .ANY (+RW +ZI)
   9:   }
  10: MYRAM 0x1000A000 UNINIT 0x00002000 {
  11: .ANY (NO_INIT)
  12: }
  13: }

So, if you have an array in your program that you don't want to be zero-initialized after reset, you can define the variable like this:

    
    unsigned char plc_eu_backup[PLC_EU_BACKUP_BUF/8] __attribute__((at(0x1000A000)));

       The variable attribute modifier __attribute__((at(adder))) is used to force the variable to be located at the address of adder. Since the 8KB area ZI variable starting at address 0x1000A000 will not be zero-initialized, the array plc_eu_backup in this area will not be zero-initialized either.

       The disadvantage of this method is obvious: you have to allocate the address of the variable yourself. If there is a lot of non-zero initialized data, it will be an unimaginable large project (future maintenance, addition, modification of code, etc.). So you have to find a way to let the compiler automatically allocate the variables in this area.

2. Scatter loading files is the same as method 1. If you still define an array, you can use the following method:

    unsigned char plc_eu_backup[PLC_EU_BACKUP_BUF/8] __attribute__((section("NO_INIT"),zero_init));

       The variable attribute modifier __attribute__((section("name"), zero_init)) is used to force the variable to be defined in the name attribute data section. Zero_init means that the uninitialized variable is placed in the ZI data section. Because the "NO_INIT" explicitly named custom section has the UNINIT attribute. (The simplest method is strongly recommended)

3. How to initialize all non-initialized variables in a module to non-zero?

If the module name is test.c, modify the scatter-loading file as follows:

   1: LR_IROM1 0x00000000 0x00080000 { ; load region size_region
   2: ER_IROM1 0x00000000 0x00080000 { ; load address = execution address
   3: *.o (RESET, +First)
   4: *(InRoot$$Sections)
   5: .ANY (+RO)
   6: }
   7: RW_IRAM1 0x10000000 0x0000A000 { ; RW data
   8: .ANY (+RW +ZI)
   9:   }
  10: RW_IRAM2 0x1000A000 UNINIT 0x00002000 {
  11: test.o (+ZI)
  12: }
  13: }

The following method is used when defining:

       int uTimerCount __attribute__((zero_init));

       Here, the variable attribute modifier __attribute__((zero_init)) is used to place uninitialized variables in the ZI data section. In fact, by default in Keil, uninitialized variables are placed in the ZI data area.


Keywords:STM32 Reference address:STM32 learning notes: how to initialize variables to non-zero after reset

Previous article:STM32 drives FM24CL16
Next article:STM32 study notes - serial port interrupt receiving indefinite data buff

Recommended ReadingLatest update time:2024-11-16 14:27

Problems encountered when porting cjson to stm32
1. The cJSON object root created does not have cJSON_Delete(root); the system crashes after executing it once 2. The string char *str obtained by cJSON parsing root, free(str), returns an error message after executing n times. After changing to myfree(str), it runs normally.
[Microcontroller]
STM32 hardware IIC and 51 analog IIC communication
IIC Introduction   The IIC protocol stipulates that the data transmitted on SDA must remain stable during the high level of SCL, and the data on SDA can only change during the low level of SCL. During IIC, data is placed on SDA at the rising edge of the pulse, and data is read from SAD at the falling edge of the pul
[Microcontroller]
STM32F4 study notes 5——The highest bit error of the data sent by the stm32 serial port
        Recently, I encountered a problem with the data transmission of the stm32 serial port when doing ModBus protocol communication based on the stm32f401 serial port. I spent a whole day looking for the problem, from the ModBus protocol format, scheduling algorithm to the serial port configuration, and finally sol
[Microcontroller]
STM32 MCU Learning (9) LCD1602 Display Output Experiment
The core code is provided by kingsraywii. The author of this article integrates it, annotates it in more detail, and improves some of the code. The chip ID acquisition, string output and printf redirection output functions are added. Note: When using the Puzhong Technology Development Board for testing, you need to un
[Microcontroller]
STM32 I2C AT24C02 driver
      If you have worked with 51's I2C, you will definitely feel that you have met it too late if you look at STM32's I2C driver. STM32 comes with its own I2C hardware module, and with ST's official library function, I2C can be used like a fire in STM32. This I2C driver here is very complete and can be used directly in
[Microcontroller]
Entering and waking up Stm32 standby mode
1. Basic Introduction 1-1: The "low power mode" of the microcontroller is like the standby mode of a mobile phone. It is different from the normal operating mode and is in a state of saving power and resources. 1-2: In operation, HCLK provides the clock for the CPU, and the cortex-m3 core executes the program code.
[Microcontroller]
Entering and waking up Stm32 standby mode
STM32 Bootloader and startup analysis
1. STM32 program download and Bootloader The three startup modes are as follows: (Screenshots and stm32 Chinese reference materials) 1. Boot from the main flash memory, that is, boot from the built-in Flash of STM32, BOOT0=0, BOOT1=X. Generally, when we use JTAG or SWD mode to download the program, it is downloaded
[Microcontroller]
STM32 Bootloader and startup analysis
STM32 timer output compare non-active mode
The STM32 timer can work in the output comparison inactive mode TIM_OCMode_Inactive. In this mode, when the timer count value reaches the comparison value, the pin level will be forced to be pulled low. However, when the pin corresponding to the timer channel is configured as GPIO_Mode_AF_PP, its default level is low.
[Microcontroller]
STM32 timer output compare non-active mode
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号