ESP32移植rt-thread自动初始化功能源码分享
ESP32移植rt-thread自动初始化功能源码分享
- 一、效果展示
- 二、源码分享
-
- 1、工程结构
- 2、auto_init.h
- 2、auto_init.c
- 4、main.c
- 5、autoInit.lf
- 5、完整工程下载
- 三、自动初始化介绍
-
- 1、核心概念:自动初始化
- 2、实现原理:段表 (Section Table) 与宏定义
- 3、使用示例
- 4、优势
- 5、注意事项
- 6、总结
一、效果展示



二、源码分享
1、工程结构

2、auto_init.h
#ifndef RTT_AUTO_INIT_H
#define RTT_AUTO_INIT_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int (*init_fn_t)(void);
#define section(x) __attribute__((section(x)))
#define used __attribute__((used))
#define INIT_EXPORT(fn, level) used const init_fn_t __rt_init_##fn section("auto_init_fn." level) = fn
/* 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 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")
// 自动初始化执行函数
void autoinit_execute(void);
#ifdef __cplusplus
}
#endif
#endif /* RTT_AUTO_INIT_H */
2、auto_init.c
/*************************************************
* @copyright:
* @author:Xupeng
* @date:2022-11-03
* @description:
**************************************************/
#include "auto_init.h"
/*
* Components Initialization will initialize some driver and components as following
* order:
* rti_start --> 0
* BOARD_EXPORT --> 1
* rti_board_end --> 1.end
*
* DEVICE_EXPORT --> 2
* COMPONENT_EXPORT --> 3
* FS_EXPORT --> 4
* ENV_EXPORT --> 5
* APP_EXPORT --> 6
*
* rti_end --> 6.end
*
* These automatically initialization, the driver or component initial function must
* be defined with:
* INIT_BOARD_EXPORT(fn);
* INIT_DEVICE_EXPORT(fn);
* ...
* INIT_APP_EXPORT(fn);
* etc.
*/
static int auto_init_start(void)
{
return 0;
}
INIT_EXPORT(auto_init_start, "0");
static int auto_init_board_start(void)
{
return 0;
}
INIT_EXPORT(auto_init_board_start, "0.end");
static int auto_init_board_end(void)
{
return 0;
}
INIT_EXPORT(auto_init_board_end, "1.end");
static int auto_init_end(void)
{
return 0;
}
INIT_EXPORT(auto_init_end, "6.end");
/*************************************************
* @function:static void components_board_init(void)
* @description:组件板级初始化
* @calls:
* @input:
* @return:
* @others:
*************************************************/
static void components_board_init(void)
{
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_auto_init_board_start; fn_ptr < &__rt_init_auto_init_board_end; fn_ptr++)
{
(*fn_ptr)();
}
}
/*************************************************
* @function:void components_init(void)
* @description:组件初始化
* @calls:
* @input:
* @return:
* @others:
*************************************************/
void components_init(void)
{
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_auto_init_board_end; fn_ptr < &__rt_init_auto_init_end; fn_ptr ++)
{
(*fn_ptr)();
}
}
/*************************************************
* @function:void autoinit_execute(void)
* @description:自动初始化执行
* @calls:
* @input:
* @return:
* @others:
*************************************************/
void autoinit_execute(void)
{
components_board_init();
components_init();
}
4、main.c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "shell_port.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "auto_init.h"
#include "esp_log.h"
// 板级初始化 (优先级1)
static int board_init_test(void)
{
printf("board_init_testn");
return 0;
}
INIT_BOARD_EXPORT(board_init_test);
static int prev_init_test(void)
{
printf("prev_init_testn");
return 0;
}
INIT_PREV_EXPORT(prev_init_test);
// 设备初始化 (优先级3)
static int device_init_test(void)
{
printf("device_init_testn");
return 0;
}
INIT_DEVICE_EXPORT(device_init_test);
static int component_init_test(void)
{
printf("component_init_testn");
return 0;
}
INIT_COMPONENT_EXPORT(component_init_test);
static int env_init_test(void)
{
printf("env_init_testn");
return 0;
}
INIT_ENV_EXPORT(env_init_test);
// 应用初始化 (优先级6)
static int app_init_test(void)
{
printf("app_init_testn");
return 0;
}
INIT_APP_EXPORT(app_init_test);
void app_main(void)
{
autoinit_execute();
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
5、autoInit.lf
[sections:auto_init_fn]
entries:
auto_init_fn+
[scheme:auto_init]
entries:
auto_init_fn -> flash_rodata
[mapping:auto]
archive: *
entries:
* (auto_init);
auto_init_fn -> flash_rodata KEEP() SORT() SURROUND(_auto_init_fn)
5、完整工程下载
文章顶部下载
三、自动初始化介绍
1、核心概念:自动初始化
自动初始化是 RT-Thread 内核提供的一种机制,它允许开发者将需要在系统启动时执行的初始化函数(如设备驱动初始化、文件系统挂载、网络接口配置等)声明为特定的类型。然后,RT-Thread 的内核启动代码会在进入主线程 main() 函数之前,自动地、按照预定义的顺序调用所有这些被声明的初始化函数。这极大地简化了系统的启动流程,避免了手动管理大量初始化代码的繁琐和潜在错误。
2、实现原理:段表 (Section Table) 与宏定义
RT-Thread 的自动初始化功能主要依赖于编译器的 段 (Section) 特性和一组特殊的 宏 (Macro)。
-
函数声明宏:
RT-Thread 定义了一系列宏,用于将函数标记为不同初始化级别的初始化函数。常用的宏包括:-
INIT_BOARD_EXPORT(fn): 标记板级初始化函数,最先执行。通常用于非常基础的硬件初始化。 -
INIT_DEVICE_EXPORT(fn): 标记设备驱动初始化函数,在板级初始化之后执行。 -
INIT_COMPONENT_EXPORT(fn): 标记组件初始化函数,在设备初始化之后执行。 -
INIT_ENV_EXPORT(fn): 标记环境初始化函数(如挂载文件系统),在组件初始化之后执行。 -
INIT_APP_EXPORT(fn): 标记应用初始化函数,最后执行。通常用于启动应用程序任务或进行最终配置。
这些宏的本质是将函数指针放置到链接器脚本中定义的特定内存段(Section)里。例如,INIT_DEVICE_EXPORT宏可能会将函数指针放在一个名为.rti_fn.1的段中。
-
-
段表与初始化顺序:
在 RT-Thread 的链接器脚本 (通常是linker_scripts/目录下的文件) 中,会定义一系列连续的段,例如:.rti_fn.0 : { ... } /* 对应 INIT_BOARD_EXPORT */ .rti_fn.1 : { ... } /* 对应 INIT_DEVICE_EXPORT */ .rti_fn.2 : { ... } /* 对应 INIT_COMPONENT_EXPORT */ .rti_fn.3 : { ... } /* 对应 INIT_ENV_EXPORT */ .rti_fn.4 : { ... } /* 对应 INIT_APP_EXPORT */这些段按照数字顺序
.rti_fn.0, .rti_fn.1, ..., .rti_fn.4在内存中连续排列。数字较小的段地址较低。

-
启动过程:
在 RT-Thread 内核的启动代码中(通常是components.c中的rtthread_startup()函数),会执行一个关键步骤rt_components_board_init()和rt_components_init()。这些函数的核心逻辑是:- 定位到
.rti_fn段表的起始地址(例如__rt_init_rti_board_start)。 - 遍历
.rti_fn.0到.rti_fn.4这些连续的段。 - 对于每个段,遍历段内存储的每一个函数指针。
- 依次调用这些函数指针指向的初始化函数。
由于段是按数字顺序排列的,函数调用的顺序就严格按照INIT_BOARD_EXPORT->INIT_DEVICE_EXPORT->INIT_COMPONENT_EXPORT->INIT_ENV_EXPORT->INIT_APP_EXPORT的顺序执行。
- 定位到
3、使用示例
开发者只需在自己的初始化函数(通常是无参数、无返回值的函数)前面加上对应的宏即可。例如,初始化一个串口设备驱动:
static int uart_init(void)
{
/* 串口硬件初始化配置 */
return 0;
}
INIT_DEVICE_EXPORT(uart_init); // 将该函数声明为设备初始化函数
初始化一个文件系统并挂载到 Flash 分区:
static int filesystem_init(void)
{
/* 挂载文件系统到 / 根目录 */
return 0;
}
INIT_ENV_EXPORT(filesystem_init); // 将该函数声明为环境初始化函数
启动一个应用线程:
static void app_thread_entry(void *parameter)
{
while (1) {
/* 应用线程工作 */
}
}
static int app_start(void)
{
rt_thread_t tid;
tid = rt_thread_create("app", app_thread_entry, RT_NULL, 2048, 25, 10);
if (tid != RT_NULL) {
rt_thread_startup(tid);
}
return 0;
}
INIT_APP_EXPORT(app_start); // 将该函数声明为应用初始化函数
4、优势
-
简化启动流程: 开发者无需在
main()函数中手动调用一堆init_xxx()函数。 - 模块化: 初始化代码与模块代码紧密结合,提高了代码的内聚性。
- 顺序可控: 通过不同的宏明确指定初始化顺序,避免依赖问题。
- 可扩展: 新增模块只需添加自己的初始化函数和宏,无需修改主启动代码。
5、注意事项
-
函数类型: 被
INIT_EXPORT宏导出的函数必须是int fn(void)类型(无参数,返回int,通常返回0表示成功)。 -
调试: 如果某个初始化函数未被调用,首先检查宏是否书写正确,其次可以通过查看生成的
map文件确认函数指针是否被正确放置到了预期的段中。 -
静态函数: 如果初始化函数仅在当前文件使用且不想暴露给外部,可以将其定义为
static。INIT_EXPORT宏仍然能正常工作。
6、总结
RT-Thread 的自动初始化机制通过利用编译器的段特性和精心设计的宏,巧妙地实现了系统启动时各模块初始化函数的自动、有序调用。这是 RT-Thread 提高开发效率、增强系统可维护性的一个重要特性。理解其原理有助于开发者更好地组织启动代码和排查启动问题。

