This article refers to [Wildfire EmbedFire] "RT-Thread Kernel Implementation and Application Development - Based on STM32", which is only used as a personal study note. For more detailed content and steps, please check the original text (you can download it from the Wildfire Data Download Center)
Article directory
When you get a transplanted RT-Thread project, you go to the main function, you can only see the code for creating threads and starting threads, hardware initialization, system initialization, and starting the scheduler in the main function. not. That's because RT-Thread extends the main function and does all the work before the main function.
--original
Reset_Handler reset function
After the system is powered on, the first function is the reset function in the startup file. This startup file is written in assembly language. The reset function will call the C library function __main
. The main work of this function is to initialize the system's heap and stack, and finally call the main()
function to enter. The world of C language.
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
$Sub$$main function
The original text said that after the single-step execution
__main
, it will jump tocomponent.c
the$Sub$$main()
function in, but__main
after I single-stepped, I went directly to themain()
function (this may be because my operation is not in place).
In Keil's compiler, there exists a pair of symbols, and they can extend a function, such as using $Sub$$
can be executed before execution , and then called to switch into the function.$Super$$
$Sub$$main()
main()
$Sub$$main()
$Super$$main()
main()
Here are component.c
some of the functions in (for the Keil compiler)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rtthread_startup();
return 0;
}
/* the system main thread */
void main_thread_entry(void *parameter)
{
extern int main(void);
extern int $Super$$main(void);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif
/* invoke system main function */
$Super$$main(); /* for ARMCC. */
}
rtthread_startup() function
$Sub$$main()
There is only one function in the previous step function rtthread_startup()
, and all the initialization of RT-Thread is implemented in this function.
Among them, rt_hw_board_init()
this function has been touched many times and is defined in board.c
to initialize the underlying firmware.
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initialization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
rt_application_init() function
rt_application_init()
A thread is created in the function, which is an initial thread. When all application threads are successfully created, the initial thread closes itself. The last line of the function calls the rt_thread_startup()
function. Although we know that the function of this function is to start the thread, the thread is not started at this time, because the rtthread_startup()
last line of the upper-level function (see above) has rt_system_scheduler_start()
not yet been executed, and the system thread has not yet started scheduling.
void rt_application_init(void)
{
rt_thread_t tid;
#ifdef RT_USING_HEAP
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(tid != RT_NULL);
#else
rt_err_t result;
tid = &main_thread;
result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(result == RT_EOK);
/* if not define RT_USING_HEAP, using to eliminate the warning */
(void)result;
#endif
rt_thread_startup(tid);
}
main_thread_entry() function
The following is the entry function of the initial thread, the main function is to initialize the component, and finally call $Super$main()
back to main()
the function.
#ifndef RT_USING_HEAP
/* if there is not enable heap, we should use static thread and stack. */
ALIGN(8)
static rt_uint8_t main_stack[RT_MAIN_THREAD_STACK_SIZE];
struct rt_thread main_thread;
#endif
/* the system main thread */
void main_thread_entry(void *parameter)
{
extern int main(void);
extern int $Super$$main(void);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif
/* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
}
main() function
main()
The function is called through the initial thread. As mentioned earlier, the initial thread needs to be closed by itself later, which means that main()
after the function runs, the whole system is main()
irrelevant. This is very different from the bare metal system. main()
There is one function in the bare metal system, so the function does not stop running while(1)
after the microcontroller is powered on, while RT-Thread (and some other RTOS) does not contain an infinite loop, instead is the creation and initialization of a bunch of threads.main()
main()
At this point, the entire startup process of RT-Thread has basically been introduced. main()
How the system operates after the operation is completed will not be discussed here.