发布时间:2023-12-19 11:00
代码由来:STM32CUBEMX-linux版V6.5.0 + STM32Cube_FW_F4_V1.27.0 + Makefile方式
目标SOC:STM32F407ZET6
STM32链接脚本
中容量STM32处理器启动代码的理解
剖析startup_stm32f407xx.s文件
Assembler.pdf
在嵌入式系统中,启动文件是整个系统非常关键的部分,它会进行一些底层的初始化。在此阶段,C语言是没法运行的,所以需要用到汇编语言编写的启动文件去构建C语言程序运行的必要环境,然后再跳转到main函数到达C语言的世界。
Makefile工程和KEIL工程下的启动文件内容上会有差异,主要体现在堆栈大小和位置的定义上,Makefile工程的在链接脚本中,而KEIL工程的可以在启动文件中找到。
上图是startup_stm32f407xx.s启动文件的开头注释,从中看出作了如下几个事情:
1、设置初始SP(R13)
2、设置初始PC(R15)=Reset_Handler
3、设置中断向量表入口地址,并用异常地址初始化向量表
4、在C库中分支到main,最终调用了main函数
继续往下看,
1、.syntax unified:这条指令设置了指令集语法,不必深究,感兴趣的详见文首参考项“Assembler.pdf“,后面提及的汇编指令都可以在这个pdf找到。
2、.cpu cortex-m4:表示CPU架构为cortex-M4
3、.fpu softvfp:选择要装配的浮点单位为 softvfp 软浮点
4、.thumb:这与 .code 16执行相同的动作
5、.global:定义全局符号,对链接文件可见,g_pfnVectors即中断向量表,Defaule_Handler即默认中断
.word 指令表示产生32位的值,从注释中可以看出 _sdata, _edata…的意义和定义的位置在链接脚本中,这些变量后面会用到。
在Reset_Handler上方还有一段注释,这里太长就没有截取出来。大概意思是这部分的代码在芯片复位后,会被调用来执行一些必要的操作,然后再调用main函数。
1、.weak:该指令表示定义符号为弱属性,如果符号不存在,将创建它们,如果有其它的同名强定义(不加.weak),就会被同名强定义替代。
2、.type:该指令用于设置一个符号的类型,%function 表示符号是函数名。
3、ldr sp , = _estack:将 _estack赋值给 SP 堆栈指针寄存器,_estack定义在链接脚本中,纪录着 SRAM 的最高地址。
4、将 .data 段从 Flash 复制到 SRAM 中,猜测是为了更高的执行效率。
5、零填充bss段。
6、跳转到 SystemInit,初始化FPU设置,向量表位置和外部存储器的配置。
7、跳转到__libc_init_array,作用是考虑到可能会使用C++代码,所以需要此函数来初始化一些东西。
8、跳转到main函数。
9、当main函数执行退出后执行 bx lr 跳转到LR寄存器(r14,链接寄存器),这时候就从main函数跳出来了。
10、.size:ELF格式下隐含标识一个段的结束。
注释说明如果处理器接受到一个出乎意料的中断,将会进入到这个死循环中,即Default_Handler
.section .text.Default_Handler表示定义的是.text段中的Default_Handler段,ax表示权限,allocation and execute,表示该节区可分配并且可执行,progbits是type,不深究。
上图为中断向量表部分截图。注释部分,表示中断向量表需要在物理地址0X00000000上,这里可以在编译出的 STM32F407ZET6.map 文件中看出 g_pfnVectors 的起始地址为 0x08000000,刚好为 Flash 的起始地址(定义在链接脚本中),所以的确是在Flash的物理地址0上。
g_pfnVectors后面的程序意思就是以 g_pfnVectors为初始地址,然后依次以字为单位写入相应的数据。
注释部分,表示为每个异常处理都提供了一个弱属性的函数并将其关联到Default_Handler,也就是默认这个弱函数执行与Default_Handler同样的功能。
如果不重新弱别名,那么就是默认执行Default_Handle,反之执行重写的中断服务函数。
至此,startup_stm32f407xx.s的启动代码大致分析完毕。在重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存。然后进入startup_stm32f407xx.s启动文件,所做的重要内容和文件开头注释中所述一致。