stm32函数放入段section中

发布者:骄阳少年最新更新时间:2016-10-05 来源: eefocus关键字:stm32  函数  段section 手机看文章 扫描二维码
随时随地手机看文章
关键字,__attribute__((section)).

对于这样一个需求,不管你写多少个硬件底层初始化函数,我都能通过固定的循环进行执行,是不动的一个状态,这种实现方式,可以通过以下介绍的方式操作。

 

         思路,有两种办法,一种是指定一个段,这个段需要固定,然后,在这个段之间的区域将函数写入进去。一种是直接将函数一直写入,编译器知道写的函数有多少个,调用编译器得到的函数个数来操作,对于写的函数个数同样灵活。

 

 

第一种办法:

         指定段的办法。

 

操作示例:

先定义一个函数类型。

         typedef int (*MyFun)(void);

 

#define INIT_FUN(fn,level) \

         const MyFun __myFun_##fn __attribute__((section(".myFun."level))) = fn

 

让其在初始化动作的时候,写入一个段中,在程序上看起来是一个text文本段了 。

这里有一个知识点,如果这样写的话,后期程序遍历的时候,发现在程序上无法执行初始化的操作,根源是在map文件中:

Section Cross References

 

    startup_stm32f10x_hd.o(RESET) refers to startup_stm32f10x_hd.o(STACK) for __initial_sp

    startup_stm32f10x_hd.o(RESET) refers to startup_stm32f10x_hd.o(.text) for Reset_Handler

    startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(.text) for NMI_Handler

    startup_stm32f10x_hd.o(.text) refers to system_stm32f10x.o(.text) for SystemInit

    startup_stm32f10x_hd.o(.text) refers to entry.o(.ARM.Collect$$$$00000000) for __main

    main.o(.text) refers to printf1.o(i.__0printf$1) for __2printf

    main.o(.text) refers to usart1.o(.text) for USART1_Config

    main.o(.text) refers to main.o(.myFun.0) for __myFun_init_begin

    main.o(.text) refers to main.o(.myFun.7) for __myFun_init_end

    main.o(.myFun.0) refers to main.o(.text) for init_begin

    main.o(.myFun.0s) refers to main.o(.text) for init_fun1

    main.o(.myFun.2) refers to main.o(.text) for init_fun2

    main.o(.myFun.7) refers to main.o(.text) for init_end

 

 

Removing Unused input sections from the image.

 

    Removing startup_stm32f10x_hd.o(HEAP), (512 bytes).

    Removing main.o(.myFun.0s), (4 bytes).

    Removing main.o(.myFun.2), (4 bytes).

    Removing core_cm3.o(.emb_text), (32 bytes).

    Removing dadd.o(.text), (330 bytes).

    Removing dmul.o(.text), (226 bytes).

    Removing ddiv.o(.text), (222 bytes).

    Removing dfixul.o(.text), (48 bytes).

    Removing cdrcmple.o(.text), (40 bytes).

    Removing depilogue.o(.text), (194 bytes).

 

10 unused section(s) (total 1612 bytes) removed from the image.

 

刚开始建立了,但是在程序上没有使用,就给删除了段。

那么这个缘由肯定是由于编译器动了手脚,因此,查看Arm Development Tool可以查到,在RealView Linker User Guide这个栏目下的Section elimination下的unused section elimination中有相关的叙述:

 

Unused section elimination

RealView Compilation Tools for ?Vision Linker User Guide

Version 4.0

Home > Using the Basic Linker Functionality > Section elimination > Unused section elimination

Unused section elimination removes unreachable code and data from the final image. This optimization can be controlled by the --remove, --no_remove, --first, --last, and --keep linker options and, indirectly, with --entry. Use the --info unusedlinker option to instruct the linker to generate a list of the unused sections that have been eliminated.

Unused section elimination is suppressed in those cases that might result in the removal of all sections.

An input section is retained in the final image under the following conditions:

  • if it contains an entry point
  • if it is referred to, directly or indirectly, by a non-weak reference from an input section containing an entry point
  • if it is specified as the first or last input section by the --first or --last option (or a scatter-loading equivalent)
  • if it is marked as unremovable by the --keep option.

Note

Compilers will normally collect functions and data together and emit one section for each category. The linker can only eliminate a section if it is entirely unused.

里面谈到了map文件最后移除了未用到的段。但是可以通过加—keep字段进行保留,让其最后不再删除。

对于本例程的用法是:

         --keep=__myFun*

 

当然了,按照map文件的提示,是将used文件变为unused,进而删除了,那么可以做一个操作:

#define INIT_FUN(fn,level) \

         const MyFun __myFun_##fn __attribute__((section(".myFun."level))) __attribute__((used)) = fn

 

就是加: __attribute__((used))变为显示的使用了这个段,那它就不会被删除了吧,测试可行!!其实这个在linux上可以找到相关的参考。

内核版本linux3.0.1.。

在main.c(init)这个文件中,

有:

extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];

static void __init do_initcalls(void)

{

         initcall_t *fn;

 

         for (fn = __early_initcall_end; fn < __initcall_end; fn++)

                   do_one_initcall(*fn);

}

在init.h中有:

typedef int (*initcall_t)(void);

 

在vmlinux.lds.h中有:

#define INIT_CALLS                                                         \

                   VMLINUX_SYMBOL(__initcall_start) = .;                            \

                   INITCALLS                                                       \

                   VMLINUX_SYMBOL(__initcall_end) = .;

 

#define INITCALLS                                                           \

         *(.initcallearly.init)                                                 \

         VMLINUX_SYMBOL(__early_initcall_end) = .;                            \

       *(.initcall0.init)                                                        \

       *(.initcall0s.init)                                                      \

       *(.initcall1.init)                                                        \

       *(.initcall1s.init)                                                      \

       *(.initcall2.init)                                                        \

       *(.initcall2s.init)                                                      \

       *(.initcall3.init)                                                        \

       *(.initcall3s.init)                                                      \

       *(.initcall4.init)                                                        \

       *(.initcall4s.init)                                                      \

       *(.initcall5.init)                                                        \

       *(.initcall5s.init)                                                      \

         *(.initcallrootfs.init)                                                        \

       *(.initcall6.init)                                                        \

       *(.initcall6s.init)                                                      \

       *(.initcall7.init)                                                        \

       *(.initcall7s.init)

 

很简单,写的函数在段.initcall0.init-----initcall7s.init中,那么遍历的时候,框头框尾,中间函数明显就能调用到。

 

 

然后在init.h中有

#define __init            __section(.init.text)

#define __initdata   __section(.init.data)

#define __exitdata  __section(.exit.data)

#define __exit_call  __used __section(.exitcall.exit)

 

同样在段上加了一个__used 修饰。猜测来的,所以加上了__attribute__((used))

 

 

上代码:

static int init_begin(void)

{

         printf("----fun init start---\r\n");

         return 0;

}

INIT_FUN(init_begin,"0");

 

 

 

static int init_fun1(void)

{

         printf("----fun init fun1---\r\n");

         return 0;

}

INIT_FUN(init_fun1,"0s");

 

 

static int init_fun2(void)

{

         printf("----fun init fun2---\r\n");

         return 0;

}

INIT_FUN(init_fun2,"2");

 

           

 

static int init_end(void)

{

         printf("----fun init end---\r\n");

         return 0;

}

 

INIT_FUN(init_end,"7");

 

上面一系列函数中:

 

init_begin函数和init_end属于框头框尾,遍历时候,就作为边界即可

 

于是,就形成:

const MyFun *vMyFun;

         for( vMyFun =  &__myFun_init_begin; vMyFun <= &__myFun_init_end; vMyFun ++)

         {

                   (*vMyFun)();   

         }

 

从而达到效果。

 

 

 

 

第二种办法:

         只有段的概念,不用计算多少个函数,由编译器来动作即可。

 

typedef int (*FunInit)(void);

#define INIT_FUNCTION(func) \

         FunInit __Fun_##func __attribute__((section("mySection")))  = func

 

void InitFun1(void)

         printf("InitFun1 init\r\n");

}

INIT_FUNCTION(InitFun1);

 

void InitFun2(void)

         printf("InitFun2 init \r\n");

}

INIT_FUNCTION(InitFun2);

 

 

         extern int mySection$$Base;

         extern int mySection$$Length;

 

                   FunInit *initFunc = (FunInit *)&mySection$$Base;

                   int count = (int)(&mySection$$Length)/sizeof(FunInit);

                   while(count--) {

                       (*initFunc)();

                       initFunc++;

                   }

就这样,可以遍历整个段中定义好的函数了。

 

 

代码下载:

         http://download.csdn.net/detail/wit_yuan/9010727中关于section的部分。

关键字:stm32  函数  段section 引用地址:stm32函数放入段section中

上一篇:stm32的DMA空闲中断数据配置
下一篇:stm32接收中文字符,解析中文字符的方法与调试

推荐阅读最新更新时间:2024-03-16 15:13

STM32中关于高电平有效,低电平有效的一点理解
在学习STM32中的过程中,经常会遇到“高电平有效”,“低电平有效”等字眼,初看时很多时候就会从字面上理解,认为高电平有效的意思就是有效电平是高电平,低电平有效的意思就是有效电平是低电平的意思。而实际上,这样的理解是有误的。下面咱们以STM32的定时器中输出比较通道为例: 这幅图实际上就是一个pwm波产生的过程,对定时器不了解的可以去查阅相关手册,现在我们先看图中标号1的输出模式控制器,这里模式是指pwm模式,他的意思就是可以通过配置寄存器TIMx_CCMR1的OC1M两位,来选择pwm的模式,但是关于模式选择,手册中有这样一句话:在向下计数时,一旦TIMx_CNT TIMx_CCR1时通道1为无效电平(OC1REF=0),否
[单片机]
<font color='red'>STM32</font>中关于高电平有效,低电平有效的一点理解
STM32F1系列——固件库函数使用手册摘要
命名 1.缩写 2.函数命名 固态函数库遵从以下命名规则 PPP表示任一外设缩写,例如:ADC。 更多缩写相关信息参阅章节1.1 缩写系统、源程序文件和头文件命名都以“stm32f10x_”作为开头,例如:stm32f10x_conf.h。 常量仅被应用于一个文件的,定义于该文件中;被应用于多个文件的,在对应头文件中定义。所有常量都由英文字母大写书写。 寄存器作为常量处理。他们的命名都由英文字母大写书写。在大多数情况下,他们采用与缩写规范与本用户手册一致。 外设函数的命名以该外设的缩写加下划线为开头。每个单词的第一个字母都由英文字母大写书写,例如:SPI_SendData。在函数名中,只允许存在一个下划线,用
[单片机]
STM32F1系列——固件库<font color='red'>函数</font>使用手册摘要
一文解析STM32启动流程
可执行程序 - cpu执行第一条用户代码 这个流程中着重讲述的是 HEX 文件如何被烧写到 STM32 内部的指定地址处。(烧写到 STM32 中的可执行文件不仅只有 HEX 格式,还有 axf、bin。针对不同格式的可执行文件,用不同的工具进行烧写)。 而本篇文章将要详细地描述一个流程: cpu执行第一条用户代码 - 调用 __main 函数- __rt_entry - main函数 这里需要注意一下,__main 是 c 库中的一个函数,和 main 函数是有区别的!!! 启动文件内容描述 上图中的汇编关键字最好记住,因为比较常用。 在此基础上,我们继续深入一点。 DCD指令 STM32 启动文件中使用 DCD 指令的
[单片机]
一文解析<font color='red'>STM32</font>启动流程
STM32三种启动模式
STM32三种启动模式对应的存储介质均是芯片内置的,它们是: 1)用户闪存 = 芯片内置的Flash。 2)SRAM = 芯片内置的RAM区,就是内存啦。 3)系统存储器 = 芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说的ISP程序。这个区域的内容在芯片出厂后没有人能够修改或擦除,即它是一个ROM区。 在每个STM32的芯片上都有两个管脚BOOT0和BOOT1,这两个管脚在芯片复位时的电平状态决定了芯片复位后从哪个区域开始执行程序,见下表: BOOT1=x BOOT0=0 从用户闪存启动,这是正常的工作模式。 BOOT1=0 BOOT0=1 从系统存储器启动,这种模式启动的程序功能由厂
[单片机]
经典STM32 ADC多通道转换
STM32 ADC多通道转换 描述:用ADC连续采集11路模拟信号,并由DMA传输到内存。ADC配置为扫描并且连续转换模式,ADC的时钟配置为12MHZ。在每次转换结束后,由DMA循环将转换的数据传输到内存中。ADC可以连续采集N次求平均值。最后通过串口传输出最后转换的结果。 程序如下: #i nclude stm32f10x.h //这个头文件包括STM32F10x所有外围寄存器、位、内存映射的定义 #i nclude eval.h //头文件(包括串口、按键、LED的函数声明) #i nclude SysTickDelay.h #i nclude UART_INTERFACE.h #i ncl
[单片机]
STM32定时器输出比较(PWM)
前言: 1.本博文基于ARM Cortex-M3内核的STM32F103ZET6处理器芯片和标准3.5.0库函数; 2.不介绍PWM的基础概念,但是需要知道一点的是,PWM是输出比较的一种特例; 3.如有不足指出,还望前辈多多指教; 4.要想学会这个知识点,必须要掌握下面这位博友写的博客里的几个概念,不然后来很有可能会懵逼; http://blog.sina.com.cn/s/blog_3ba262a10101esd1.html Ⅰ 定时器和PWM (1)8个定时器中,除了TIM6和TIM7,其他定时器都可以产生PWM输出; (2)高级定时器TIM1和TIM8可以同时输出7路PWM(CH1~7,共7个通道),通用定时器同时可产
[单片机]
<font color='red'>STM32</font>定时器输出比较(PWM)
STM32-systick系统定时器
systick系统定时器 系统定时器存在内核中,是24位的定时器,只能向下递减,嵌套在NVIC中 counter 在时钟的驱动下 在reload的初值开始向下递减计时到0,产生中断置位标志然后又从reload值开始重新递减计数,循环 定时时间计算 t=reload*(1/clk) clk=72M时,t=72*(1/72m)=1us clk=72M时,t=72000*(1/72m)=1ms clk=72M时,t=72000000*(1/72m)=1s 1s=1000MS =1000 000US=1000 000 000NS sysTick属于内核中的外设,他的中断优先级和外设的中断优先级相比,哪个
[单片机]
STM32-systick系统定时器
stm32定时器优先级
什么是优先级   优先级是具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。   当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。 stm32定时器优先级   STM32 可以支持的 68 个外部中断通道,已经固定的分配给相应的外部设备。每个中断通道都具备自己
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved