编译
编译流程
STM32 编译的过程如下图所示
bin 与 elf
*.bin
是二进制文件,包含了代码。需要将其烧录至指定的位置(eg. 0x0800 0000
FLASH 地址)。
*.elf
: Executable Linkable Format 。文件中包含了 symbol look-ups
,relocated table
。指定了写入的地址。
link
以下的代码可以在 STM32 各固件库中的工程模板下找到(
TrueStudio
目录下),修改即可使用,无需重新写。
在最后一步连接中,不同的目标文件*.obj
和链接文件*.ld
组成 *.elf
。
MEMORY
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
}
MEMORY
指定了芯片中可用的内存片段。 ORIGIN
:起始长度;LENGTH
:段的长度。r
:readable; w
writable; x
executable.
SECTION
SECTIONS
指定了内存中段的布局。这些段是在启动文件和其他源文件中声明的段。连接器在这里将他们放入内存中不同的区域。link script
内只有唯一的SECTIONS
,其中可以安排不同的段。
.
符号 & *
符号
.
始终保存了目前输出的位置(或者说放置段的地址)。每当使用 .
完成一段的赋值后,.
会自加相应的地址偏移。eg.
SECTIONS
{
output :
{
file1(.text)
. = . + 1000;
file2(.text)
. += 1000;
file3(.text)
} = 0x1234;
}
该代码是把 file1 中 .text 段,放置在开头。然后间隔1000,放置 file2 中 .text 段。再间隔1000,放置 file3 中的 .text 段。
*
代表全选。
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
} >FLASH
先放置所有文件中的 .text
段再放置所有文件中所有含有 .text
字样的段。(比如不同文件中定义的函数之类的。感觉是 .text
的子段,不过这应该只是一种单纯的命名方式。)
入口函数
入口函数有四种选择:
- the `-e’ entry command-line option;
- the
ENTRY(symbol)
command in a linker control script; - the value of the symbol
__start
, if present; - the address of the first byte of the
.text
section, if present; - The address 0.
STM32 中默认入口函数是
__start
,但是 STM32 官方例程会使用Reset_Handler
(复位中断函数)作为入口函数。
示例
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* other segements here */
}
KEEP
代表在这一部分中,代码优化可以省略。ALIGHN(4)
是函数,代表这一段按照4字节边界对齐。>FLASH
将这一段放置在 MEMORY
声明的 FLASH
中。这一块的含义是将 .isr_vector
这个段放在 FLASH
起始的地址处,按照四字节对齐,跳过优化。
可以从启动文件中知道,
.isr_vector
是 STM32 的中断向量表,放置系统的中断处理函数。
可以在 GNU 官方文档 中查找其余函数定义。
编译参数
- Nano libs and Standard C lib
GNU ARM 编译器提供了两套 C 函数库。一个是 newlib
,更完整且优化程度更高。另一个是 newlib-nano
,其在大小方面有删减。编译时,使用 -specs=nano.specs
使用 newlib-nano