蒙蒙plus
蒙蒙plus
发布于 2024-11-03 / 14 阅读
0
0

rtthread启动流程

rt-thead启动流程

了解操作系统的启动流程能够更好的掌控我们的项目,根据需要二次开发。这里我们将循序渐进的梳理启动过程了解每个步骤都干了什么。

MCU启动流程,从Reset_Handler谈起

系统启动之前先有裸机的初始化过程

  1. startup.s
    1. Reset_Handler
    2. SystemInit
    3. __iar_program_start
  2. components.c
    1. __low_level_init
    2. __iar_data_init3
    3. rtthread_startup
    4. rt_hw_board_init
    5. rt_application_init
    6. rt_system_scheduler_start

rtthread系统自动初始化流程

RT-Thread 提供了一系列宏定义来支持不同阶段的自动初始化:

  1. rt_hw_board_init
    1. INIT_BOARD_EXPORT(fn): 用于硬件的初始化,此时调度器还未启动。
  2. main_thread_entry
    1. INIT_PREV_EXPORT(fn): 主要用于纯软件的初始化,没有太多依赖的函数。
    2. INIT_DEVICE_EXPORT(fn): 用于外设驱动初始化,例如网卡设备。
    3. INIT_COMPONENT_EXPORT(fn): 用于组件初始化,如文件系统或LWIP。
    4. INIT_ENV_EXPORT(fn): 用于系统环境初始化,例如挂载文件系统。
    5. INIT_APP_EXPORT(fn): 用于应用初始化,例如GUI应用。

实现原理

通过将指定数据划分到自定义的section中实现编译时所有函数指针在flash中分组,再通过特殊手段拿到这个section的起始与结束,通过下文的循环遍历执行即可。

    for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }

初看这些命名可能感觉比较魔幻不知所云,这里我们将拨开迷雾见明月,逐层分析了解这里的神奇设计是如何将分散在各个*.c初始化程序入口统一放到一起实现了类似动态数组的效果。

rtdef.h中的宏定义

查看头文件rtdef.h了解宏相关定义

查看 project.map了解INIT_EXPORT(fn, level)宏到底干了什么

//rtdef.h

#define INIT_EXPORT(fn, level)                                                       \
   rt_used const init_fn_t __rt_init_##fn rt_section(".rti_fn." level) = fn


/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn)           INIT_EXPORT(fn, "1")

/* init cpu, memory, interrupt-controller, bus... */
#define INIT_CORE_EXPORT(fn)            INIT_EXPORT(fn, "1.0")
/* init pci/pcie, usb platform driver... */
#define INIT_FRAMEWORK_EXPORT(fn)       INIT_EXPORT(fn, "1.1")
/* init platform, user code... */
#define INIT_PLATFORM_EXPORT(fn)        INIT_EXPORT(fn, "1.2")
/* init sys-timer, clk, pinctrl... */
#define INIT_SUBSYS_EARLY_EXPORT(fn)    INIT_EXPORT(fn, "1.3.0")
#define INIT_SUBSYS_EXPORT(fn)          INIT_EXPORT(fn, "1.3.1")
/* init early drivers */
#define INIT_DRIVER_EARLY_EXPORT(fn)    INIT_EXPORT(fn, "1.4")

/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initialization) */
#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")
/* application initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn)             INIT_EXPORT(fn, "6")

/* init after mount fs */
#define INIT_FS_EXPORT(fn)              INIT_EXPORT(fn, "6.0")
/* init in secondary_cpu_c_start */
#define INIT_SECONDARY_CPU_EXPORT(fn)   INIT_EXPORT(fn, "7")



//rtcompiler.h
#define rt_section(x)               @ x    //IAR 环境
#define rt_section(x)               __attribute__((section(x)))  #MDK环境

除了初始化过程,FinSH命令交互也使用了同样的技术

### section是什么?

这里需要补充编译器的链接相关知识

从存区功能划分

  1. 只读ROM

    • 非易失性存储器,掉电不丢失数据
    • flash eeprom
  2. 读写 RAM

    • 易失性存储器,掉电不丢失数据
    • DRAM SDRAM PRAM 等

从程序中划分

种类 描述
code 机器代码指令
RO-data 常量数据 只读数据
RW-data 初值非0 的全局变量
ZI-data 初值为0 的全局变量

在编译过程中,为方便管理又对不同的编辑结果进行分组

Section Kind 描述
.intvec ro code 程序向量表
.text ro code 代码指令默认**.text**
.rodata const 常量只读数据 RO-data
.data inited 初值非0 的全局变量
.bss zero 初值为0 的全局变量
CSTACK uninit 不初始化的全局变量
FSymTab const rtthread自定义的FinSH命令用到的
.rti_fn.0 const rtthread自定义自动初始化0组地址
.rti_fn.0.end const rtthread自定义自动初始化0组结束地址

再看__rt_init_rti_board_start__rt_init_rti_board_end

static int rti_board_start(void)
{
    return 0;
}
INIT_EXPORT(rti_board_start, "0.end");
//展开 可以看到放在了自定义的.rti_fn.0.end
rt_used const init_fn_t __rt_init_rti_board_start rt_section(".rti_fn." level) = rti_board_start


static int rti_board_end(void)
{
    return 0;
}
INIT_EXPORT(rti_board_end, "1.end");
//展开 可以看到放在了自定义的.rti_fn.1.end
rt_used const init_fn_t __rt_init_rti_board_end rt_section(".rti_fn." level) = rti_board_end

至此我们已经完全梳理出rtthread系统的自动初始化流程具体的工作细节了。

  1. 新定一个函数指针__rt_init_XXX,并将指针放到自定的section中
  2. 根据section根据命名自动排序规则创建一个特殊的函数rti_board_end,rti_end来划分每个section的结束地址
  3. 再在合适的位置循环遍历指定区间的函数即可完成自动初始化工作。

评论