自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。
这里的最主要的看点就是RT-Thread,在flash中开辟了一片区域用来存放需要设备初始化的函数地址,使用宏定义的方式把需要执行初始化的函数加入到这片区域中,这篇区域主要分为两部分。
第一部分为rt_components_board_init()函数中执行,通过使用INIT_BOARD_EXPORT(fn)宏定义把需要的初始化函数加入到初始化队列中。
第二部分为 rt_components_init()函数中执行,通过 INIT_PREV_EXPORT(fn)、INIT_DEVICE_EXPORT(fn) 申明的初始化函数、INIT_COMPONENT_EXPORT(fn)申明的初始化函数、INIT_ENV_EXPORT(fn) 申明的初始化函数、 INIT_APP_EXPORT(fn)申明的初始化函数。
下面使用串口初始化作为例子讲解
这是串口初始化的函数,使用INIT_BOARD_EXPORT(rt_hw_usart_init),把函数地址写到flash初始化区
int rt_hw_usart_init(void) /* 串口初始化函数 */
{
... ...
/* 注册串口 1 设备 */
rt_hw_serial_register(&serial1, "uart1",
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
uart);
return 0;
}
INIT_BOARD_EXPORT(rt_hw_usart_init); /* 使用组件自动初始化机制 */
具体实现步骤:
INIT_BOARD_EXPORT(rt_hw_usart_init)找到
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn
通过这两条宏定义替换INIT_BOARD_EXPORT(rt_hw_usart_init)就是等价于执行RT_USED const init_fn_t __rt_init_rt_hw_usart_init SECTION(".rti_fn."1) = rt_hw_usart_init
PS:__rt_init_##fn中的##就是一个字符串连接符
关于 SECTION的详细介绍,可以看这篇文章:linkhttps://blog.csdn.net/mloves0729/article/details/14519485。
大致的意思就是分配一个section用来存放数据,以".rti_fn.1”作为section名字,程序中使用section名字直接访问对应的flash内存
RT_USED const init_fn_t __rt_init_rt_hw_usart_init SECTION(".rti_fn."1) = rt_hw_usart_init
翻译过来就是把 rt_hw_usart_init的函数地址放在.rti_fn.1section段中
调用同一个宏定义会把几个函数的地址放在同一个section段中
比如:INIT_BOARD_EXPORT(rt_hw_pin_init)和INIT_BOARD_EXPORT(rt_hw_usart_init)都放在.rti_fn.1section段中
从map文件中比较直观能看出来,主要分为两段,一个是.rti_fn.1,另一个就是剩下的初始化段
在__rt_init_rt_hw_usart_init这个地址中存放着rt_hw_usart_init函数的地址。
自动初始化的执行
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)();
}
__rt_init_rti_board_start flash地址是0x0800f5f4,里面存放初始化启动函数的地址,通过函数指针历遍需要初始化的函数,从__rt_init_rti_board_start 到 __rt_init_rt_hw_pin_init 再到__rt_init_rt_hw_usart_init 。
PS:section的排序是同一section放一起,按函数名字排序,不同section按名字排序,