C语言section关键字初始化函数列表-keil环境

半年前在公众号看过section初始化列表,当时看得迷迷糊糊没有去深入学习,最近在项目中刚好需要用到,重新去学习了一下。在嵌入式学习工作中,初始化硬件时一般都是写一个初始化函数然后去到main函数去调用,当不需要用到某一个硬件模块时,如果没有去main函数删除对应的硬件初始化函数,程序就会报错,这样程序耦合性就变高了。使用section关键字初始化函数列表能够降低耦合性,下面介绍keil环境下section关键字初始化函数列表的两种方法。

一、section关键字

section主要作用是将函数或者变量放在指定段中,既可以是RAM内存和可以是ROM内存,这样就可在指定的位置取出。标记为attribute__((used))的变量被标记在目标文件中,以避免链接器优化删除。

#define SECTION(level) __attribute__((used, section("my_val."level)))
static int value1 SECTION("0");
static int value2 SECTION("0");

int SECTION("0") test0 (void) 
{
	return value1;
}

int SECTION("0") test1 (void) 
{
	return value2;
}

int main(void)
{
	value1 = 1;
	value2 = 2;
	test0();
	test1();
}

查看map文件

 分析map文件可知,value1和value2被放在RAM内存的my_val.0段是连续的,test0和test1被放在ROM内存my_val.0段也是连续的。

 二、初始化列表

方法一:

由上面可知,section字段中变量在内存中是连续的,所以我们将需要初始化函数以指针形式存在字段中,只需要记录字段的首地址和结束地址,在mian函数将字段中的函数指针遍历就可以调用对应的初始化函数,从事实现自动初始化函数列表。

(1)代码实现

typedef void (*initcall_t)(void);//函数指针类型
typedef struct
{
    initcall_t fn_minit;
} initcall_type; //封装成结构体类型

extern initcall_type   __initcall_start_;//记录开始地址
extern initcall_type   __initcall_end_;  //记录结束地址

#define INITCALL_LIST(ifn) \
 static initcall_type __initcall_##ifn  __attribute__((used, section(".init.1"))) = {ifn}

#define INITCALL_START \
	  initcall_type __initcall_start_  __attribute__((used, section(".init.0.start"))) 
	
#define INITCALL_END \
	  initcall_type __initcall_end_  __attribute__((used, section(".init.1.end"))) 

#define INITIALISE_ALL \
    { for (initcall_type *fn = &__initcall_start_+1; fn < &__initcall_end_; fn++) { \
          if (fn->fn_minit) { \
          (fn->fn_minit)(); } \
      } \
    }

static void test0 (void) 
{
	printf("test0 method1 initialize\r\n");
}
INITCALL_LIST(test0);

static void test1 (void) 
{
	printf("test1 method1 initialize\r\n");
}
INITCALL_LIST(test1);

INITCALL_START;
INITCALL_END;

int main(void)
{
	SystemInitialize();
	INITIALISE_ALL; //初始化函数列表
	
	while(1)
	{
		
	}
}

(2)查看map文件,.init.1段存放2个函数指针所以占8个字节

(3)运行结果

方法二:

(1)keil中链接脚本的修改步骤

 (2)打开.sct源文件

 (3)修改.sct文件,添加.init.1字段,大小为0x100

 (4)代码实现

typedef void (*initcall_t)(void);
typedef struct
{
    initcall_t fn_minit;
} initcall_type;


#define INITCALL_LIST(ifn) \
 static initcall_type __initcall_##ifn  __attribute__((used, section(".init.1"))) = {ifn}

extern uint32_t Image$$MY_INITCALL$$Base;
extern uint32_t Image$$MY_INITCALL$$Limit;
initcall_type *__initcall_start_ = (initcall_type *)&Image$$MY_INITCALL$$Base;//获得段起始地址
initcall_type * __initcall_end_ =  (initcall_type *)&Image$$MY_INITCALL$$Limit;//获得结束段地址

#define INITIALISE_ALL \
    { for (initcall_type *fn = __initcall_start_; fn < __initcall_end_; fn++) { \
          if (fn->fn_minit) { \
          (fn->fn_minit)(); } \
      } \
    }

static void test0 (void) 
{
	printf("test0 method2 initialize\r\n");
}
INITCALL_LIST(test0);

static void test1 (void) 
{
	printf("test1 method2 initialize\r\n");
}
INITCALL_LIST(test1);


int main(void)
{
	SystemInitialize();
	INITIALISE_ALL; //初始化函数列表
	
	while(1)
	{
		
	}
}

(5)查看map文件

 (6)运行结果

三、总结

虽然是学习section关键字的用法,但是在实现初始化列表的过程中还设计了许多知识点,比如#define宏定义的巧妙使用、单片机内存管理相关知识、以及ARM脚本知识等,后面会写一篇关于单片机内存管理的文章。

参考文章:

STM32初始化函数的另外一种写法_连志安的博客的博客-CSDN博客

C关键字section的作用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值