ARMのCortex-M基盤技術(3) - 原理と応用コンパイルしたカーネル

概要:

  現在開発Keil社に他の開発環境と同様に、一例として説明したので、私は、(keil526)よりKeilの開発ツールを使用します。

1.コンパイラのリンクを定義します。

 どんなに我々はシンプルなコードを書く方法を、あなたは「コンパイル - >リンク」の対象となりませんプロセスを実行可能ファイルを生成します:

  • コンパイラは、彼らはオブジェクトファイルの形で存在しているコンピュータは、バイナリ形式を認識に「翻訳」私たちのソースコードを書くことです。
  • リンクはもちろん、それはすべてのオブジェクトファイルとシステムコンポーネントは単一の実行可能ファイルに結合されているだろう「パッケージ」です。

 埋め込まれた懸念にもかかわらず、C言語コンパイラがあります異なるプラットフォームのための多くの異なるコンパイラているなど、:

  • 共通の下でのWindowsは、MicrosoftのVisual C ++で開発され、一般的に、単独で使用していない、Visual Studioの中で統合されています。
  • LinuxはGCCによって開発された共通GUNの下で、多くのLinuxディストリビューションは、GCCが付属しています。
  • Macは、一般的なLLVM /クランの下でそれはXcodeで統合されている(Xcodeの統合がかつてのGCCで、GCCは後でLLVM /クラン性能がGCCよりも強力で、LLVM /クラン変更されたため、適合しません)。

  Windowsプラットフォームの下で開発されたとき、私たちはほとんど使用の開発ツールは、コンパイラの数を統合するために私たちを助けるためにある、ということを意味し、それを構成するための唯一のインタフェースを必要としています。ここで私は最高のKeil社の開発環境を知りたい、これらのリンクのコンパイラは、ツールを使用する方法です。

Keil社のリンクコンパイルの下に、コンパイラは、コネクタを説明します。

2. Keil社のコンパイラへのリンク

  私たちは、プロジェクトをコンパイルするためにKeil社を使用するときに最初の講演で述べた、ボックス次の出力の内容は以下です:

以下に示すように、\ Keil_v526 \ ARM \ ARMCC \ビン:コンパイラのコンパイラコンパイラは、私たちのコンピュータのディスクDのDを見ることができます:

  armar 是用于把.o 文件打包成 lib 文件,armasm 编译汇编文件 ,armcc 编译 c/c++文件 ,armlink 链接对象文件 ,fromelf 生成下载格式文件,它根据 axf 映像文件转化成 hex 文件,并列出编译过程出现的错误(Error)和警告(Warning)数量 。调用这些编译工具,需要用到Windows的“命令行提示符工具”,为了让命令行方便地找到这些工具,我们先把工具链的目录添加到系统的环境变量中。

2.1 添加环境变量

  Win7 系统为例添加工具链的路径到 PATH 环境变量 。

  (1)右键电脑系统的“计算机图标”,在弹出的菜单中选择“属性” ,如下图

 

 

(2)在弹出的属性页面依次点击“高级系统设置” ->“环境变量”,在用户变量一栏中找到名为“PATH”的变量,若没有该变量,则新建一个。编辑“PATH”变量,在它的变量值中输入工具链的路径,如本机的是“;D:\Keil_v526\ARM\ARMCC\bin”,注意要使用“分号;”让它与其它路径分隔开(英文分号),输入完毕后依次点确定,如下图

(3) 打开 Windows 的命令行,点击系统的“开始菜单”,在搜索框输入“cmd”,在搜索结果中点击“cmd.exe”即可打开命令行,见图

 

 

(4)在弹出的命令行窗口中输入“fromelf”回车,若窗口打印出 formelf 的帮助说明,那么路径正常,就可以开始后面的工作了;若提示“不是内部名外部命令,也不是可运行的程序…”信息, 说明路径不对,请重新配置环境变量,并确认该工作目录下有编译工具链。

  这个过程本质就是让命令行通过“PATH”路径找到“fromelf.exe”程序运行,默认运行“fromelf.exe”时它会输出自己的帮助信息,这就是工具链的调用过程, Keil本质上也是如此调用工具链的,只是它集成为 GUI(界面),相对于命令行对用户更友好,毕竟上述配置环境变量的过程已经让新手烦躁了。解释一下,这个cmd框中的内容怎么和Keil对应起来。fromelf 可根据 axf 文件生成 hexbin 文件, hex和 bin 文件是大多数下载器支持的下载文件格式 。例如如果我们想利用 fromelf 生成 bin 文件,可以在 MDK的“Option for Target->User”页中添加调用 fromelf 的指令,如下图

 

 

还有链接器的配置界面如下:

 

 

 

 那么这些东西有什么用呢?来看一下这段代码:

 1 Reset_Handler   PROC
 2                 EXPORT  Reset_Handler               [WEAK]
 3                 IMPORT  SystemInit
 4                 IMPORT  __main
 5  
 6                 LDR     r0, =errorfunc
 7                 BLX     r0
 8                 LDR     r0, =__main
 9                 BX      r0
10                 ENDP

  启动代码中的Reset_Handler代码,大家可以一眼看出来这段代码是错误的,其中红色字体的errorfunc是我故意填在这里的一个不存在的函数,编译必然报错,那么我们看看编译器是怎么报错的?

 重点在途中蓝色底纹的部分,注意第一个单词:assembling……,然后后面跟着出错信息,没错,这是一个汇编器错误;再看下面,我再代码里面给出一个错误(我在main.c里面删掉了一个头文件):

看蓝色底纹部分,注意第一个单词:compiling……,然后后面跟着出错信息,没错,这是一个编译器错误,跟上面的不同,这是编译器报错,我们再看下面一个错误,代码如下

 1 int main(void)
 2 {
 3     char ch;
 4  
 5     /* Init board hardware. */
 6     /* attach 12 MHz clock to FLEXCOMM0 (debug console) */
 7     CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
 8  
 9     BOARD_InitPins();
10     BOARD_BootClockFROHF48M();
11     BOARD_InitDebugConsole();
12  
13     PRINTF("hello world.\r\n");
14  
15     while (1)
16     {
17         ch = errorfunc();
18         PUTCHAR(ch);
19     }
20 }

看蓝色底纹部分,注意第一个单词:linking……,然后后面跟着出错信息,这次是一个链接器错误!

  大家经常说的编译器报错,其实是几个不同的东西再报错,编译器、汇编器、链接器都会报错,那你可能会问,知道这个有啥用呢?当然是有用的,比如汇编器报错,基本跟C语言没关系,基本可以断定是汇编语言语法错误或者是嵌入C语言的汇编语言出错(在C中嵌入汇编是一种非常有效的编程手段);如果是链接器报错,那就基本跟C语言语法无关,不是你的C语法上出错,很可能是你调用了不存在的函数或者链接器脚本写错了,或者使用了不存在的标号Symbol,或者没有包含头文件.h;而只有编译器报错,才总是你的C写的有问题.
 
3. 工作报表.map文件

   真正有用的链接器描述文件“*.map”非常有用(编译链接后,双击工程列表下的第二个文件夹可以打开,变成灰色的那个)

  “*.map”绝对是你的“核心员工”的工作报表,也是最复杂的一个。它主要包含交叉链接信息,查看该文件可以了解工程中各种符号之间的引用以及整个工程的 Code、 RO-data、 RW-data 以及 ZI-data 的详细及汇总信息。它的内容中主要包含了“节区的跨文件引用”、“删除无用节区”、“符号映像表”、“存储器映像索引”以及“映像组件大小”。

3.1 节区的跨文件引用

 1 Section Cross References
 2 
 3     startup_stm32f10x_hd.o(RESET) refers to startup_stm32f10x_hd.o(STACK) for __initial_sp
 4     startup_stm32f10x_hd.o(RESET) refers to startup_stm32f10x_hd.o(.text) for Reset_Handler
 5     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.NMI_Handler) for NMI_Handler
 6     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.HardFault_Handler) for HardFault_Handler
 7     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.MemManage_Handler) for MemManage_Handler
 8     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.BusFault_Handler) for BusFault_Handler
 9     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.UsageFault_Handler) for UsageFault_Handler
10     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.SVC_Handler) for SVC_Handler
11     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.DebugMon_Handler) for DebugMon_Handler
12     startup_stm32f10x_hd.o(RESET) refers to stm32f10x_it.o(i.PendSV_Handler) for PendSV_Handler

......(省略)

  在这部分中,详细列出了各个*.o 文件之间的符号引用。由于*.o 文件是由 asm 或 c/c++源文件编译后生成的,各个文件及文件内的节区间互相独立,链接器根据它们之间的互相引用链接起来,链接的详细信息在这个“Section Cross References”一一列出。

  解释一下第3行,其他行的解释也差不多,第3行说明的是 startup_stm32f10x.o 文件中的“RESET”节区中的“__initial_sp” 符号(/函数)引用了同文件“STACK”节区(/函数)。这些跨文件引用的符号其实就是源文件中的函数名、变量名 。

3.2 删除无用节区

 1 Removing Unused input sections from the image.
 2 
 3     Removing startup_stm32f10x_hd.o(HEAP), (512 bytes).
 4     Removing core_cm3.o(.emb_text), (32 bytes).
 5     Removing system_stm32f10x.o(i.SystemCoreClockUpdate), (164 bytes).
 6     Removing system_stm32f10x.o(.data), (20 bytes).
 7     Removing misc.o(i.NVIC_Init), (112 bytes).
 8     Removing misc.o(i.NVIC_PriorityGroupConfig), (20 bytes).
 9     Removing misc.o(i.NVIC_SetVectorTable), (20 bytes).
10     Removing misc.o(i.NVIC_SystemLPConfig), (32 bytes).
11     Removing misc.o(i.SysTick_CLKSourceConfig), (40 bytes).
12     Removing stm32f10x_adc.o(i.ADC_AnalogWatchdogCmd), (20 bytes).
13     Removing stm32f10x_adc.o(i.ADC_AnalogWatchdogSingleChannelConfig), (16 bytes).
14     Removing stm32f10x_adc.o(i.ADC_AnalogWatchdogThresholdsConfig), (6 bytes).
15     Removing stm32f10x_adc.o(i.ADC_AutoInjectedConvCmd), (22 bytes).

......(省略)

  这部分列出了在链接过程它发现工程中未被引用的节区,这些未被引用的节区将会被删除(指不加入到*.axf 文件,不是指在*.o 文件删除),这样可以防止这些无用数据占用程序空间。这部分是编译器自动做的,不需要人工参与。

3.3 符号映像表

 

   这个表列出了被引用的各个符号在存储器中的具体地址、占据的空间大小等信息。如我们可以查到 LED_GPIO_Config 符号(0x080002c5)存储在 0x080002c4 地址,它属于 Thumb Code 类型,大小为 90 字节,它所在的节区为 bsp_led.o 文件的 i.LED_GPIO_Config 节区。

3.4  存储器映像索引

 1 Memory Map of the image
 2 
 3   Image Entry point : 0x08000131
 4 
 5   Load Region LR_IROM1 (Base: 0x08000000, Size: 0x000005d4, Max: 0x00080000, ABSOLUTE)
 6 
 7     Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x000005d4, Max: 0x00080000, ABSOLUTE)
 8 
 9     Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object
10 
11     0x08000000   0x08000000   0x00000130   Data   RO            3    RESET               startup_stm32f10x_hd.o
12     0x08000130   0x08000130   0x00000000   Code   RO         3207  * .ARM.Collect$$$$00000000  mc_w.l(entry.o)
13     0x08000130   0x08000130   0x00000004   Code   RO         3210    .ARM.Collect$$$$00000001  mc_w.l(entry2.o)
14     0x08000134   0x08000134   0x00000004   Code   RO         3213    .ARM.Collect$$$$00000004  mc_w.l(entry5.o)
15     
16     ......
17     
18     0x080002c4   0x080002c4   0x00000060   Code   RO         3192    i.LED_GPIO_Config   bsp_led.o
19     0x08000324   0x08000324   0x00000004   Code   RO         3132    i.MemManage_Handler  stm32f10x_it.o
20     0x08000328   0x08000328   0x00000002   Code   RO         3133    i.NMI_Handler       stm32f10x_it.o
21     0x0800032a   0x0800032a   0x00000002   Code   RO         3134    i.PendSV_Handler    stm32f10x_it.o
22     
23     ......
24 
25 
26     Execution Region RW_IRAM1 (Exec base: 0x20000000, Load base: 0x080005d4, Size: 0x00000400, Max: 0x00010000, ABSOLUTE)
27 
28     Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object
29 
30     0x20000000        -       0x00000400   Zero   RW            1    STACK               startup_stm32f10x_hd.o

  本工程的存储器映像索引分为 ER_IROM1 及 RW_IRAM1 部分,它们分别对应 STM32内部 FLASH 及 SRAM 的空间。相对于符号映像表,这个索引表描述的单位是节区,而且它描述的主要信息中包含了节区的类型及属性,由此可以区分 Code、 RO-data、 RW-data 及ZI-data。

  从上面的表中我们可以看到 i.LED_GPIO_Config 节区存储在内部 FLASH 的0x080002c4 地址,大小为 0x00000060,类型为 Code,属性为 RO。而程序的 STACK 节区(栈空间)存储在 SRAM 的 0x20000000 地址,大小为 0x00000400,类型为 Zero,属性为 RW(即 RW-data)。

3.5 映像组件大小 

  map 文件的最后一部分是包含映像组件大小的信息(Image component sizes),这也是最常查询的内容。

  最后一部分列出了只读数据(RO)、可读写数据(RW)及占据的 ROM 大小。其中只读数据大小为 1492 字节,它包含 Code 段及 RO-data 段; 可读写数据大小为 1024 字节,它包含RW-data 及 ZI-data 段;占据的 ROM 大小为 1492 字节,它除了 Code 段和 RO-data 段,还包含了运行时需要从 ROM加载到 RAM的 RW-data数据(本工程中 RW-data数据为 0字节)。其实,在map文件中,连每一个函数的存储空间占用情况都会列出来(符号映像表 )。

总结:综合整个 map 文件的信息,可以分析出,当程序下载到 STM32 的内部 FLASH 时,需要使用的内部 FLASH 是从 0x0800 0000 地址开始的大小为 1492 字节的空间;当程序运行时,需要使用的内部 SRAM 是从 0x20000000 地址开始的大小为 1024 字节的空间。

  如果你真的把这个文件看明白了并能熟练应用,那么恭喜你,你就解锁了一个重要技能包。

 

おすすめ

転載: www.cnblogs.com/39950436-myqq/p/11545140.html