在嵌入式学习工作中经常初始化一个硬件就写一个初始化函数,基本都要到主函数中调用进行初始化,main函数都要改变。当利用__attribute__((section("name")))
这个属性就可以构造一个初始化函数表
,这样每次初始化一个硬件啥的就不用到main函数中去调用初始化函数。式在RTT初始化函数和Linux初始化驱动模块也是类似这样做的。
attribute的用法
http://www.keil.com/support/man/docs/armcc/armcc_chr1359124982450.htm
代码
- 头文件
#ifndef _HARDWARE_INIT_H_
#define _HARDWARE_INIT_H_
#if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARMCC Complier */
#define INIT_SECTION(x) __attribute__((section(x)))
#define INIT_USED __attribute__((used))
#elif defined(__ICCARM__) /* IAR ARMCC Complier */
#elif defined(__GNUC__) /* GNUC Complier */
#else
#error "not support tool chain"
#endif
typedef void (*init_func)(void);
typedef struct{
init_func _init_func;
}init_func_t;
#define INIT_EXPORT(handler) INIT_USED init_func_t _init_##handler##_func INIT_SECTION("INIT_LIST") = \ //INIT_LIST自定义段名
{ handler, }
void sys_init(void);
#endif
- 源文件
在MDK中使用下面方式获得自定义段的起始和终止地址。
static init_func_t *init_list_begin;
static init_func_t *init_list_end;
void sys_init(void)
{
init_func_t *index;
#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern const int INIT_LIST$$Base;
extern const int INIT_LIST$$Limit;
init_list_begin = (init_func_t *)&INIT_LIST$$Base; //获得段起始地址
init_list_end = (init_func_t *)&INIT_LIST$$Limit; //获得结束段地址
#elif defined(__ICCARM__) /* IAR ARMCC Complier */
#elif defined(__GNUC__) /* GNUC Complier */
#endif
for(index = init_list_begin; index < init_list_end; index++)
{
index->_init_func();
}
}
- 初始化函数使用INIT_EXPORT修饰
void MY_USART_Init(void)
{
__MY_USART_Init(115200);
}
INIT_EXPORT(MY_USART_Init);
用INIT_EXPORT修饰过的函数都会定义一个函数指针在自定义的section——INIT_LIST
,这个自定义的段由编译器静态分配。
- 主函数中调用sys_init()
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
sys_init();
while(1)
{
software_timer_main_loop();
}
}
原文地址:https://www.cnblogs.com/AresGuangjie/p/12392892.html
时间: 2024-11-14 11:47:49