Embedded job 11

Write a C program, review the concepts of global variables, local variables, heap and stack, and program and verify them in Ubuntu (x86) system and STM32(Keil) (STM32 sends the serial port printf information to the host computer serial port assistant through serial port).

1, Memory partition

1 . Program partition in memory

regionStorage contentcharacteristic
Stack areaLocal variable value, return value, parameterIt is automatically allocated and released by the compiler and has a "last in, first out" memory structure
HeapData of dynamically allocating memory during program operationHeap is located between BSS area and stack area in memory. It is generally allocated and released by programmers. If programmers do not release it, it will be recycled by the operating system at the end of the program
Global area (static area)Global / static variablesInitialized global variables and static variables are in one area (RW), uninitialized global variables and uninitialized static variables are in another adjacent area (ZI), and are released by the system after the program is completed
Constant areaGlobal / local variable valuesThe constant string is also placed here, and the exit program is automatically released by the system
Code areaBinary codeBinary code for storing function body

Memory storage order (from top to bottom): stack area - > heap area - > Global Area - > constant area - > code area

//The general storage sequence and contents are as follows:
#include <iostream>
using namespace std;
#include <string.h>

int a = 0;  //Global initialization area 
char *p1;  //Global uninitialized area 

int main(void)
{ 
    int b;  //Stack 
	char s[] = "abc";  //"abc" is in the constant area and s is on the stack. 
	char *p2;  //Stack 
	char *p3 = (char*)"123456";  //123456\0; In the constant area, p3 is on the stack. 
	static int c = 0;  //Global (static) initialization area 

	p1 = (char *)malloc(10); 
	p2 = (char *)malloc(20); //The allocated 10 and 20 byte areas are in the heap area.
 
	strcpy(p1, "123456");  //123456 \ 0 is placed in the constant area, and the compiler may optimize it in the same place as the "123456" pointed to by p3. 

	delete p1, p2;
	return 0;
} 

2. Global and local variables

**Global variables: * all externally defined variables of functions, whose scope is the whole program, that is, all source files, including. c and. h files
Local Variable: a variable defined inside the function body. The scope is limited to the function body. Leaving the function body will be invalid, and calling again will be an error
Global variables differ from local variables in that:
1. Different scopes 2 Differences in memory 3 Different existence time (life cycle)
4. Different ways of use

3 heap and stack

Both heap and stack allocate memory dynamically, and the space size of both is variable.

Heap:
The freely applied space grows from low to high according to the memory address, and its size is determined by the upper limit of system memory / virtual memory. It is slow, but has great freedom and large available space.
Stack:
Automatic Variables are stored and grow from high to low according to the memory address. Its maximum value is determined at compile time. It is fast, but has poor freedom and small maximum space.

The difference between the two
Application method of heap and stack:
Heap: automatically allocated by the system.
Stack: programmers need to apply and specify the size.

Application efficiency of heap and stack
Stack: automatically allocated by the system, fast. But programmers have no control.
Heap: it is the memory allocated by new. It is generally slow and easy to generate memory fragments, but it is the most convenient to use.

Heap and stack size
Heap: the space obtained by heap is flexible and large.
Stack: 2M or 1M, which is a constant determined at compile time. The space available from the stack is small.

Storage content in heap and stack
Stack: local variable. When a function is called, the first thing on the stack is the address of the next instruction (the next executable statement of the function call statement) in the main function, and then each parameter of the function.
Heap: generally, one byte is used at the head of the heap to store the size of the heap. The specific contents of the heap are arranged by the programmer.

4. Memory allocation of stm32

In an STM32 program code, stack area, heap area, global area (static area), constant area and code area are successively distributed from high memory address to low memory address, in which. bss segment is distributed in high address and. data segment is distributed in low address.
Memory high address:

stack area
Temporarily created local variables are stored in the stack area.
When a function is called, its entry parameters are stored in the stack area.
When the function returns, its return value is stored in the stack area.
The local variables defined by const are stored in the stack area.
heap
Heap area is used to store memory segments dynamically distributed during program operation, which can be increased or decreased.
malloc and other functions can be used to dynamically distribute memory.
The memory distributed by malloc function must be released with free, otherwise it will cause memory leakage.
Global area (static area)
The global area has bss segment and data segment, readable and writable.
① . bss segment
Uninitialized global variables are stored in bss segment.
Global variables initialized to 0 and static variables initialized to 0 are stored in bss segment.
. bss segment does not occupy executable file space, and its contents include operating system initialization.
② . data segment
The initialized global variables are stored in the data segment.
Static variables are stored in data segment.
. The data section occupies the executable file space, and its contents include program initialization.
The global variables defined by const are stored in rodata section.
Constant area
The string is stored in the constant area.
The contents of the constant area cannot be modified.
Memory low address

Code area
The sequence execution code is stored in the code area.
String constants may also be stored in the code area.

A program is essentially composed of bss segment, data segment and text segment. It can be combined with the following programs to help understand the storage location of each data

#include <stdio.h>
 
static unsigned int val1 = 2; //val1 is stored in data segment
unsigned int val2 = 2;        //The initialized global variables are stored in the data segment
unsigned int val3 ;           //Uninitialized global variables are stored in bss segment
const unsigned int val4 = 2;  //val4 is stored in rodata (read only data segment)
 
unsigned char Demo(unsigned int num)  // num is stored in the stack area
{
	char var = "123456";     // var is stored in the stack area, and "123456" is stored in the constant area
	unsigned int num1 = 2 ;  // num1 is stored in the stack area
	static unsigned int num2 = 1;    // num2 is stored in data segment
    const unsigned int num3 = 6;     //num3 is stored in the stack area
	void *p;
	p = malloc(8);          //p stored in the stack area
	free(p);
    return 1;
}
 
void main()
{
	unsigned int num = 0 ;
	num = Demo(num);        //The return value of Demo() function is stored in the stack area.
}

Memory management in STM32

The memory structure of STM32 includes RAM random access memory, ROM read only memory and Flash Memory.
**RAM (random access memory): * * the stored contents can be accessed by random reading and writing through instructions. The data stored in RAM will be lost during power down, so it can only be stored during startup and operation. RAM can be divided into two types: Dynamic RAM (DRAM) and Static RAM (SRAM).
**ROM (read only memory): * * can only read data from it, but can not write data arbitrarily. Compared with RAM, ROM has the disadvantage of slow reading and writing speed. However, because it has the advantage that the data can remain unchanged after power failure, it is often used to store one-time written programs and data. For example, the chip of the main BIOS program is ROM memory.
**Flash Memory: * * Flash Memory is developed later because ROM is not easy to change. Flash Memory not only has the characteristics of ROM power failure without losing data, but also can change the data when needed, but the price is higher than ROM.

Heap and stack in STM32

MCU is an integrated circuit chip, which integrates CPU, ram, ROM, various I/O ports, interrupt system, timer / counter and other functions. The CPU includes various bus circuits, calculation circuits, logic circuits and various registers. STM32 has general register R0 ‐ R15 and some special function registers, including stack pointer register. When STM32 runs the program normally and an interrupt comes, the CPU needs to stack the value in the register into RAM, and then store the address of the data in the stack register. When the interrupt processing is completed and exits, the data will be out of the stack to the previous register, which is automatically completed in C language.

Stack is often mentioned in programming. To be exact, it is an area in RAM. Let's start with a few instructions:
**(1) * * all contents in the program will only appear in flash and ram (without external expansion).
(2) The division of segments is to store similar data types in an area for easy management, but as mentioned above, the data of any segment is ultimately in flash and ram.

C language is divided into stack, heap, bss, data and code segments. Specifically, what data is stored in each segment? Baidu directly. Focus on the analysis of STM32 and the division of segments in MDK.

Code, RO data, RW data and Zi data under MDK:
***Code: * * * is the of storing program code.
***Ro data: * * * is the storage const constant and instruction.
***RW data: * * * is a global variable whose storage initialization value is not 0.
***Zi data: * * * is a global variable that stores uninitialized or initialized values of 0.

Flash=Code + RO Data + RW Data
RAM= RW-data+ZI-data
This is the size of each segment that can be obtained after MDK compilation, and the corresponding FLASH and RAM size can be obtained. However, there are two data segments that will also occupy RAM, but only when the program is running, that is, heap and stack. In the stm32 startup file s file, there are stack settings. In fact, the memory occupation of this stack starts from the address after RAM is allocated to RW data + Zi data.
**Heap: * * is the memory area where the compiler calls dynamic memory allocation.
**Stack: * * is where the local variables are when the program is running, so if the array of local variables is too large, it may cause stack overflow.
The size of the stack is not known after the compiler compiles, but only when it runs, so one thing to note is not to cause stack overflow... Or wait for hardfault to find you.

2, Output verification examples under Ubuntu and STM32 addresses

1. Output verification under Ubuntu

Open Ubuntu to create a project in the terminal using the following command
cd zuoye11 * * * create a new folder
Then in the current folder * * * GEDIT test c
In the new test Copy the following code in the C file:

#include <stdio.h>
#include <stdlib.h>
//Define global variables
int init_global_a = 1;
int uninit_global_a;
static int inits_global_b = 2;
static int uninits_global_b;
void output(int a)
{
	printf("hello");
	printf("%d",a);
	printf("\n");
}

int main( )
{   
	//Define local variables
	int a=2;
	static int inits_local_c=2, uninits_local_c;
    int init_local_d = 1;
    output(a);
    char *p;
    char str[10] = "lyy";
    //Define constant string
    char *var1 = "1234567890";
    char *var2 = "qwertyuiop";
    //Dynamic allocation
    int *p1=malloc(4);
    int *p2=malloc(4);
    //release
    free(p1);
    free(p2);
    printf("Stack area-Variable address\n");
    printf("                a : %p\n", &a);
    printf("                init_local_d: %p\n", &init_local_d);
    printf("                p: %p\n", &p);
    printf("              str: %p\n", str);
    printf("\n Heap area-Dynamic application address\n");
    printf("                   %p\n", p1);
    printf("                   %p\n", p2);
    printf("\n Global area-Global and static variables\n");
    printf("\n.bss paragraph\n");
    printf("Global external no initial value uninit_global_a: %p\n", &uninit_global_a);
    printf("Static external no initial value uninits_global_b: %p\n", &uninits_global_b);
    printf("Static internal no initial value uninits_local_c: %p\n", &uninits_local_c);
    printf("\n.data paragraph\n");
    printf("Global external initial value init_global_a: %p\n", &init_global_a);
    printf("Static external initial value inits_global_b: %p\n", &inits_global_b);
    printf("Static internal initial value inits_local_c: %p\n", &inits_local_c);
    printf("\n Text constant area\n");
    printf("Literal constant address     : %p\n",var1);
    printf("Literal constant address     : %p\n",var2);
    printf("\n Code area\n");
    printf("Program area address       : %p\n",&main);
    printf("Function address         : %p\n",&output);
    return 0;
}

Save and exit the file test C Documents
Use the following command to compile and execute test c

gcc test.c -o test
./test

The output results under Ubuntu are as follows:


It can be seen that the address value of Ubuntu in the stack area and heap area increases gradually from low address to high address

2. Output verification example under STM32 address

New construction on keil,
Here I use the previous project (serial communication): Embedded operation 6.2

, modify it based on the project in the blog.
In BSP_ usart. Add header file code to h file

#include <stdio.h>
#include <stdlib.h>

In BSP_ usart. Rewrite fputc function in C file

int fputc(int ch, FILE *f)
{
	USART_SendData(DEBUG_USARTx, (uint8_t)ch);
	while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
	return (ch);
}

main.c file is modified to the following code:

#include "stm32f10x.h"
#include "bsp_usart.h" / / add bsp_usart.h header file

int init_global_a = 1;
int uninit_global_a;
static int inits_global_b = 2;
static int uninits_global_b;

void output(int a)
{
	printf("hello");
	printf("%d",a);
	printf("\n");
}

int main(void)
{	
	//Define local variables
	int a=2;
	static int inits_local_c=2, uninits_local_c;
	int init_local_d = 1;
	char *p;
	char str[10] = "lyy";
	//Define constant string
	char *var1 = "1234567890";
	char *var2 = "qwertyuiop";
	//Dynamic allocation
	int *p1=malloc(4);
	int *p2=malloc(4);
	USART_Config();//Serial port initialization
	output(a);
	//release
	free(p1);
	free(p2);
	printf("Stack area-Variable address\n");
	printf("                a: %p\n", &a);
	printf("                init_local_d: %p\n", &init_local_d);
	printf("                p: %p\n", &p);
	printf("              str: %p\n", str);
	printf("\n Heap area-Dynamic application address\n");
	printf("                   %p\n", p1);
	printf("                   %p\n", p2);
	printf("\n Global area-Global and static variables\n");
	printf("\n.bss paragraph\n");
	printf("Global external no initial value uninit_global_a: %p\n", &uninit_global_a);
	printf("Static external no initial value uninits_global_b: %p\n", &uninits_global_b);
	printf("Static internal no initial value uninits_local_c: %p\n", &uninits_local_c);
	printf("\n.data paragraph\n");
	printf("Global external initial value init_global_a: %p\n", &init_global_a);
	printf("Static external initial value inits_global_b: %p\n", &inits_global_b);
	printf("Static internal initial value inits_local_c: %p\n", &inits_local_c);
	printf("\n Text constant area\n");
	printf("Literal constant address     : %p\n",var1);
	printf("Literal constant address     : %p\n",var2);
	printf("\n Code area\n");
	printf("Program area address       : %p\n",&main);
	printf("Function address         : %p\n",&output);
	return 0;
}

Burn the input chip after successful compilation
Note that when burning here, you still remember to set boot0 to 1 and boot1 to 0

After burning, unplug the USB (power off), and then set boot0 and boot1 to 0
After plugging in (power on), open the serial port debugging assistant. After opening the serial port, press the RESET key, and the display results are as follows:

It can be seen that the address value of STM32 in stack area and heap area increases gradually from low address to high address. It is the same as the address values of Ubuntu in stack and heap.

3, Summary

Through this experiment, I learned and became familiar with the allocation addresses of heap, stack, global, local and other variables in C program, and verified the program memory partition again by applying C program on Ubuntu and stm32 respectively. The allocation addresses of heap, stack, global and local variables in C programs under Ubuntu and stm32 are summarized and compared.
Through the experiment, I am familiar with the process of chip burning and serial port debugging again. I am not unfamiliar before, but there are still many deficiencies and need to learn more.

Keywords: Single-Chip Microcomputer

Added by georgepapa on Thu, 16 Dec 2021 17:08:54 +0200