In Linux, there are many programming ideas worth learning. Many technical experts have applied these ideas and mechanisms to microcontroller programming, especially simulating the automatic initialization process of the Linux kernel in STM32.
Generally speaking, we will follow certain routines when writing programs. We will execute functions one after another according to sequential logic.
If the logic is very complex and involves many modules, then the code executed sequentially will be bloated and the modules will be very tightly coupled. There are various peripheral drivers in the Linux kernel, and it is almost impossible to execute them logically in a sequence.
The kenrel code can have such a large amount of code, it is large but not messy, it effectively separates each level and each module, and a large amount of code is logically organized together, and this initcall plays a vital role .
By imitating this method, we finally clear the main function code in the picture, separate this logic, and achieve the same function.
How to implement such a function requires some background knowledge:
1, Organization of program code
2, knowledge related to link scripts.
3, Application of function pointers.
The organization of the code, such as the picture, you need to know the variables a, b and function pointer f, f2 is stored in which sections of the program, you can take a look at this stm32 startup code implementation | C language, the above a , f are stored in the bss segment, b, f2 are stored in the data segment, because the initial value has been given, and implementing this intcall will put the data that needs to be automatically initialized into a custom segment, such as .initcall.
How to put it in a specific section, you need to use the attribute((section)) keyword to change the data storage section.
The current program is compiled using these segments. Except for .isr_vector, which is also added, the others are the default ones of the compiler.
Add a piece of code first:
Of course this is not enough, you also need to tell the linker (LD) to link the .initcall section into the program, so this section also needs to be modified.
This section is aligned by 8 bytes, defines two global variables, and links these data in order 0-5. After these two modifications, let's take a look at each section of the program.
As shown in the picture:
There is an extra red frame for the .initcalls section. This section is 8 bytes in total, starting from 0x80005a8.
Let’s take a look at the specific situation of this section, using the readelf tool.
matches the size tool above, and the address of the green box is SystemInit (0x08000231, little endian mode.)
So through attribute and modifying the link script, the function pointer variable is placed in the .initcall section.
So how to call this function is similar to the previous initialization data segment data. It traverses this segment, then takes out the function address, and then forcibly converts the address in the segment into a function pointer, and then calls it directly.
The picture implemented is to take the function address from the .initcall section and then call it directly. It is very easy to confuse the address of the function with the address of the function pointer variable.
With the code modified like this, the automatic initialization function can indeed be adjusted, but every time I have to write such a long section of static initcall_t __ attribute__(( __ used__,__ section__(“.initcall.0.init”))) , it’s just uncomfortable. It can be modified through macros in the Linux kernel.
The same goes for this one.
Add some macros that are executed according to the logical sequence of the program
0, low_level_init For example, initialize the system basic clock
1, arch_init For example, put some initialization of CPU architecture such as initializing NVIC.
2, dev_init initializes peripheral modules, such as i2c, flash, spi, etc.
3, board_init makes some settings for specific hardware boards.
4, os_init Some settings of the operating system, such as file system, network protocol stack, etc.
5, app_init finally runs the user program.
Modify your own program and use macros instead. In this way, the call to do_initcalls will be executed in the order of 0, 1-to 5.
Finally let’s take a look at the initcall section:
In this way, just add something like dev_init(), app_init() to the automatic initialization function, and it will be automatically called without the need for a main function. A sequence of execution.
For example, the initialization of i2c control is placed in dev_init. There are many i2c slave devices hanging below. You only need to initialize each slave device with app_init. Even if a new one comes, just use this app_init to initialize it. No need. Change the original, highly separated degree of coupling between modules.
This simulates Linux kenerl initialization and verification is successful, and finally uploaded to the library.
The above is the detailed content of Simulate Linux automatic initialization process on STM32. For more information, please follow other related articles on the PHP Chinese website!