说明:本文灵感来自于 RT-Thread 的自动化初始思路,表示感谢.
如何让我们的初始化函数自动执行,让代码看起来更简洁:
如下面所示:
int main(void)
{
while (1)
{
}
}
串口助手打印显示:
init BOARD …
init DEVICE …
init COMPONENT …
init ENV …
init APP …
实际上在main函数里面我并没有做任何操作,就已经做到5个初始化函数已经执行。
如果我们把要初始化函数的地址按照顺序排列在一起,我们只要拿到第一个函数的地址,那么我们就可以依次拿到其它函数的地址,然后去执行相应的函数。
typedef int (*aini_init_fn_t)(void);
此函数形参为void,返回值类型int
后面我们自己的初始化函数也必须是这种格式
注意:不要在初始化函数中使用堵塞类型的操作,因为他们是顺序执行的,会影响排列在后面的函数执行
#define AINI_USED __attribute__((used))
#define AINI_SECTION(x) __attribute__((section(x)))
#define AINI_INIT_EXPORT(fn,level) \
AINI_USED const aini_init_fn_t __aini_call_##fn AINI_SECTION(".aini_call." level) = fn
_aini_call##fn 是一个aini_init_fn_t类型的指针变量,并把函数地址fn赋值给它。
##操作是连接左右两边的字符串,这样就可以定义前缀是"_aini_call"+函数名的变量,把这个变量放入.aini_call.区域,并使用level来进行标记排序
//板级初始化 顺序1
#define AINI_BOARD_INIT(fn) AINI_INIT_EXPORT(fn,"1")
//设备初始化 顺序3
#define AINI_DEVICE_INIT(fn) AINI_INIT_EXPORT(fn,"2")
//组件初始化 顺序4
#define AINI_COMPONENT_INIT(fn) AINI_INIT_EXPORT(fn,"3")
//环境初始化 顺序5
#define AINI_ENV_INIT(fn) AINI_INIT_EXPORT(fn,"4")
//APP初始化 顺序6
#define AINI_APP_INIT(fn) AINI_INIT_EXPORT(fn,"5")
static int aini_start(void)
{
// serial_hw_console_output("os_start\r\n");
return 0;
}
AINI_INIT_EXPORT(aini_start,"0");
static int aini_board_start(void)
{
// serial_hw_console_output("os_board_start\r\n");
return 0;
}
AINI_INIT_EXPORT(aini_board_start,"0.end");
static int aini_board_end(void)
{
// serial_hw_console_output("os_board_end\r\n");
return 0;
}
AINI_INIT_EXPORT(aini_board_end,"1.end");
static int aini_end(void)
{
// serial_hw_console_output("os_end\r\n");
return 0;
}
AINI_INIT_EXPORT(aini_end,"5.end");
/**
* @brief 自动调用 OS_BOARD_INIT(fn) 自定的函数
* .init_call.1
*/
static void os_components_board_init(void)
{
volatile const aini_init_fn_t *pfn;
for(pfn = &__aini_call_aini_board_start; pfn < &__aini_call_aini_board_end;pfn++)
{
(*pfn)();
}
}
/**
* @brief 自动初始化 section .init_call.2 ~ .init_call.5
*
*/
static void os_components_init(void)
{
volatile const aini_init_fn_t *pfn;
for(pfn = &__aini_call_aini_board_end; pfn < &__aini_call_aini_end; pfn++)
{
(*pfn)();
}
}
aini_components_board_init() aini_components_init() 两个函数需要手动调用
$Super$$main 0x08001395 Thumb Code 2 main.o(i.main)
serial_hw_console_output 0x08001399 Thumb Code 26 main.o(i.serial_hw_console_output)
__aini_call_aini_start 0x080013b8 Data 4 aini.o(.aini_call.0)
__aini_call_aini_board_start 0x080013bc Data 4 aini.o(.aini_call.0.end)
__aini_call_board_init 0x080013c0 Data 4 main.o(.aini_call.1)
__aini_call_aini_board_end 0x080013c4 Data 4 aini.o(.aini_call.1.end)
__aini_call_device_init 0x080013c8 Data 4 main.o(.aini_call.2)
__aini_call_component_init 0x080013cc Data 4 main.o(.aini_call.3)
__aini_call_env_init 0x080013d0 Data 4 main.o(.aini_call.4)
__aini_call_app_init 0x080013d4 Data 4 main.o(.aini_call.5)
__aini_call_aini_end 0x080013d8 Data 4 aini.o(.aini_call.5.end)
可以看出__aini_call_aini_start __aini_call_aini_end 起到了占位作用
int env_init(void)
{
serial_hw_console_output("init ENV ...\r\n");
}
AINI_ENV_INIT(env_init);
首先定义 int fun(void)类型的初始化函数
接着使用下面5个宏之一指定初始化函数的执行顺序:
1.AINI_BOARD_INIT(fn)
2.AINI_DEVICE_INIT(fn)
3.AINI_COMPONENT_INIT(fn)
4.AINI_ENV_INIT(fn)
5.AINI_APP_INIT(fn)
int main(void)
{
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
aini_components_board_init();
aini_components_init();
while (1)
{
}
}
/* 下面是我自定义的初始化函数 */
int app_init(void)
{
serial_hw_console_output("init APP ...\r\n");
}
AINI_APP_INIT(app_init);
int env_init(void)
{
serial_hw_console_output("init ENV ...\r\n");
}
AINI_ENV_INIT(env_init);
int component_init(void)
{
serial_hw_console_output("init COMPONENT ...\r\n");
}
AINI_COMPONENT_INIT(component_init);
int device_init(void)
{
serial_hw_console_output("init DEVICE ...\r\n");
}
AINI_DEVICE_INIT(device_init);
int board_init(void)
{
serial_hw_console_output("init BOARD ...\r\n");
}
AINI_BOARD_INIT(board_init);
现在看起来好像还是没那么简介毕竟main函数里面还是调用了很多函数,没有完全做到底层和业务层完全分离
先来了解下编译器语法:
$Sub$$main() //重定义main函数,在mian函数之前执行
$Super$$main()//跳转到main函数
有了这两个语法我们继续优化:
int $Sub$$main(void)
{
/* 1.关中断 */
//do nothing
/* 2.初始化系统时钟,片内资源 */
ani_seq_init();
/* 3.初始化板级设备 */
aini_components_board_init();
/* 4.初始化一些软件级别的初始化,如库,APP等 */
aini_components_init();
/* 5. 开中断 */
//do nothing
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int main(void);
extern int $Super$$main(void);
/* 6.跳转到main函数 */
$Super$$main(); /* for ARMCC. */
#else
#error 'not support complier!'
#endif
return 0;
}
__weak int ani_seq_init(void)
{
/* 初始化系统时钟,片内资源 */
}
再来看看mian函数
int main(void)
{
while (1)
{
}
}
是不是已经很简洁了
我把源代码工程放到了gitee 仓库,欢迎学习交流!
aini地址