[应用相关] STM32上实现驱动注册initcall机制

[复制链接]
924|9
 楼主| 药无尘 发表于 2022-9-15 15:41 | 显示全部楼层 |阅读模式
一、前言

上一节嵌入式中实现应用层和硬件层分层管理我们实现了代码应用层和硬件层的分离管理,但是代码中还存在一个问题,每个硬件如LED控制,GPIO口需要初始化,初始化函数bsp_led_init();这个函数需要在主函数中调用初始化,类似这样:

  1. void bsp_init(void)
  2. {
  3.     bsp_rcc_init();
  4.     bsp_tick_init();
  5.     bsp_led_init();
  6.     bsp_usart_init();
  7. }

这样存在的问题是:当有很对驱动,加入100个硬件驱动,我们只用到了了50个,剩下的源文件不参与编译,此时如果忘记将主函数中的相应初始化删除,就会报错。这样操作很麻烦,不能很好的实现单个驱动文件的隔离。

那么现在就提供解决此问题的方式。这个方式源自于Linux内核--initcall机制。具体讲解网络上很多,在此不在详细说明。

可阅读:

keil 之Image:

https://www.cnblogs.com/idle_man/archive/2010/12/18/1910158.html

linux的initcall机制(针对编译进内核的驱动) :

https://www.cnblogs.com/downey-blog/p/10486653.html


 楼主| 药无尘 发表于 2022-9-15 15:43 | 显示全部楼层
二、代码
头文件:
  1. #ifndef _COLA_INIT_H_
  2. #define _COLA_INIT_H_


  3. #define  __used  __attribute__((__used__))

  4. typedef void (*initcall_t)(void);

  5. #define __define_initcall(fn, id) \
  6.     static const initcall_t __initcall_##fn##id __used \
  7.     __attribute__((__section__("initcall" #id "init"))) = fn;

  8. #define pure_initcall(fn)       __define_initcall(fn, 0) //可用作系统时钟初始化  
  9. #define fs_initcall(fn)         __define_initcall(fn, 1) //tick和调试接口初始化
  10. #define device_initcall(fn)     __define_initcall(fn, 2) //驱动初始化
  11. #define late_initcall(fn)       __define_initcall(fn, 3) //其他初始化
  12.    

  13. void do_init_call(void);
  14.    
  15. #endif

源文件:
  1. #include "cola_init.h"



  2. void do_init_call(void)
  3. {
  4.     extern initcall_t initcall0init$Base[];
  5.     extern initcall_t initcall0init$Limit[];
  6.     extern initcall_t initcall1init$Base[];
  7.     extern initcall_t initcall1init$Limit[];
  8.     extern initcall_t initcall2init$Base[];
  9.     extern initcall_t initcall2init$Limit[];
  10.     extern initcall_t initcall3init$Base[];
  11.     extern initcall_t initcall3init$Limit[];
  12.    
  13.     initcall_t *fn;
  14.    
  15.     for (fn = initcall0init$Base;
  16.             fn < initcall0init$Limit;
  17.             fn++)
  18.     {
  19.         if(fn)
  20.             (*fn)();
  21.     }
  22.    
  23.     for (fn = initcall1init$Base;
  24.             fn < initcall1init$Limit;
  25.             fn++)
  26.     {
  27.         if(fn)
  28.             (*fn)();
  29.     }
  30.    
  31.     for (fn = initcall2init$Base;
  32.             fn < initcall2init$Limit;
  33.             fn++)
  34.     {
  35.         if(fn)
  36.             (*fn)();
  37.     }
  38.    
  39.     for (fn = initcall3init$Base;
  40.             fn < initcall3init$Limit;
  41.             fn++)
  42.     {
  43.         if(fn)
  44.             (*fn)();
  45.     }
  46.       
  47. }

在主进程中调用void do_init_call(void)进行驱动初始化,驱动注册初始化时调用:
  1. pure_initcall(fn)        //可用作系统时钟初始化  
  2. fs_initcall(fn)          //tick和调试接口初始化
  3. device_initcall(fn)      //驱动初始化
  4. late_initcall(fn)

举个例子:
  1. static void led_register(void)
  2. {
  3.     led_gpio_init();
  4.     led_dev.dops = &ops;
  5.     led_dev.name = "led";
  6.     cola_device_register(&led_dev);
  7. }

  8. device_initcall(led_register);

这样头文件中就没有有对外的接口函数了。
 楼主| 药无尘 发表于 2022-9-15 15:44 | 显示全部楼层
三、代码
gitee:https://gitee.com/schuck/cola_os
girhub:https://github.com/sckuck-bit/cola_os
qiufengsd 发表于 2022-10-25 22:18 | 显示全部楼层
借助于 linux内核中的initcall机制被调用?
maudlu 发表于 2022-10-25 22:39 | 显示全部楼层
initcall是kernel经典设计机制之一
loutin 发表于 2022-11-1 10:08 | 显示全部楼层
静态编译进内核和动态加载两种方式了吗?  
kkzz 发表于 2022-11-1 10:37 | 显示全部楼层
如何开启initcall         
maqianqu 发表于 2022-11-1 15:04 | 显示全部楼层
使用一个字符串声明你的驱动初始化函数,那么所有的驱动初始化函数都存在内存中一个连续的段中,系统启动以后,会从这个段的第一个函数开始
SantaBunny 发表于 2022-11-2 20:57 | 显示全部楼层

initcall是kernel设计机制之一
您需要登录后才可以回帖 登录 | 注册

本版积分规则

79

主题

623

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部