以下流程主要取自RT-Rhread官方文档
RT-Thread启动流程
RT-Thread支持多种平台和多种编译器,而rtthread_startup()函数是RT-Thread规定的同意启动入口。一般执行顺序是:系统先从启动文件开始运行,然后进入RT-Thread的启动rtthread_startup(),最后进入用户入口main(),如下图所示:
以MDK-ARM为例,用户程序入口为main()函数,位于main.c文件中。系统启动后先从汇编代码startup_stm32f103xe.s开始运行,然后跳转到C代码,进行RT-Thread系统启动,最后进入用户程序入口main()。
为了在进入main()之前完成RT-Thread系统功能初始化,我们使用了MDK的扩展功能$Sub$和$Super$。可以给main添加$$Sub$$main,这个$$Sub$$main可以先调用一些要补充在main之前的功能函数(这里添加RT-Tread系统启动,进行系统一系列初始化),再调用$Super$$main转到main()函数执行,这样可以让用户不用去管main()之前的系统初始化操作。
RT-Thread程序内存分布
一般MCU包含的存储空间有:片内Flash与片内RAM,RAM相当于内存,Flash相当于硬盘。编译器会将一个程序分类为好几个部分,分别存储再MCU不同的存储区。
Keil工程再编译完之后,会有相应的程序所占用的空间提示信息,其中:
1)Code:代码段,存放程序的代码部分;
2)RO-data:只读数据段,存放程序中定义的常量;
3)RW-data:读写数据段,存放初始化为非0值的全局变量;
4)ZI-data:0数据段,存放未初始化的全局变量及初始化为0的变量;
编译完工程会生成一个.map的文件,该文件说明了各个函数占用的尺寸和地址,再文件的最后几行也说明了上面几个字段的关系:
1)RO Size包含了Code及RO-data,表示程序占用Flash空间的大小;
2)RW Size包含了RW-data及ZI-data,表示运行时占用的RAM的大小;
3)ROM Size包含了Code、RO Data已经RW Data,表示烧写程序所占用的Flash空间的大小;
程序运行之前,需要有文件实体被烧录到STM32的Flash中,一般是bin或者hex文件,该被烧录文件成为可执行映像文件。如下图所示,是可执行映像文件烧录到STM32后的内存分布,它包含RO段和RW段两个部分:其中RO段中保存了Code、RO-data的数据,RW段保存了RW-data的数据,由于ZI-data都是0,所以未包含再映像文件中。
STM32再上电之后默认从Flash启动,启动之后会将RW段中RW-data(初始化为全局变量)搬运到RAM中,但不会搬运RO段,即CPU的执行代码从Flash中读取,另外根据编译器给出的ZI地址和大小分配出ZI段,并将RAM区域清零。
其中动态内存堆为未使用的RAM空间,应用程序申请和释放的内存块来自该空间。
RT-Thread自动初始化机制
自动初始化机制是指初始化函数不需要被显式调用,只需要再函数定义处通过宏定义的方式进行申明,就会再系统启动过程中被执行。
再系统启动流程图中,有两个函数:rt_components_board_init()与rt_components_init(),气候的带底色方框内部的函数表示被自动初始化的函数,其中:
-
“board init functions” 为所有通过 INIT_BOARD_EXPORT(fn) 申明的初始化函数。
-
“pre-initialization functions” 为所有通过 INIT_PREV_EXPORT(fn)申明的初始化函数。
-
“device init functions” 为所有通过 INIT_DEVICE_EXPORT(fn) 申明的初始化函数。
-
“components init functions” 为所有通过 INIT_COMPONENT_EXPORT(fn)申明的初始化函数。
-
“enviroment init functions” 为所有通过 INIT_ENV_EXPORT(fn) 申明的初始化函数。
-
“application init functions” 为所有通过 INIT_APP_EXPORT(fn)申明的初始化函数。
rt_components_board_init()函数执行的比较早,主要初始化相关硬件环境,执行这个函数时将会遍历通过INIT_BORAD_EXPORT(fn)声明的初始化函数表,并调用各个函数 。
rt_components_init()函数会在操作系统运行起来之后创建的main线程里被调用执行,这个时候硬件环境和操作系统已经初始化完成,可以执行应用相关代码。rt_components_init()函数会遍历通过剩下的其他几个宏申明的初始化函数表。
RT-Thread的自动初始化机智使用了自定义RTI符号段,将需要在启动时进行初始化的函数指针放在该段中,形成一张初始化函数表,在系统启动过程中会遍历该表,并调用表中的函数,达到自动初始化的目的。
用来实现自动初始化功能的宏接口定义详细描述如下表所示:
初始化函数主动通过这些宏接口进行申明,如INIT_BOARD_EXPORT(rt_hw_usart_init),连接器会自动收集所有被申明的初始化函数,放在RTI符号段中,该符号段位于内存分布的RO段中,该RTI符号段中的所有函数在系统初始化时会被自动调用。