超级版主
 
- 积分
- 4970
- 金钱
- 4970
- 注册时间
- 2019-5-8
- 在线时间
- 1259 小时
|
本帖最后由 正点原子运营 于 2021-11-1 10:43 编辑
以下文章摘自微信公众号——开源电子网《STM32开发中常用的C语言知识点》
更多技术文章,请扫下方二维码关注
裸机系统自动初始化详解
1.1 自动初始简介
自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。
例如我们在裸机上初始化各个外设时,一般把初始化函数放到main函数中,那么造成代码篇幅过长,为了解决这个问题,RT_thread和OneOS也是用了自动初始化机制,这样有利于代码简洁和整体上比较模块化,在传统裸机上,初始化是使用如下几个方式来初始化函数:
第一种:全部放置在main函数里面定义,如以下源码所示:
- int main(void)
- {
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
- delay_init(72); /* 延时初始化 */
- usart_init(115200); /* 串口初始化为115200 */
- usmart_dev.init(72); /* 初始化USMART */
- led_init(); /* 初始化LED */
- lcd_init(); /* 初始化LCD */
- key_init(); /* 初始化按键 */
- beep_init(); /* 蜂鸣器初始化 */
- norflash_init(); /* 初始化NORFLASH */
- my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */
- while(1)
- {
- ;
- }
- }
复制代码
第二种方式:
- <font size="4">void _init(void)
- {
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9);
- /* 设置时钟, 72Mhz */
- delay_init(72); /* 延时初始化 */
- usart_init(115200); /* 串口初始化为115200 */
- usmart_dev.init(72); /* 初始化USMART */
- led_init(); /* 初始化LED */
- lcd_init(); /* 初始化LCD */
- key_init(); /* 初始化按键 */
- beep_init(); /* 蜂鸣器初始化 */
- norflash_init(); /* 初始化NORFLASH */
- </font><div><font size="4"> </font></div>
复制代码
上述两种初始化方式也是导致初始化代码篇幅过长,小编参考了Rt-thread和OneOS的自动初始化,我觉得它们这样初始化函数比较合理,可以使用于我们平常代码规范编写。
1.2 自动初始化原理
自动初始化原理也是挺简单理解的,简单来说,就是遍历初始化函数的地址,然后执行到该地址时就会调用该地址的初始化函数,首先我们定义一个地址区域,该地址区域就是存放我们的初始化函数地址的区域,如以下源码所示:
- static int32_t lv_device_init_start(void)
- {
- return 0;
- }
- LV_INIT_EXPORT(lv_device_init_start, "1.", "");
- static int lv_device_init_end(void)
- {
- return 0;
- }
- LV_INIT_EXPORT(lv_device_init_end, "1.end", "");
复制代码
上述源码表示:定义两个地址,分别为外设初始化开始地址,外设初始化结束地址,首先关注重要的函数是以下两个函数
- <font size="4">LV_INIT_EXPORT(lv_device_init_start, "1.", "");
- </font><div><font size="4">LV_INIT_EXPORT(lv_device_init_end, "1.end", "");</font></div>
复制代码
上述的函数可以在某个头文件定义,如以下源码所示:
- typedef signed int lv_int32_t;
- typedef lv_int32_t lv_err_t;
- typedef lv_err_t (*lv_init_fn_t)(void);
- #define LV_INIT_PRIO_HIGH "1"
- #define LV_INIT_PRIO_MIDDLE "2"
- #define LV_INIT_PRIO_LOW "3"
- #define LV_SECTION(x) __attribute__((section(x)))
- #define LV_USED __attribute__((used))
- #define LV_INIT_EXPORT(lv_fn, lv_level, lv_sublevel)
- \
- LV_USED const lv_init_fn_t _lv_call_##lv_fn \
- LV_SECTION(".init_call." lv_level lv_sublevel) = lv_fn
复制代码
下面我们来分析自动初始化机制的原理,首先我们注意一个代码段,如以下所示:
- #define LV_SECTION(x) __attribute__((section(x)))
复制代码
__attribute__((section(x)))的代码段定义的是x存储函数地址或者变量地址,然后LV_SECTION(".init_call." lv_level lv_sublevel) = fn代码段表示“.init_call.1.” = fn,注意:x = “.init_call.1.”,所以“.init_call.1.”字符串指向fn(fn就是函数地址)。
“##”在c语言中必表示字符串连接符,所以我们得出以下代码:
- LV_USED const lv_init_fn_t_lv_call_lv_device_int_start".int_call.1."
复制代码
同理,LV_INIT_EXPORT(lv_device_init_end, "1.end", "");也是一样的操作,所以得到以下代码:
- LV_USED const lv_init_fn_t _lv_call_lv_device_init_end “.init_call.1.end”
复制代码
1. 3自动初始化示例
在裸机系统中,我们怎么制作自动初始化机制呢,首先我们在外设初始化函数中略加修改,例如led_init()函数,如以下源码所示:
- void led_init(void)
- {
- /* 定义引脚的函数…此处省略 */
- }
- 如果使用自动初始化,那么led_init()函数需要修改,如以下源码所示:
- static int led_init(void)
- {
- /* 定义引脚的函数…此处省略 */
- return 0;
- }
- LV_DEVICE_INIT(led_init, LV_INIT_PRIO_LOW);
- 注意:必须return0,下面调用LV_DEVICE_INIT()外设初始化,其他外设也是一样的道理.
- 1.遍历初始化函数,如以下源码所示:
- static lv_err_t _k_device_auto_init(void)
- {
- const lv_init_fn_t *lv_fn_ptr_init_start;
- /* 开始地址 */
- const lv_init_fn_t *lv_fn_ptr_init_end;
- /* 结束地址 */
- const lv_init_fn_t *lv_fn_ptr;
- /* 当前地址 */
- lv_err_t ret;
- /* 获取初始化开始地址 */
- lv_fn_ptr_init_start = &_lv_call_lv_device_init_start + 1;
- /* 获取初始化结束地址 */
- lv_fn_ptr_init_end = &_lv_call_lv_device_init_end - 1;
- for (lv_fn_ptr = lv_fn_ptr_init_start;
- lv_fn_ptr <= lv_fn_ptr_init_end;
- lv_fn_ptr++)
- /* 遍历地址 */
- {
- ret = (*lv_fn_ptr)();
- /* 执行地址的函数 */
- if (ret != 0)
- {
- return ret;
- }
- }
- return 0;
- }
复制代码
从上述源码可知:初始化开始地址不正是LV_INIT_EXPORT(lv_device_init_start,"1.", "");函数吗,而初始化结束地址不正是LV_INIT_EXPORT(lv_device_init_end,"1.end", "");,然后我们把led_init()函数地址插入这地址区域中,如下图所示:
最后在main函数这样定义即可,如以下源码所示:
- int main(void)
- {
- lv_err_t ret;
- /* 外设初始化 */
- ret = _k_device_auto_init();
-
- if (0 != ret)
- {
- printf( "\r\ndevice startup failed\r\n");
- }
-
- ................
-
- while(1);
- }
复制代码
|
|