ESP32移植rt-thread自动初始化功能源码分享

AI3小时前发布 beixibaobao
1 0 0

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)

  1. 函数声明宏:
    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 的段中。
  2. 段表与初始化顺序:
    在 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 在内存中连续排列。数字较小的段地址较低。

    在这里插入图片描述

  3. 启动过程:
    在 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 文件确认函数指针是否被正确放置到了预期的段中。
  • 静态函数: 如果初始化函数仅在当前文件使用且不想暴露给外部,可以将其定义为 staticINIT_EXPORT 宏仍然能正常工作。

6、总结

RT-Thread 的自动初始化机制通过利用编译器的段特性和精心设计的宏,巧妙地实现了系统启动时各模块初始化函数的自动、有序调用。这是 RT-Thread 提高开发效率、增强系统可维护性的一个重要特性。理解其原理有助于开发者更好地组织启动代码和排查启动问题。

在这里插入图片描述

© 版权声明

相关文章