//components.c 中定义
/* re-define main function */
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
1在这里 $Sub$$main
函数仅仅调用了 rtthread_startup()
函数。RT-Thread 支持多种平台和多种编译器,而 rtthread_startup()
函数是 RT-Thread 规定的统一入口点,所以 $Sub$$main
函数只需调用 rtthread_startup()
函数即可。
例如采用 GNU
GCC
编译器编译的 RT-Thread
,就是直接从汇编启动代码部分跳转到 rtthread_startup()
函数中,并开始第一个 C 代码的执行的。
//components.c 中定义
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initalization
* 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();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* 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-Thread 的设备框架做的初始化;
初始化各个应用线程,并启动调度器。
那么其他等级分别对应什么宏进行初始化的?,看下面的表格:
初始化顺序 | 宏接口 | 描述 |
---|---|---|
1 | INIT_BOARD_EXPORT(fn) | 非常早期的初始化,此时调度器还未启动 使用该宏后,fn 将属于 “board init functions” |
2 | INIT_PREV_EXPORT(fn) | 主要是用于纯软件的初始化、没有太多依赖的函数 使用该宏后,fn 将属于 “pre-initialization functions” |
3 | INIT_DEVICE_EXPORT(fn) | 外设驱动初始化相关,比如网卡设备 使用该宏后,fn 将属于 “device init functions” |
4 | INIT_COMPONENT_EXPORT(fn) | 组件初始化,比如文件系统或者 LWIP 使用该宏后,fn 将属于 “components init functions” |
5 | INIT_ENV_EXPORT(fn) | 系统环境初始化,比如挂载文件系统 使用该宏后,fn 将属于 “enviroment init functions” |
6 | INIT_APP_EXPORT(fn) | 应用初始化,比如 GUI 应用 使用该宏后,fn 将属于 “application init functions” |
查看源码,这 6 个宏定义如下:( 不同的段:1 2 3 4 5 6 )
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
INIT_EXPORT(fn, level) 表示这个函数 fn 现在属于哪个初始化 level 段, 由 SECTION(".rti_fn."level) 进行定义
#define INIT_EXPORT(fn, level)
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn
而 SECTION(x) 是:#define SECTION(x) __attribute__((section(x)))
__attribute__((section("name")))
:将作用的函数或数据放入指定名为"name"的输入段中。(在不同的编译器中实现的方式也有所不同。)
以上就是整个的宏定义。作用就是将函数 fn 的地址赋给一个 __rt_init_fn 的指针,然后放入相应 level 的数据段中。所以函数使用自动初始化宏导出后,这些数据段中就会存储指向函数的指针。
rt_components_board_init():for循环会遍历位于__rt_init_rti_board_start
到 __rt_init_rti_board_end
之间保存的函数指针,然后依次执行这些函数。
rt_components_init():for循环会遍历位于__rt_init_rti_board_end
到 __rt_init_rti_end
之间保存的函数指针,然后依次执行这些函数 。
段名 | 函数指针/宏 |
---|---|
.rti_fn.0 | __rt_init_rti_start |
.rti_fn.0.end | __rt_init_rti_board_start |
.rti_fn.1 | INIT_BOARD_EXPORT(fn) |
.rti_fn.1.end | __rt_init_rti_board_end |
.rti_fn.2 | INIT_PREV_EXPORT(fn) |
.rti_fn.3 | INIT_DEVICE_EXPORT(fn) |
.rti_fn.4 | INIT_COMPONENT_EXPORT(fn) |
.rti_fn.5 | INIT_ENV_EXPORT(fn) |
.rti_fn.6 | INIT_APP_EXPORT(fn) |
.rti_fn.6.end | __rt_init_rti_end |
2回到int rtthread_startup(void)中,改函数在启动流程中,调用了两个函数 rt_components_board_init() 与 rt_components_init() 就完成了6部分的初始化。
上面的启动代码基本上可以说都是和 RT-Thread 系统相关的,那么用户如何加入自己的应用程序的初始化代码呢?RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。
int main(void)
{
/* user app entry */
return 0;
}
注:为了在进入 main 程序之前,完成系统功能初始化,可以使用 $sub$$
和 $super$$
函数标识符在进入主程序之前调用另外一个例程,这样可以让用户不用去管 main() 之前的系统初始化操作。