RT-Thread是一个开源的嵌入式实时操作系统,具有丰富的中间层组件和出色的硬件和软件生态系统,所有这些都为物联网行业提供了出色的支持。自 2006 年成立以来,RT-Thread 已为 1 亿台设备提供支持,包括可穿戴设备、智能家电、汽车电子、医疗电子、消费电子、能源和许多其他行业。
常规初始化
在嵌入式开发过程中,我们主要采用这种方法来初始化外设。
int main(int argc,char*argv[]){ clk_init(); led_init(); beep_init(); key_init();…..while(1){…}}这种初始化的顺序相对清晰,很容易弄清楚已经初始化的外设以及它们的初始化顺序。但是,该功能特别麻烦,尤其是当需要初始化的外围设备很多时。main
自动初始化
在计算机上对 C 进行编程以打印hello world
#include<stdio.h>int main(int argc,char*argv[]){ printf(“hello world\r\n”);return1;}在这里,我们可以直接用于打印,无需任何初始化步骤;这个想法导致了自动初始化机制。printfRT-Thread
RT-Thread自动初始化: int led_init(){…}INIT_APP_EXPORT(led_init);int main(int argc,char*argv[]){ led_on(); rt_kprintf(“hello rt thread\r\n”);return1;}自动初始化的中心思想是,在执行函数之前,每个外设的初始化都已完成,所有外设都可以直接在函数中使用。例如,上面的程序直接用于输出并点亮main main rt_kprintfLED
自动初始化的接口
从源代码中截获的自动初始化,如下所示:API RT-Thread
/* 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功能
从初始化函数来看,我们知道它们的最终调用是函数。只有输入的参数不同。我们来看看这个函数的定义:INIT_EXPORT
#define INIT_EXPORT(fn, level) \ RT_USED constinit_fn_t __rt_init_##fn SECTION(“.rti_fn.” level) = fn该函数有两个参数;第一个参数指示需要初始化哪个函数,传递函数指针(函数名称),第二个参数指示要将函数指针放入哪个段。接下来让我们进入Macro,在我们跳转到Macro之前,需要了解几个先决条件INIT_EXPORT
RT_USEDdefine RT_USED属性(已使用)已在目标文件中标记该函数,以防止链接器删除未使用的部分。attribute__(used)
init_fn_t类型typedef int (*init_fn_t)(void);此处,返回值 定义为具有函数参数的函数指针类型,并重命名intvoidinit_fn_t
####属于C语言,其作用是将两种语言符号组合成一个语言符号
SECTIONdefine SECTION(x) 属性((section(x)))__attribute__((section(name)))将功能函数或数据放入指定为的输入段中name
通过上述初步备份,让我们分析以下Macro。将Macro展开为如下所示:INIT_EXPORT
RT_USED constinit_fn_t __rt_init_fn SECTION(“.rti_fn.” level)= fn这个Macro的功能是将指向函数的指针分配给变量,这个变量类型是,并且存储在指定的段中。因此,在使用自动初始化Macro导出函数后,指向每个初始化函数的指针将存储在这些数据段中。当我们取消引用时,这些指针将在我们执行相应的函数时被采用。fn__rt_init_fnRT_USED const
init_fn_t.rti_fn.level
Segments划分
源代码如下:component.c
staticint rti_start(void){return0;}INIT_EXPORT(rti_start,“0”);staticint rti_board_start(void){return0;}INIT_EXPORT(rti_board_start,“0.end”);staticint rti_board_end(void){return0;}INIT_EXPORT(rti_board_end,“1.end”);staticint rti_end(void){return0;}INIT_EXPORT(rti_end,“6.end”);使用Macro导出的上述segments的分布如下表所示:INIT_EXPORT
添加自动初始化后导出的六个segments后,每个segments的分布如下表所示:
rt_components_board_init功能
前往检查函数的实现:rt_components_board_init
void rt_components_board_init(void){#if RT_DEBUG_INITint result;conststruct rt_init_desc *desc;for(desc =&__rt_init_desc_rti_board_start; desc <&__rt_init_desc_rti_board_end; desc ++){ rt_kprintf(“initialize %s”, desc->fn_name); result = desc->fn(); rt_kprintf(“:%d done\n”, result);}#elsevolatileconstinit_fn_t*fn_ptr;for(fn_ptr =&__rt_init_rti_board_start; fn_ptr <&__rt_init_rti_board_end; fn_ptr++){(*fn_ptr)();}#endif}如果没有,请考虑 ,很明显地发现 正在执行以下内容:RT_DEBUG_INITrt_components_board_init
volatileconstinit_fn_t*fn_ptr;for(fn_ptr =&__rt_init_rti_board_start; fn_ptr <&__rt_init_rti_board_end; fn_ptr++){(*fn_ptr)();}上面的代码定义了一个指针,当指针的范围在 and 的范围内时,该指针被取消引用,其中指针是自动初始化时放置的函数指针,因此它是函数的执行。也就是说,执行导出的函数。
fn_ptr__rt_init_rti_board_startrt_init_rti_board_endINIT_BOARD_EXPORT(fn)rt_components_init功能
源代码:
void rt_components_init(void){#if RT_DEBUG_INITint result;conststruct rt_init_desc *desc; rt_kprintf(“do components initialization.\n”);for(desc =&__rt_init_desc_rti_board_end; desc <&__rt_init_desc_rti_end; desc ++){ rt_kprintf(“initialize %s”, desc->fn_name); result = desc->fn(); rt_kprintf(“:%d done\n”, result);}#elsevolatileconstinit_fn_t*fn_ptr;for(fn_ptr =&__rt_init_rti_board_end; fn_ptr <&__rt_init_rti_end; fn_ptr ++){(*fn_ptr)();}#endif}如果没有,请考虑 ,很明显地发现 正在执行以下内容:RT_DEBUG_INITrt_components_init
执行自动初始化函数
启动过程:RT-Thread
Sample
将以下测试代码添加到函数中:main.c
int led_init(void){return1;}INIT_APP_EXPORT(led_init);编译后的文件如下所示:.map
函数指针位于segments中,函数在执行时会取消引用该指针,即执行函数
__rt_init_led_init.rti_fn.6rt_components_init()led_init免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:开源RT线程RTOS自动初始化机制分析-freertos任务切换原理 https://www.yhzz.com.cn/a/9092.html