我正在使用一款ARM Cortex M3芯片(STM32F2),ST公司提供了一份“标准外设库”。它包含了一些有用的 .c 和 .h 文件以及 .s 文件。
在一个C语言项目中,这些 .s 文件的目的是什么?如何让编译器/链接器等使用它们?
.s扩展名是GNU和许多其他工具链用于汇编文件的约定。
据我所知,STM32标准外设库本身不包含任何汇编文件,但CMSIS库包含各种STM32部件的启动代码,例如startup_stm32f2xx.s是所有STM32F2xx系列设备的启动代码。针对不同的工具链有不同的实现,您需要构建和链接与您特定部件和工具链相关联的文件。如果您正在使用可以构建和运行示例项目或者创建特定于部件的项目的IDE,则可能已经完成了这个步骤 - 如果您有可以运行的代码,那么肯定已经完成了。
如何构建和链接代码取决于您使用的工具链。大多数基于IDE的工具将自动识别扩展名并调用汇编器生成对象文件,该文件将像其他文件一样进行链接。工具链版本之间的确切内容略有不同,但主要创建C运行时环境(堆栈和堆),初始化处理器,定义初始中断/异常向量表,初始化静态数据并跳转到main()。
例如Keil/ARM RealView版本的核心文件如下:
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
Reset_Handler
是处理器重置后程序计数器(PC)寄存器所设置的地址。
SystemInit
是一个外部的C代码函数,用于执行大部分的初始化 - 这可能需要根据你的硬件进行自定义。Cortex-M不同寻常的地方在于它可以在复位后立即开始运行C代码,因为向量表包括了复位地址和初始堆栈指针地址,这些会在复位时自动加载到SP寄存器中。因此,你不需要太多的汇编知识来让它运行起来。
__main()
是编译器提供的C代码入口点。它不是你编写的main()函数,而是在调用你的`main()'函数之前要执行标准库、静态数据、堆的初始化。__main()
完成的大部分工作,但本质上它执行相同的功能。SystemInit()
在system_stm32f2xx.c中定义,并且可能需要根据你的板子进行自定义(正确的晶振频率、PLL设置、外部SRAM配置等)。由于这是C代码,并且有良好的注释,你可能会更容易理解它。它们通常包含汇编代码。汇编器将其转换为目标文件,稍后由链接器与主要内容一起链接。但我想这取决于编译器、工具链等。
.s文件通常包含向量表。它定义了系统在中断发生时应该执行什么操作。这个表(代码)被放置在链接器文件中由您定义的内存地址中。例如,每当重置发生时,处理器应该从哪里开始运行,应该运行什么代码。同样地,还有其他处理程序(中断向量)。在STM32中,通常控制器会循环执行特定的处理程序。
如下面的例子所示:请参见此链接以获取详细说明
.section INTERRUPT_VECTOR, "x"
.global _Reset
_Reset:
B Reset_Handler /* Reset */
B . /* Undefined */
B . /* SWI */
B . /* Prefetch Abort */
B . /* Data Abort */
B . /* reserved */
B . /* IRQ */
B . /* FIQ */
Reset_Handler:
LDR sp, =stack_top
BL c_entry
B .
这个汇编代码稍后将转换为目标文件,并与您的.c文件和.ld链接,以创建.elf或.bin文件。