看到了linux中的模块加载和卸载, 知道是通过attribute((sectiion))来实现的, 但是经过一顿研究, 发现如果想要在单片机中使用, 要自己写链接脚本. 我这种菜鸡也写不出来啊.黄天不负有心人, 最终找到一个简单的方法, 那就是通过__attribute__((constructor))
__attribute__((constructor))
是GCC和兼容的编译器中的一个特性,用于指示编译器将一个函数标记为在程序启动时自动执行的初始化函数。
当你在一个函数声明或定义前加上__attribute__((constructor))
属性时,就会告诉编译器,在程序加载时(在main函数执行之前),需要自动调用这个函数。这个特性通常用于在程序启动时执行一些全局的初始化工作,比如注册回调函数、初始化全局变量等。
#define AUTO_INIT __attribute__((constructor))
AUTO_INIT void LED_Init (void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(LED0_GPIO_CLK, ENABLE);
RCC_AHB1PeriphClockCmd(LED1_GPIO_CLK, ENABLE);
RCC_AHB1PeriphClockCmd(LED2_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = LED0_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(LED0_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
LED0(0);
LED1(0);
LED2(0);
}
也许有人会说为什么要整这个, 主要还是为了模块化, 比如我们公司的产品, 会根据客户的需求, 挂不同的外设, 通过一个配置文件(开关宏), 就可以实现功能裁剪.(我们公司用的是iar, 也是通过类似attribute实现)
注意这个是在启动的时候就会执行被修饰的函数, 因此用于硬件初始化是没问题的, 或者指挥在最开始调用的函数.
//2024-1-23修改:
自动化注册是使用了编译器的特性, linux中的module_init()就是, 如果想要了解单片机中怎么使用的, 可以看rt-thread中的export部分, 并不需要修改连接脚本.
关于自动注册怎么写框架,以按键为例
1.在bsp_io.c中写io初始化,整个文件的函数不向外暴露
#ifndef BSP_CLASS_H
#define BSP_CLASS_H
//创建io操作类
typedef void(*io_write)(u8 value);
typedef u8 (*io_read)(void);
typedef struct {
io_write write;
io_read read;
}io_ops_t;
//io设备类
typdef struct {
const char* name;
const io_ops_t *ops;
struct node* next; //这个是结点,用于挂到bsp链表
}io_t;
#endif
#include "stm32f10x.h"
#include "bsp_class.h"
//只有按键,只需要写读函数
u8 bsp_key_io_read(void)
{
return io_state;
}
//实例化操作
const io_ops_t key_ops = {
.read = bsp_key_io_read;
};
//实例化io设备类
static io_t key = {
.name = "key",
.ops = &key_ops,
};
static void bsp_key_io_init()
{
//初始化io
//将bsp层的按键注册到bsp链表
bsp_reg(&key);
}
AUTO_BSP_INIT(bsp_key_io_init)
2.在dev_key.c中获取按键状态, 其他模块通过订阅按键事件处理相应事情(这里要向外面暴露一个订阅按键状态函数)
#include "bsp_class.h"
io_t *key = NULL
static void key_handle(void)
{
u8 state = key->ops->read();
switch(state)
{
case INIT:
break;
case PRESS:
//这里可以通过订阅按键事件做相应的处理
break;
}
}
static void dev_key_init(void)
{
key = bsp_find("key");
if(key){
dev_register(key_handle);//裸机把他注册到一个10ms的定时任务中,rtos中单独创建一个按键线程用来检测按键
}
}
AUTO_DEV_INIT(dev_key_init);