简介
mr-auto-init 模块为 mr-library 项目下的可裁剪模块,以C语言编写,可快速移植到各种平台(主要以嵌入式mcu为主)。
mr-auto-init 模块通过隐式调用初始化函数,使代码更简介更模块化。
自动初始化
常规调用初始化函数都是在 main
函数前单独调用,使得程序非常冗长同时高度耦合,导致模块化困难。而自动初始化,因为其为隐式调用,使得程序非常简洁,高度解耦。在各类 RTOS
中自动初始化已经是较为常见的存在。
自动初始化的核心为编译器命令 __attribute__((section(x)))
这条指令会将修饰的函数添加到指定的同一块内存中,由于内存是连续的,让我们以指定的长度移动时便可将函数依次提取出来。
初始化函数类型
首先我们先定义一个初始化函数类型,需要强调的是,一旦确定这个函数类型,后续所有的初始化函数都应是此类型的。因为函数保存在一整块内存中,我们只有确定的函数类型才能正确的取出函数。
typedef int (*init_fn_t)(void);
优先级导出函数
__attribute__((section(x)))
这条指令虽然实现了自动初始化,但是并不能满足我们实际使用中,初始化需要按照优先级调用的需求,因此我们需要完善这个命令。
#define INIT_EXPORT(fn,level) \
mr_used const init_fn_t _mr_init_##fn mr_section(".mri_fn."level) = fn
例如:
int init(void)
{
printf("auto-init");
}
INIT_EXPORT(init, "1");
最终经过展开
__attribute__((used))const init_fn_t _mr_init_init __attribute__((section(".mri_fn." "1"))) = init
这个函数就被导出到了".mri_fn.1"
这块空间。
当我们再导出一个函数到".mri_fn.2"
,当自动初始化时将先调用".mri_fn.1"
中的函数再调用".mri_fn.2"
中的函数。
获取初始化函数起始地址和初始化函数结束地址
static int mri_start(void)
{
return 0;
}
INIT_EXPORT(mri_start, "0");
static int mri_end(void)
{
return 0;
}
INIT_EXPORT(mri_end,"6.end");
创建两个函数,当我们取mri_start
地址时我们将得到初始化函数起始地址,取mri_end
地址时我们将得到初始化函数结束地址。
如何自动初始化
从函数起始地址开始取函数,调用完毕后向后移动,直到到达函数结束地址。
void mr_auto_init(void)
{
volatile const init_fn_t *fn_ptr;
/* auto-init-board */
for (fn_ptr = &_mr_init_mri_start; fn_ptr < &_mr_init_mri_board_end; fn_ptr++)
{
(*fn_ptr)();
}
/* auto-init-other */
for (fn_ptr = &_mr_init_mri_board_end; fn_ptr < &_mr_init_mri_end; fn_ptr++)
{
(*fn_ptr)();
}
}
使用示例
/* -------------------- 模拟需要导出的初始化函数 -------------------- */
int board_init(void)
{
printf("auto-init: board\r\n");
return 0;
}
INIT_BOARD_EXPORT(board_init);
int device_init(void)
{
printf("auto-init: device\r\n");
return 0;
}
INIT_DEVICE_EXPORT(device_init);
int env_init(void)
{
printf("auto-init: env\r\n");
return 0;
}
INIT_ENV_EXPORT(env_init);
int app_init(void)
{
printf("auto-init: app\r\n");
return 0;
}
INIT_APP_EXPORT(app_init);
/* -------------------- 使用 -------------------- */
int main(void)
{
/* 初始化 */
mr_auto_init();
/* 用户代码 */
...
}
注意事项
当您使用中出现功能无法实现并且您的编译器为 GCC
时,请您手动在 link.ld
/ SECTIONS
/ .text
中添加以下代码。
/* section information for initial. */
. = ALIGN(4);
_mr_init_ = .;
KEEP(*(SORT(.mri_fn*)))
_mr_init__end = .;
剩余底层代码位于开源代码中,请下载开源代码。
开源代码
仓库链接(https://gitee.com/chen-fanyi/mr-library.git)
路径:master/mr-library/ device / mr_auto_init
或者给我发email:macrsh@outlook.com