首页 > 技术知识 > 正文

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

开源RT线程RTOS自动初始化机制分析-freertos任务切换原理

添加自动初始化后导出的六个segments后,每个segments的分布如下表所示:

开源RT线程RTOS自动初始化机制分析-freertos任务切换原理1

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

开源RT线程RTOS自动初始化机制分析-freertos任务切换原理2

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

猜你喜欢