自动初始化外设

__attribute__((constructor))在单片机编程中用于全局初始化,如LED设置和按键事件处理。它允许模块化设计并通过配置文件控制功能。文章介绍了如何在STM32F10x平台使用这个特性,并给出了按键管理的框架示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看到了linux中的模块加载和卸载, 知道是通过attribute((sectiion))来实现的, 但是经过一顿研究, 发现如果想要在单片机中使用, 要自己写链接脚本. 我这种菜鸡也写不出来啊.黄天不负有心人, 最终找到一个简单的方法, 那就是通过__attribute__((constructor))

编译链接实战(16)编译器内置属性修饰符__attribute__((constructor))用法解析___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);

### STM32外设初始化教程 在STM32微控制器中,使用HAL库进行外设初始化是一种常见的方式。通过STM32CubeMX工具可以自动生成初始化代码,简化开发流程。以下是基于HAL库的外设初始化方法以及示例代码。 #### 使用STM32CubeMX配置外设 利用STM32CubeMX图形化界面完成硬件资源分配和软件框架搭建后,生成的代码会自动包含必要的初始化函数调用。这些函数通常位于`main.c`文件中的`MX_GPIO_Init()`或其他特定外设初始化函数中[^1]。 #### GPIO外设初始化示例 以下是一个简单的GPIO外设初始化示例: ```c // 初始化GPIO引脚 void MX_GPIO_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); // 启用GPIOA时钟 GPIO_InitTypeDef GPIO_InitStruct = {0}; // 配置PA5为推挽输出模式 GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` 上述代码展示了如何启用GPIOA端口时钟并设置PA5引脚为低速推挽输出模式。 #### UART串口外设初始化示例 对于UART通信模块,其初始化过程如下所示: ```c // USART2初始化配置 static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 9600; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } ``` 此部分代码实现了USART2的基本参数设定,包括波特率、数据位长度、停止位数目等,并完成了实际的初始化操作。 #### 定义错误处理函数 为了增强程序健壮性,在初始化失败时应定义相应的错误处理逻辑: ```c void Error_Handler(void) { while(1); // 进入死循环等待调试 } ``` 以上即为基于HAL库实现STM32外设初始化的主要内容及其典型应用实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

入门->放弃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值