1. Stack memory
The stack storage area is a data storage area opened up by the user in the SRAM (or RAM) of the on-chip memory, and the size of the stack area can be arbitrarily specified according to the user's needs (as long as it does not exceed the size of the SRAM or RAM), and the location of the stack area is allocated by the compiler.
The stack pointer SP of the Cortex-M3/M4 processor is "decrement when full and increment when empty", showing the characteristic of reverse growth downward.
The storage characteristics of data in the stack area are "first in, last out, last in, first out".
This characteristic is determined by the way the stack pointer moves. The pointer value corresponding to the data pushed onto the stack first is relatively large, and the pointer value corresponding to the data pushed onto the stack later is relatively small. When popping the stack, the value of the stack pointer increases incrementally, so the data with a larger pointer value is popped last.
The role of the stack:
Storage of local variables, transfer of data between functions or subroutines when calling a function (saving of formal parameters, in fact, all local variable units defined in the called function will be released once the function call is completed), saving of field data when calling a function, and saving of field data when an interrupt occurs.
2. Function of startup code
The execution of a C program starts with the main function main().
But how does the microcontroller find the main() function after it is powered on?
Obviously, it is impossible for us to find the entry address of the main() function from the hardware.
In fact, the entry address of the main() function is assigned by the compiler during the compilation process.
In addition, the microcontroller has a startup process from the time it is powered on to the execution of the main() function. This startup process is exactly the process of executing the startup code, and it takes a very short time.
The role of the startup code:
1) Initialize the stack pointer SP == _initial_sp;
2) Initial PC pointer == Reset_Handler (reset handler);
3) Initialize the interrupt vector table;
4) Configure the system clock;
5) Call the C library _main() function to initialize the user stack, and finally call the main() main function to go to the C world.
3. Write startup code
Generally, standard CMSIS startup codes are in assembly language. Before using C to write startup code, we need to first consider whether C can be used to write startup code?
a) Correctly place the exception & interrupt vector table in the storage space;
b) Scatter loading & C Library initialization (call __main function);
c) Stack initialization.
The function a) is implemented in the standard assembly startup code by declaring a data segment (that is, the assembly instruction DCD xxx). We can use an array to do this in C. The key is to place it in the specified position, which requires scattered loading. Therefore, a) should be achievable;
b) is the easiest to implement, just call __main directly;
c) Although you can also know how the stack is initialized by reading the standard CMSIS assembly startup code.
Taking LPC54608 as an example, early chips had different processing methods. For example, the STM32F103 series that everyone is familiar with requires the startup code and scattered loading to be completed.
3.1. Exception & interrupt vector table
We said before that this can be achieved with an array, but an ordinary array certainly cannot, because the essence of these vectors is the entry point of the interrupt service function, that is, the "function pointer", so this array must be a function pointer array:
So we first declare a function pointer type:
typedef void ( *__vector )( void );
Then define a function pointer array named __vector_table:
__vector __vector_table[] = {};
This array is easy to make, but the most important thing is the placement. We must ensure that the vector table is placed at the 0x00000000 address of the Flash (the default vector table address in the LPC54608 chip). This is the key to the problem.
C and assembly cannot solve this problem. The linker in the compiler development environment is responsible for placing the program in the storage space, and the linker is guided by the scattered loading script file, which requires the intervention of scattered loading.
For example, among the various development environments commonly used to develop MCUs (Keil, IAR, Eclipse, etc.), the one that really plays a compiling role is the compiling kernel. Keil's compiling kernel is ARMCC (sometimes written as CCARM), IAR's compiling kernel is ICCARM, and Eclipse is generally GCC. The compiling kernel is actually divided into four functional modules:
Preprocessor, Compiler, Assembler, Linker. The preprocessor is responsible for combining various header files with the corresponding C files, deleting comments, expanding macro definitions, etc.
The compiler is responsible for converting C language into the corresponding assembly language;
The assembler is responsible for converting the assembly language into a relocatable target file *.o, which is already a binary file;
The linker is responsible for linking all .o files into a whole and allocating real physical addresses (which is the scattered loading mentioned repeatedly above).
How to arrange the exception & interrupt vector table?
Here we take Keil MDK as an example (IAR and GCC have different syntax in this part, but the principle is the same). There is a special description script file to guide how the linker works. In Keil, this file has the extension .sct/.scf. Here you can find the scatter loading description file:
Here is a direct list of the scatter-loading files I wrote for my own startup code:
load_rom 0x00000000 0x00080000
{
vector_rom 0x00000000 0x400
{
*( vector_table, +first)
}
execute_rom 0x400 FIXED 0x0007FC00
{
*( InRoot$$Sections )
.any( +ro )
}
execute_data 0x20000000 0x00010000
{
.any ( +rw +zi )
}
ARM_LIB_HEAP +0 empty 0x400 {}
ARM_LIB_STACK 0x20020000 empty -400 {}
}
The +first in *( vector_table, +first) is used to ensure that the necklace table is placed at the front of the Flash address. It should be noted here that the language in the scattered loading file is a script language, which is neither C nor assembly. This file cannot be compiled or debugged. If it is written incorrectly, it can only be modified through experience and cannot be debugged.
If we want to place an array, we need to declare a data segment Symbol, and then use scatter loading to specify the placement of this data segment Symbol, which is +first. So we need to first let this array generate a segment:
const __vector __vector_table[] __attribute__( ( section ( "vector_table" ), used ) ) = {};
So we use __attribute__ to achieve it.
We complete the stack specification in scatter loading, namely:
ARM_LIB_HEAP +0 empty 0x400 {}
ARM_LIB_STACK 0x20020000 empty -400 {}
These two sentences mean declaring a heap of size 0x400 and a stack of size 0x400. Note that the heap and the stack are different. Simply put, function calls will occupy stack space, and using functions such as malloc will generate heap allocation. For specific details, please go to Baidu.
*( InRoot$$Sections )
This part specifies the placement of the C Library and the scattered loading code;
In summary, this scatter-loading file ultimately places the code in memory space in a form similar to the following:
The __attribute__ keyword is used to specify the attributes of symbols such as functions and variables. It is a C language keyword that can be recognized by the compiler. Of course, this depends on what compiler is used. Different C compilers have slightly different syntax. Some C compilers do not even support the __attribute__ keyword. The following is the functions that can be achieved by the __attribute__ keyword in Keil's help document:
__attribute__( ( section ( "vector_table" ), used ) ), where "section("vector_table")" means placing a section named vector_table in the elf (relocatable object file) file. The used keyword specifies to the compiler that the variable should be kept as a static type in the OBJ file.
Combined with the +first attribute of *( vector_table, +first) of the scattered loading, we have successfully defined a vector data segment named vector_table that is placed at the front address 0x00000000 of the Flash and will not be optimized away by the compiler, and the value will not be changed.
This is a special stack label recognized by the compiler, which can be used in scatter loading, assembly or C language. The main stack pointer MSP is placed at address 0x00000000 (the first address of the vector table), and the PC pointer is placed at address 0x00000004, so we first import this label:
0x00000004 places the PC pointer, we need to put the reset vector here:
3.2, __reset_handler reset function
This is relatively simple. The most important thing is to call __main. Some initializations should be placed before __main. The FPU of LPC54608 and some SRAM switches should be placed before __main.
First import the __main function:
extern void __main( void );
Then comes the function body of __reset_handle:
void __reset_handler ( void )
{
#if ( ( __FPU_PRESENT == 1 ) && ( __FPU_USED == 1 ) )
SCB->CPACR |= ( ( 3UL << 10 * 2 ) | ( 3UL << 11 * 2 ) ); // set CP10, CP11 Full Access
#endif
SCB->VTOR = ( uint32_t ) 0x00000000;
SYSCON->ARMTRACECLKDIV = 0;
SYSCON->AHBCLKCTRLSET[ 0 ] = SYSCON_AHBCLKCTRL_SRAM1_MASK | SYSCON_AHBCLKCTRL_SRAM2_MASK | SYSCON_AHBCLKCTRL_SRAM3_MASK;
__main();
}
Because it is in C language, it will be easier to add the function body you need before __main.
3.3. Default exception & interrupt handling function
Take one of the vectors as an example: (USB0 interrupt)
Function body:
void usb0_irqhandler ( void )
{
while( 1 );
}
Then declare it as a "weak function" and still use __attribute__ to specify its attribute as "weak":
extern void usb0_irqhandler ( void ) __attribute__( ( weak ) );
If you do not write an interrupt service function, but the program enters this interrupt, the program will enter the default interrupt service function. If you write one yourself, your own interrupt service function will overwrite the function declared as "weak", of course, the premise is that the function name is the same.
Previous article:STM32 byte alignment #pragma pack
Next article:Detailed explanation of STM32 startup code assembly instructions
Recommended ReadingLatest update time:2024-11-23 04:46
- Popular Resources
- Popular amplifiers
- Naxin Micro and Xinxian jointly launched the NS800RT series of real-time control MCUs
- How to learn embedded systems based on ARM platform
- Summary of jffs2_scan_eraseblock issues
- Application of SPCOMM Control in Serial Communication of Delphi7.0
- Using TComm component to realize serial communication in Delphi environment
- Bar chart code for embedded development practices
- Embedded Development Learning (10)
- Embedded Development Learning (8)
- Embedded Development Learning (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- STMicroelectronics discloses its 2027-2028 financial model and path to achieve its 2030 goals
- 2024 China Automotive Charging and Battery Swapping Ecosystem Conference held in Taiyuan
- State-owned enterprises team up to invest in solid-state battery giant
- The evolution of electronic and electrical architecture is accelerating
- The first! National Automotive Chip Quality Inspection Center established
- BYD releases self-developed automotive chip using 4nm process, with a running score of up to 1.15 million
- GEODNET launches GEO-PULSE, a car GPS navigation device
- Should Chinese car companies develop their own high-computing chips?
- Infineon and Siemens combine embedded automotive software platform with microcontrollers to provide the necessary functions for next-generation SDVs
- Continental launches invisible biometric sensor display to monitor passengers' vital signs
- FPGA Design Code Cleanliness 2
- Quantifying video game time helps teenagers grow up healthily
- Overview of multi-string battery protection chip manufacturers
- EEWORLD University ----TI Smart Door Lock Solution
- CRT: What can save you?
- Confused about measuring tantalum capacitors with a digital multimeter?
- Calculation of wireless communication distance
- GPRS series application 12
- This year, friends in the RF field, please come and discuss.
- 【National Technology N32G430】Review of lighting up WS2812 lamp