ESP32移植rt-thread自动初始化功能源码分享
ESP32移植rt-thread自动初始化功能源码分享一、效果展示二、源码分享1、工程结构2、auto_init.h2、auto_init.c4、main.c5、autoInit.lf5、完整工程下载三、自动初始化介绍1、核心概念自动初始化2、实现原理段表 (Section Table) 与宏定义3、使用示例4、优势5、注意事项6、总结一、效果展示二、源码分享1、工程结构2、auto_init.h#ifndefRTT_AUTO_INIT_H#defineRTT_AUTO_INIT_H#includestdint.h#includestddef.h#ifdef__cplusplusexternC{#endiftypedefint(*init_fn_t)(void);#definesection(x)__attribute__((section(x)))#defineused__attribute__((used))#defineINIT_EXPORT(fn,level)usedconstinit_fn_t__rt_init_##fnsection(auto_init_fn.level)fn/* board init routines will be called in board_init() function */#defineINIT_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) */#defineINIT_PREV_EXPORT(fn)INIT_EXPORT(fn,2)/* device initialization */#defineINIT_DEVICE_EXPORT(fn)INIT_EXPORT(fn,3)/* components initialization (dfs, lwip, ...) */#defineINIT_COMPONENT_EXPORT(fn)INIT_EXPORT(fn,4)/* environment initialization (mount disk, ...) */#defineINIT_ENV_EXPORT(fn)INIT_EXPORT(fn,5)/* application initialization (rtgui application etc ...) */#defineINIT_APP_EXPORT(fn)INIT_EXPORT(fn,6)// 自动初始化执行函数voidautoinit_execute(void);#ifdef__cplusplus}#endif#endif/* RTT_AUTO_INIT_H */2、auto_init.c/************************************************* * copyright: * author:Xupeng * date:2022-11-03 * description: **************************************************/#includeauto_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. */staticintauto_init_start(void){return0;}INIT_EXPORT(auto_init_start,0);staticintauto_init_board_start(void){return0;}INIT_EXPORT(auto_init_board_start,0.end);staticintauto_init_board_end(void){return0;}INIT_EXPORT(auto_init_board_end,1.end);staticintauto_init_end(void){return0;}INIT_EXPORT(auto_init_end,6.end);/************************************************* * function:static void components_board_init(void) * description:组件板级初始化 * calls: * input: * return: * others: *************************************************/staticvoidcomponents_board_init(void){volatileconstinit_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: *************************************************/voidcomponents_init(void){volatileconstinit_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: *************************************************/voidautoinit_execute(void){components_board_init();components_init();}4、main.c#includestdio.h#includefreertos/FreeRTOS.h#includefreertos/task.h#includeshell_port.h#includeesp_chip_info.h#includeesp_flash.h#includeauto_init.h#includeesp_log.h// 板级初始化 (优先级1)staticintboard_init_test(void){printf(board_init_test\n);return0;}INIT_BOARD_EXPORT(board_init_test);staticintprev_init_test(void){printf(prev_init_test\n);return0;}INIT_PREV_EXPORT(prev_init_test);// 设备初始化 (优先级3)staticintdevice_init_test(void){printf(device_init_test\n);return0;}INIT_DEVICE_EXPORT(device_init_test);staticintcomponent_init_test(void){printf(component_init_test\n);return0;}INIT_COMPONENT_EXPORT(component_init_test);staticintenv_init_test(void){printf(env_init_test\n);return0;}INIT_ENV_EXPORT(env_init_test);// 应用初始化 (优先级6)staticintapp_init_test(void){printf(app_init_test\n);return0;}INIT_APP_EXPORT(app_init_test);voidapp_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_rodataKEEP()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、使用示例开发者只需在自己的初始化函数通常是无参数、无返回值的函数前面加上对应的宏即可。例如初始化一个串口设备驱动staticintuart_init(void){/* 串口硬件初始化配置 */return0;}INIT_DEVICE_EXPORT(uart_init);// 将该函数声明为设备初始化函数初始化一个文件系统并挂载到 Flash 分区staticintfilesystem_init(void){/* 挂载文件系统到 / 根目录 */return0;}INIT_ENV_EXPORT(filesystem_init);// 将该函数声明为环境初始化函数启动一个应用线程staticvoidapp_thread_entry(void*parameter){while(1){/* 应用线程工作 */}}staticintapp_start(void){rt_thread_ttid;tidrt_thread_create(app,app_thread_entry,RT_NULL,2048,25,10);if(tid!RT_NULL){rt_thread_startup(tid);}return0;}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 提高开发效率、增强系统可维护性的一个重要特性。理解其原理有助于开发者更好地组织启动代码和排查启动问题。