在嵌入式开发过程中,可能会遇到初始化代码自动初始化,比如RTT中就运用到这项技术。那么初始化代码是如何做到自动化调用的呢?
在嵌入式实际开发过程中,往往需要对 bsp 部分进行外设配置,以及一些模块、参数进行初始化,常见的方法如下:
那有没有一种方法能解决上述的问题呢?
答案是肯定的,本文将向大家介绍一种自动初始化的实现方式。注意,此方案在不同的平台,由于链接器使用的链接脚本可能不一致,容易出现问题,需要大家重点注意,细节在下文中将详细介绍。
为了更好的阐述代码自动初始化的技术,本文通过一个demo程序进行讲解如何实现该技术,以下是该demo程序:
typedef void (*init_func)(void);
#define INIT_EXPORT(fn, level) const init_func init_##fn __attribute__((used, section(".init."level))) = fn
void start(void)
{
return;
}
INIT_EXPORT(start, "1");
void end(void)
{
return;
}
INIT_EXPORT(end, "4");
接下来分析上述代码:
关于宏定义:#define INIT_EXPORT(fn, level) const init_func init_##fn attribute((used, section(".init."level))) = fn的代码分析如下:
综述分析:宏#define INIT_EXPORT(fn, level) const init_func init_##fn attribute((used, section(".init.“level))) = fn,其实定义了一个init_func类型的函数指针,并将此函数指针通过__attribute__((used, section(”.init."level)))指定存放到特定的段内。
demo程序中定义了两个函数:
注意事项:
最后的vhdl_board_init初始化函数:
void vhdl_board_init(void)
{
const init_func *fn_ptr = NULL;
for (fn_ptr = &init_start; fn_ptr < &init_end; fn_ptr++)
{
(*fn_ptr)();
}
}
此函数在main函数中调用,通过for循环遍历start和end中间的段,并调用进行初始化。
通过上述一系列的操作,用户初始化函数时只需要调用宏INIT_EXPORT(fn,level)即可完成自动初始化。
为了针对不同类型的初始化,可以将start和end之间距离拉大点,用作区别不同类型的初始化,如下所示
INIT_EXPORT(start, "1");
INIT_EXPORT(end, "6");
并且对外开放相应的接口宏
#define INIT_BSP_PORT(func) INIT_EXPORT(func, "2");
#define INIT_DATA_PORT(func) INIT_EXPORT(func, "3");
用户通过调用 BSP_INIT_PORT 和 DATA_INIT_PORT 进行注册
在上述方法中,我们使用 attribute((section(".init."level))) 将数据存入指定的段内,那么是否成功了呢,以及排序方式是否符合我们的预期呢?接下来我们需要通过检查map文件进行确认。
keil/mdk编译完程序之后,双击工程栏处的工程,可以查看map文件,还不知道的可以网上搜下
以下是上述demo的map文件中关键字段,我们可以看到我们需要的数据存放在正确的段内,并且排序也是正常的,注意一定要检查排序和字段是否完整!
.ALIGN(4)
__rt_init_start=.;
KEEP(*(SORT(.rti_fn*)))
__rt_init_end=.;
.ALIGN(4)
typedef void (*init_fn_t)(void);
#define INIT_EXPORT(fn,level) __attribute__((used)) const init_fn_t __rt_init_##fn __attribute__((section(".rti_fn." level))) = fn
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start,"0");
static int rti_board_start(void)
{
return 0;
}
// 注册rti_board_start到内存中
INIT_EXPORT(rti_board_start,"0.end");
static int rti_board_end(void)
{
return 0;
}
// 注册rti_board_end到内存中
INIT_EXPORT(rti_board_end,"1.end");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_start,"6.end");
void rt_components_board_init(void)
{
volatile const init_fn_t *fn_ptr;
// 遍历内存中注册的地址段
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
}
// 遍历剩下的地址内存空间,防止函数指针访问出错
void rt_components_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif /* RT_DEBUG_INIT */
}
#endif /* RT_USING_COMPONENTS_INIT */
//最后生成的map文件如下:
*(SORT(.rti_fn*))
.rti_fn.0 0x0800cf30 0x4 ./rt-thread/src/components.o
0x0800cf30 __rt_init_rti_start
.rti_fn.0.end 0x0800cf34 0x4 ./rt-thread/src/components.o
0x0800cf34 __rt_init_rti_board_start
.rti_fn.1 0x0800cf38 0x4 ./drivers/drv_clk.o
0x0800cf38 __rt_init_clock_information
.rti_fn.1.end 0x0800cf3c 0x4 ./rt-thread/src/components.o
0x0800cf3c __rt_init_rti_board_end
.rti_fn.6 0x0800cf40 0x4 ./rt-thread/components/finsh/shell.o
0x0800cf40 __rt_init_finsh_system_init
.rti_fn.6.end 0x0800cf44 0x4 ./rt-thread/src/components.o
0x0800cf44 __rt_init_rti_end
0x0800cf48 __rt_init_end = .
0x0800cf48 . = ALIGN (0x4)