STM32F10x启动文件详解

2023-05-16


本文为使用汇编开发STM32系列文章之----启动文件详解篇,全部文章目录点此跳转。


  本文不会像其他文章一样只是简单的说一下启动文件的每个部分是什么,说了很多却又像没说一样。本文将对启动文件中的每句话的作用及其如此编写的原因进行深入探究,使大家真正的理解和掌握启动文件的编写,而不是只懂修改堆栈大小。

目录

  • 一、基础设定
    • 1.Cortex-M3内核的复位序列
    • 2.向量表
    • 3.AAPCS协定
  • 二、启动文件 startup_stm32f10x_md.s 详解
    • 1.文件头
    • 2.堆栈空间分配
    • 3.向量表段
    • 4.代码段
      • 4.1 第一段
      • 4.2 第二段
      • 4.3 第三段

一、基础设定

  在开始正式解释STM32F10x启动文件前,应首先对其Cortex-M3内核的复位序列以及中断向量等设定进行说明,才可能充分理解启动文件中每句代码的必要性。

1.Cortex-M3内核的复位序列

  Cortex-M3内核上电后,首先对系统进行复位操作,保证初始状态的正确。离开复位状态后,首先要做的两件事是取出栈顶(MSP)的初始值以及程序计数器(PC)的初始值。Cortex-M3规定,在地址0x00000000处存放32位的栈顶(MSP)初始值,在地址0x00000004处存放32位的程序计数器(PC)初始值。实际流程如下图所示。
在这里插入图片描述
  需要说明的是,因为每个存储单元大小为8位,一个32位的值占用4个存储单元也就是4个地址偏移,所以栈顶(MSP)初始值和程序计数器(PC)初始值实际上是在存储单元上连续存放的。

2.向量表

  Cortex-M3内核拥有11个系统异常和最多240个外部中断,这些都是可以在代码执行的任何阶段对其打断,并进行异常的处理。每当发生异常时,异常产生部分会返回给Cortex-M3内核一个编号,每个编号对应着固定的异常,以此可以判断所产生的是哪一个异常。
  而,向量表中记录的就是每个异常发生后应该跳转到的代码执行地址-----即中断服务函数地址。通过固定向量表中异常的位置,加上发生中断时的编号(n),即可在发生异常时通过向量表的基地址(base_add)加偏移地址计算出中断服务函数的地址(add)存放位置:add = bas_add + n * 4。
  STM32f10xxx固定的向量表顺序如下所示,其中开始执行时的地址是固定的,但是在运行开始以后,向量表是可以移动至其他位置的。(因为在flash中是无法在程序中更改向量地址的,则可以在运行开始后将向量表移动至ram中,即可随时修改向量地址。)

优先级名称地址说明
--0x00000000保留,为迎合Cortex-M3内核设定,此处放置MSP初值
-3 (固定)Reset0x00000004复位
-2 (固定)NMI0x00000008不可屏蔽中断
-1 (固定)HardFault0x0000000C硬件失效
0 (可设置)MemManage0x00000010存储管理
1 (可设置)BusFault0x00000014预取指失败或存储器访问失败
2 (可设置)BusFault0x00000018预取指失败或存储器访问失败
--0x0000001C-0x0000002B保留
3 (可设置)SVCall0x0000002C通过SWI指令的系统服务调用
4 (可设置)DebugMonitor0x00000030调试监控器
--0x00000034保留
5 (可设置)PendSV0x00000038可挂起的系统服务
6 (可设置)SysTick0x0000003C系统嘀嗒定时器
7 (可设置)WWDG0x00000040窗口定时器中断
8 (可设置)PVD0x00000044连到EXTI的电源电压检测(PVD)中断
9 (可设置)TAMPER0x00000048侵入检测中断
10 (可设置)RTC0x0000004C实时时钟(RTC)全局中断
11 (可设置)FLASH0x00000050闪存全局中断
12 (可设置)RCC0x00000054复位和时钟控制(RCC)中断
13-17 (可设置)EXTI0至EXTI40x00000058至0x00000068EXTI线0至4中断
18-24 (可设置)DMA1至DMA70x0000006C至0x00000084DMA1 通道1 至 通道7 全局中断
0x00000088至0x00000012C其他25到66优先级的中断

上表并不完全,因为篇幅原因,就不放完整向量表了。想看完整向量表的可以查看文档《STM32F10xxx中文参考手册》,或者点此查看。

3.AAPCS协定

  在进行STM32的编程时,使用的都是C语言。但是芯片上电后首先执行的是启动文件,是汇编语言编写的,之后由汇编语言环境跳转到C语言环境进行执行。在某些情况下也需要从C语言环境跳转到汇编语言执行一些C语言做不到的事情。由此就产生了两种新的环境转换情景,那么就需要两个环境在进行转换时按照一定的约束或者说是规则,保证跳转后可正常执行,保证还可正常跳转回之前状态。
  则AAPCS(ARM Architecture Procedure Call Standard)诞生了,即 “ARM架构程序调用标准” 。其中约定了调用函数时的参数、返回值以及某些寄存器在C语言环境下的作用等等。
  我们知道在汇编语言环境下可以访问的通用寄存器有R0-R15,除了一些特殊功能寄存器例如R13(MSP/PSP)、R14(LR)以及R15(PC)外,其他寄存器一般都可用于存储计算过程的数据使用。但是在C语言环境下,有函数类型代码,其包含参数和返回值,则AAPCS规定了其R0-R15的使用方式。其中R0-R4用于传递参数和返回结果,R4-R11用于保存函数内部的局部变量,R12定义为 “程序调用过程中备份寄存器”,其他的特殊寄存器则功能不变。两个环境下寄存器功能如下图所示。
在这里插入图片描述

二、启动文件 startup_stm32f10x_md.s 详解

  因为stm32的启动文件具有一般性,本文将startup_stm32f10x_md.s文件作为解释对象。由于本人水平有限,难免会有错误之处,欢迎指正。

1.文件头

  文件开头比较好理解,是ST官方编写者做的版权声明和文件功能的说明,翻译后如下所示。

;******************** (C) 版权所有 2011 STMicroelectronics ********************
;* 文件名称           : startup_stm32f10x_md.s
;* 作者               : MCD 应用团队
;* 版本               : V3.5.0
;* 日期               : 2011年3月11日
;* 说明               : 基于MDK-ARM工具链的STM32F10x中容量器件矢量表
;*                      这个文件执行:
;*                      - 初始化 SP(堆栈)
;*                      - 初始化 PC(程序指针)指向 Reset_Handler
;*                      - 设置除ISR地址外的向量表条
;*                      - 配置时钟系统
;*                      - 在C库中分支到__main(最终调用main())
;*                      重置后,CortexM3处理器处于线程模式,优先级为特权,堆栈设置为Main。
;* <<< 使用上下文菜单中的配置向导 >>>   
;*******************************************************************************
;本固件仅供参考,旨在为客户提供有关其产品的编码信息,以节省时间。
;因此,意法半导体公司不对因此类固件内容和/或客户使用本文中包含的
;与其产品相关的编码信息而引起的任何直接、间接或后果性损害承担责任。
;*******************************************************************************

2.堆栈空间分配

  以下为栈空间的配置,为ram上的一段连续空间(RAM 地址0x2000 0000开始),用于在寄存器不够用时保存数据使用,常用的指令PUSH就是入栈操作,将数据临时保存到这段空间去,POP是将栈内的某些数据取出来。保存数据时,是由高地址向低地址增长。

; <h> 配置栈
;   <o> 栈大小(以字节为单位)可以在 <0x0-0xFFFFFFFF:要保证8字节对齐>范围内
; </h>

Stack_Size      EQU     0x00000600

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

  ①第一句中 “EQU” 指令是一个伪指令,用于告诉编译器,在编译时,把此句后面的所有符号 “Stack_Size” 替换为值 0x00000600。相当于C语言中的#define的功能。
  ②第二句中 “AREA” 指令用于定义一个新的数据段或者代码段,编译器在编译时,会把这部分的代码编译为一个独立的部分,之后在链接时按段按需链接。
  后面的一些参数表示,段名为STACK,NOINIT为此段的内存空间不进行初始化,READWRITE表示此段为可读可写的数据,ALIGN=3表示此段的数据为2的3次方,即8字节对齐。
  ③第三句中 “SPACE” 指令用于分配一段连续的内存空间,空间大小为 Stack_Size = 0x00000600。“Stack_Mem” 则为标签,是 “SPACE” 指令分配的内存空间的首地址,可以用来跳转以及位置定位。
  ④第四句中 “__initial_sp” 也为标签,是 “SPACE” 指令分配的内存空间的末地址,可以用来跳转以及位置定位。

  以下为堆空间的配置,与栈类似,也是ram上的一段连续空间,用于使用C库中的内存管理函数使用,分配的空间可供数据存放,所以在不使用C库中的内存管理时,则可以不分配堆空间。与栈不同的是,保存数据时,是由低地址向高地址增长。

; <h> 配置堆
;   <o>  堆大小(以字节为单位)可以在 <0x0-0xFFFFFFFF:要保证8字节对齐>范围内
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

  堆的配置代码与栈的代码基本一致。“EQU” 告诉编译器,在编译时,把此句后面的所有符号 “Heap_Size” 替换为值 0x00000200,相当于C语言中的#define的功能。“AREA” 指令用于定义一个新的数据段或者代码段,编译器在编译时,会把这部分的代码编译为一个独立的部分,之后在链接时按段按需链接。后面的一些参数表示,段名为HEAP,NOINIT为此段的内存空间不进行初始化,READWRITE表示此段为可读可写的数据,ALIGN=3表示此段的数据为2的3次方,即8字节对齐。“SPACE” 指令用于分配一段连续的内存空间,空间大小为 Heap_Size = 0x00000200。“__heap_base” 和 “Heap_Mem” 则为标签,是 “SPACE” 指令分配的内存空间的首地址,可以用来跳转以及位置定位。“__heap_limit” 也为标签,是 “SPACE” 指令分配的内存空间的末地址,可以用来跳转以及位置定位。

3.向量表段

  所谓的向量表,其实就是按规定格式放置一些中断服务函数的入口地址,以在发生中断时CPU能够对应中断跳转到中断服务函数进行相应的处理,下面是实际代码。

; 在重置时映射到地址0的向量表
; 实际上就是一些中断服务函数
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; 栈顶
                DCD     Reset_Handler              ; Reset				处理程序
                DCD     NMI_Handler                ; NMI				处理程序
                DCD     HardFault_Handler          ; Hard Fault			处理程序
                DCD     MemManage_Handler          ; MPU Fault			处理程序
                DCD     BusFault_Handler           ; Bus Fault			处理程序
                DCD     UsageFault_Handler         ; Usage Fault		处理程序
                DCD     0                          ; 保留
                DCD     0                          ; 保留
                DCD     0                          ; 保留
                DCD     0                          ; 保留
                DCD     SVC_Handler                ; SVCall 				处理程序
                DCD     DebugMon_Handler           ; Debug Monitor 	处理程序
                DCD     0                          ; 保留
                DCD     PendSV_Handler             ; PendSV 				处理程序
                DCD     SysTick_Handler            ; SysTick 				处理程序

                ;cortex-m3内核的外部中断源
                DCD     WWDG_IRQHandler            ; 窗看门狗
                DCD     PVD_IRQHandler             ; 电压检测
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 7
                DCD     ADC1_2_IRQHandler          ; ADC1_2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; 高优先级USB  或  CAN1的TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; 低优先级USB  或  CAN1的RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; 外部中断线 9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 触发器和变换
                DCD     TIM1_CC_IRQHandler         ; TIM1 捕获比较
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 事件
                DCD     I2C1_ER_IRQHandler         ; I2C1 错误
                DCD     I2C2_EV_IRQHandler         ; I2C2 事件
                DCD     I2C2_ER_IRQHandler         ; I2C2 错误
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; 外部中断线 15..10
                DCD     RTCAlarm_IRQHandler        ; RTC 通过外部中断线报警
                DCD     USBWakeUp_IRQHandler       ; USB 从挂起唤醒
__Vectors_End
__Vectors_Size  EQU  __Vectors_End - __Vectors

①首先第一句还是定义一个段,名称为RESET,属性为数据段、并且是只读的。因为是只读的,所以可以知道这个段在编译时就会被分配到flash处。
②第2-4句,是声明全局标签,告诉其他段要找 __Vectors、__Vectors_End 和 __Vectors_size 的话可以来这里找。然后可以看到的是,__Vectors 和 __Vectors_End 分别在这个向量表的最前方和最后方,标签代表位置,即地址,则可知 __Vectors 是向量表的首地址,__Vectors_End 是向量表的末地址。最后一句话 “__Vectors_Size EQU __Vectors_End - __Vectors”,表示__Vectors_Size 是 __Vectors_End 的值减去 __Vectors 的值,则 __Vectors_Size 是整个向量表占用的大小。
③剩下的就全是 “DCD…” 的代码了,DCD指令表示在存储器上分配一片连续的字存储单元,并把 DCD 后面跟的值赋值到刚分配的存储单元内。在stm32上,字的大小是32位。
  这段DCD代码需要着重说的是前两句。第一句 “DCD __initial_sp” ,此处是分配32位的空间大小,并放置栈空间栈顶的地址。第二句 “DCD Reset_Handler” ,分配32位的空间大小,并放置复位中断服务函数的地址。因为此段代码是在flash上分配空间,且是第一次分配,则第一句分配的地址一定是0x0000 0000,并放置__initial_sp的值,第二句的地址一定是0x0000 0004,放置 Reset_Handler 的值。前面 “Cortex-M3内核的复位序列” 说过,Cortex-M3内核上电复位后,会从0x0000 0000处取出MSP值,也就是栈顶值。会从0x0000 0004中取出PC值,也就是程序指针计数器的值。那么,此时PC的值则会对应Reset_Handler,PC的作用就是指向下一次程序执行的地址。则stm32在运行时会首先跳转到Reset_Handler函数处开始程序的执行。
  而其他的DCD是用来分配其他stm32所有的中断服务函数,而且是固定的顺序,不可改变。

4.代码段

   代码段也只是将所有的中断服务函数列出,发生中断时,完成最基本的死循环行为,防止因为中断异常而对设备造成损害。对于代码段我把它分成三部分讲解,下面是第一段。

4.1 第一段

                 AREA    |.text|, CODE, READONLY

;上电复位的处理程序
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

; 异常处理程序,全是弱定义,可重新自定义 (下面的无限循环可自行修改)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP
MemManage_Handler\
                PROC
                EXPORT  MemManage_Handler          [WEAK]
                B       .
                ENDP
BusFault_Handler\
                PROC
                EXPORT  BusFault_Handler           [WEAK]
                B       .
                ENDP
UsageFault_Handler\
                PROC
                EXPORT  UsageFault_Handler         [WEAK]
                B       .
                ENDP
SVC_Handler     PROC
                EXPORT  SVC_Handler                [WEAK]
                B       .
                ENDP
DebugMon_Handler\
                PROC
                EXPORT  DebugMon_Handler           [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

  这一段包含的是全部的系统必要中断服务函数,属于系统内核级的。首先第一句话,还是老样子,定义了一个名叫 |.text| 的 代码 段,并且是只读的。下面就是10个中断服务函数,Reset_Handler、NMI_Handler、HardFault_Handler、MemManage_Handler、BusFault_Handler、UsageFault_Handler、SVC_Handler、DebugMon_Handler、PendSV_Handler、SysTick_Handler。每个函数都是由标签指明函数名,PROC表示函数的开始,ENDP表示函数的结束,中间使用 EXPORT 指令声明全局标签,可供其他段使用时来此寻找。指令最后加 [WEAK] 表示,此声明是弱声明,如果有其他相同函数定义,其他的函数有效,此弱声明函数作废。除 Reset_Handler 中断服务函数外,每个函数内部都是 “B .” ,指令B是跳转,B后加点,表示跳转地址是本指令地址,即死循环。
  其中 “IMPORT …” 跟 EXPORT 指令正好相反,EXPORT 是声明外部全局标签,而 IMPORT 引入外部全局标签。例如 “IMPORT __main” 是将外部其他段的 __main 函数链接到此处,“IMPORT SystemInit” 是将外部其他段的 SystemInit 函数链接到此处,在本段中需要跳转至 __main 函数 和 SystemInit 函数时,编译器可以找到函数原型的位置。
  其中 Reset_Handler 是stm32复位后执行的第一个中断服务函数,函数中的程序会起到很重要的作用。其中:
① “LDR R0, =SystemInit” 是将函数 SystemInit 的地址放到寄存器 R0中保存。
② “BLX R0” 是跳转到 R0 寄存器存储的地址处运行。B是直接跳转指令,B后加L表示保存PC的值到寄存器 R14,可以在执行完跳转全部指令后返回跳转前的位置。B后加X是表示根据跳转的地址改变当前的状态,地址的最低位 add[0] 若是1,则将存储器状态更改为 Thumb 状态,反之地址的最低位 add[0] 若是0,则将存储器状态更改为 ARM 状态。( 注意Cortex-M3内核不允许进入ARM 状态,否则将会产生一个硬件异常中断 HardFault_Handler )。
  则执行完此句后,程序将跳转到 SystemInit()函数执行系统初始化的操作,执行完后回到此处继续执行下面的第三句话。
③ “LDR R0, =__main” 同样的,此句是将 __main 函数的地址放到寄存器 R0中保存。( 注意: __main 和 main 不是一个函数。__main 是库函数,用于由非C语言环境转换为C语言环境时使用的,C语言环境配置函数 )
④ “BX R0” 是跳转到 R0 寄存器存储的地址处运行。B是直接跳转指令,B后未加L,则执行完跳转后不会返回到此处。B后加X是表示根据跳转的地址改变当前的状态,地址的最低位 add[0] 若是1,则将存储器状态更改为 Thumb 状态,反之地址的最低位 add[0] 若是0,则将存储器状态更改为 ARM 状态。
  则执行完此句后,程序将跳转到 __main() 函数执行C语言环境初始化的操作,包括堆栈、寄存器等的设置,初始化完成后,将会跳转至咱们常见的 main() 函数,自此进入C语言的世界一去不复返。

4.2 第二段

  接下来这段看起来就比较简单了,是其他全部外设正常中断服务函数的集合,因为此种中断不会产生严重后果,在默认的情况下,可以使其为同种功能。则下方就是将全部的外设正常中断服务函数写为了一个函数。如下。

Default_Handler PROC
                EXPORT  WWDG_IRQHandler            [WEAK]
                EXPORT  PVD_IRQHandler             [WEAK]
                EXPORT  TAMPER_IRQHandler          [WEAK]
                EXPORT  RTC_IRQHandler             [WEAK]
                EXPORT  FLASH_IRQHandler           [WEAK]
                EXPORT  RCC_IRQHandler             [WEAK]
                EXPORT  EXTI0_IRQHandler           [WEAK]
                EXPORT  EXTI1_IRQHandler           [WEAK]
                EXPORT  EXTI2_IRQHandler           [WEAK]
                EXPORT  EXTI3_IRQHandler           [WEAK]
                EXPORT  EXTI4_IRQHandler           [WEAK]
                EXPORT  DMA1_Channel1_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel2_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel3_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel4_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel5_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel6_IRQHandler   [WEAK]
                EXPORT  DMA1_Channel7_IRQHandler   [WEAK]
                EXPORT  ADC1_2_IRQHandler          [WEAK]
                EXPORT  USB_HP_CAN1_TX_IRQHandler  [WEAK]
                EXPORT  USB_LP_CAN1_RX0_IRQHandler [WEAK]
                EXPORT  CAN1_RX1_IRQHandler        [WEAK]
                EXPORT  CAN1_SCE_IRQHandler        [WEAK]
                EXPORT  EXTI9_5_IRQHandler         [WEAK]
                EXPORT  TIM1_BRK_IRQHandler        [WEAK]
                EXPORT  TIM1_UP_IRQHandler         [WEAK]
                EXPORT  TIM1_TRG_COM_IRQHandler    [WEAK]
                EXPORT  TIM1_CC_IRQHandler         [WEAK]
                EXPORT  TIM2_IRQHandler            [WEAK]
                EXPORT  TIM3_IRQHandler            [WEAK]
                EXPORT  TIM4_IRQHandler            [WEAK]
                EXPORT  I2C1_EV_IRQHandler         [WEAK]
                EXPORT  I2C1_ER_IRQHandler         [WEAK]
                EXPORT  I2C2_EV_IRQHandler         [WEAK]
                EXPORT  I2C2_ER_IRQHandler         [WEAK]
                EXPORT  SPI1_IRQHandler            [WEAK]
                EXPORT  SPI2_IRQHandler            [WEAK]
                EXPORT  USART1_IRQHandler          [WEAK]
                EXPORT  USART2_IRQHandler          [WEAK]
                EXPORT  USART3_IRQHandler          [WEAK]
                EXPORT  EXTI15_10_IRQHandler       [WEAK]
                EXPORT  RTCAlarm_IRQHandler        [WEAK]
                EXPORT  USBWakeUp_IRQHandler       [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler

                B       .

                ENDP

  可以看到,此中断服务函数的标签名称为 “Default_Handler”,意思为默认的中断函数。与其他函数相同,都是由 PROC 指令开始,ENDP 指令结束。函数开始,将包含的全部外设中断函数使用 EXPORT 指令向全局外弱声明,再给我给每个函数起一个指定名称的标签,因为是连续放置,则这些标签的地址全部相同。此函数内部的实际功能也是 “B .” ,B后加点表示始终跳转到本指令,即死循环。

4.3 第三段

  此段是一些关于处理由汇编转到C环境的相关配置,如下。

                ALIGN
;*******************************************************************************
; 用户栈和堆初始化
;*******************************************************************************
                 IF      :DEF:__MICROLIB           ;如果使用了MICROLIB(C库)
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap
                 
__user_initial_stackheap
                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR

                 ALIGN

                 ENDIF

                 END

;******************* (C) 版权 2011 STMicroelectronics *****文件结束*****

  可以看到,第一句话是 ALIGN 指令,ALIGN 为字节对齐指令,后边未跟数字,表示默认的4字节对齐,即从此指令开始,如果地址不是4字节对齐的,将会填充一些空数据,使下一条指令从四字节对齐处开始。再下方是一个 IF-ELSE-ENDIF 结构,IF后跟着 “ :DEF:__MICROLIB ” ,表示如果使用了C语言的标准库函数,则执行 IF 和 ELSE中间的指令,否则执行ELSE 和ENDIF中间的指令。是否使用C语言的标准库函数,再keil中的魔术棒中设置,如下。
在这里插入图片描述
  从代码中的两种情况可以看到,如果使用了C语言的标准库函数,只需要将 标签 __initial_sp、__heap_base 和 __heap_limit 向外声明为全局标签即可,在 __main() 函数中就会自动将C语言环境配置好。如果未使用C语言的标准库函数,则需要编写一个初始化堆栈的函数,标签为 __user_initial_stackheap ,并将其声明为外部全局标签,以供 __main() 函数在初始化C语言环境时使用。其中 __user_initial_stackheap 函数中的 R0、R1、R2 和 R3 寄存器,由前面说过的 AAPCS协定 所规定,按其编写即可。
  最后是指令 END ,代表整个代码的结束处,与其对应的是整个代码的入口处 ENTRY 指令,但是 ENTRY 指令并未在启动文件中出现。其实 ENTRY 指令是在 __main() 函数中的,真正的入口处也是在 __main() 函数中。


  至此,启动文件全部解释完毕,每一句的原因和意义都说清楚了。因为本人水平有限,如果错误,欢迎指正。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32F10x启动文件详解 的相关文章

  • YOLOV1个人理解总结

    YOLO是著名的端到端目标检测框架 xff0c 最大的特点是检测快 xff0c 亲自试验YOLOv3 v5 v5在jeston Xavier NX上可以达到15FPS左右 xff0c 检测精度也相当可观 xff0c 可以达到实时检测 xff
  • STM32HAL库-USART-调试串口(大小端测试)

    概述 本篇文章介绍如何使用STM32HAL库 xff0c USART 调试串口 xff08 大小端测试 xff09 示例 硬件 xff1a STM32F103CBT6最小系统板 软件 xff1a Keil 5 29 43 STM32Cube
  • Windows下实现C语言Socket通讯TCP传送结构体信息

    版权声明 xff1a 本文为博主原创文章 xff0c 未经博主允许不得转载 Windows下实现C语言Socket通讯 lt TCP传送结构体信息 gt 首先socket通信的步骤 xff1a 三次握手协议 三次握手协议 代码 服务器端 i
  • #STC8A8K# #STC8F1K# #STC8G1K# #STC8H1K# ——STC8系列单片机整体评价

    成就更好的自己 声明 xff1a 这不是广告 xff0c 只是发表看法和吐槽 xff0c 不吹不擂 xff1b 本文以STC8A8K64S4A12 为例 xff0c 简要分析一下STC8系列 xff1b STC 宏晶科技 公司作为国内的一家
  • 拓展模块使用教程和心得(三):ULN2003模块与28BYJ48步进电机(测试平台:STC8A8K,STM32F103)

    成就更好的自己 关于ULN2003模块和对应的28BYJ48步进电机 xff0c 网上现有的资料与教程对于初学者有点不太友好 xff0c 造成的现象是仅仅只能让步进电机转起来 xff0c 但是没有对于控制转速和转角的实践分析 xff0c 本
  • intellij 从2020升级到2023 踩坑实录

    1 下载新版本intellij 工作机器上的intellij版本为2020社区版 xff0c 版本比较老旧 xff0c 需要进行升级 IDE这种提高生产力的工具 xff0c 还是蛮重要的 xff0c 也是值得稍微多花点时间研究一下的 升级之
  • 拓展模块使用教程和心得(五):HC08及HC系列蓝牙模块

    成就更好的自己 今天在做单片机的数据采集并给PC处理 xff0c 根据前期的想法本来是要使用nRF24L01进行的 xff0c 奈何nRF无线模块转USB接口太不给力 xff0c 因此使用到了HC08作为发送和接收数据的模块 目录 简介 内
  • ROS四旋翼无人机快速上手指南(3):工作环境文件框架与各功能包整体讲解

    成就更好的自己 这一章节主要讲解无人机平台的控制文件架构和各个功能包组成和用法 uav ws与shell源码链接 xff1a https gitee com theroadofengineers ros uav 目录 主要项目文件架构概述
  • STM32程序不运行与MicroLIB讲解

    成就更好的自己 目录 引言 什么是MicroLIB 不使用Microlib导致卡死的原理 卡死解决办法 xff1a 优化空间测评 引言 先说问题 xff0c 这几天在使用STM32H750调试程序的过程中出现了一些问题 xff0c 博主使用
  • 拓展模块使用教程和心得(7):3irobotix杉川Delta-1A激光雷达的STM32F407HAL驱动

    成就更好的自己 随着现代智能化设备程度的提高 xff0c 激光雷达已经是在智能控制与检测领域的极其常用的智能化传感器 xff0c 今天就来介绍一下3irobotix公司的Delta 1A激光雷达 Delta 1A激光雷达是一款低成本中等性能
  • RT-Thread&&STM32开发系列(2):FAT头文件包含报错与RTT和CubeMX文件兼容问题

    成就更好的自己 近期的项目需要在RTT下 xff0c 使用SD卡运行FAT文件系统 xff0c 而且还能通过USB将这个SD卡虚拟化成大容量可存储设备 xff0c 在项目初期遇到了一些小问题 xff0c 记录一下 xff0c 防止别的朋友踩
  • [已解决] RTT 串口DMA接受 开头丢失一整包的问题

    起因 项目需求需要设备通过串口的方式发送信息 xff0c STM32F4将串口的信息接收并保存到SD卡中 xff1b 通过XCOM串口助手发送文件的方式进行功能测试 xff0c 测试的时候发现总会丢弃一部分头 项目需要两路串口都保存到SD卡
  • 用实际的激光雷达和里程计运行gmapping历程

    我用的激光雷达是LMS1XX系列的 xff0c 该系列的激光雷达有ROS下的驱动程序可以到网上下载编译 里程计是自己机器人发布的信息 一开始以为自己得到了这两个信息就能运行gmapping了 xff0c 结果发现自己太天真 上网查了一下原因
  • 无数四轴的航模常识

    KV 值 无刷电机KV值定义为 转速 V xff0c 意思为输入电压增加1伏特 xff0c 无刷电机空转转速增加的转速值 例如KV1000的无刷电机 xff0c 代表电压为11V的时候 xff0c 电机的空转转速为11000转 分 KV值越
  • 5G系统的性能指标

    ITU R制定了5G系统的8个性能指标和3种应用场景 流量密度 xff1a 10Tbit s km 2 连接数密度 xff1a 10 6每平方千米 时延 xff1a 空口1ms 能效 xff1a 相对于4G xff0c 100倍提升 用户体
  • find命令用法小结

    0 前言 不管我决心如何组织文件 xff0c 似乎总有无法找到文件的时候 有时是因为我不记得最初的文件名 xff0c 其他时候 xff0c 我知道名字 xff0c 但我不记得在哪里保存它了 甚至有时我需要一个我最初就没有创建的文件 但是 x
  • 在EBAZ4205 zynq7010上运行AXI_DMA中断回环测试

    在EBAZ4205 zynq7010上运行AXI DMA loop interrupt 整体的布局图 这是上面的一张接口图 下面对每个模块附上截图 AXI DMA的输出mm2s introut s2mm introut接到PS系统的中断触发
  • http 方式请求pos请求和get请求,认证方式 用户名密码

    利用apache包请求get和post请求 xff0c 用户名和密码认证 xff0c 用户名密码填则认证 xff0c 不填测不认证 利用maven构建项目 需要导入一个包 lt dependency gt lt groupId gt org
  • jenkins部署jar包到远程服务器

    jenkins部署jar包到远程服务器 1 安装Publish Over SSH插件2 配置ssh以及jenkins的ssh配置3 jenkins任务的配置4 总结 一 xff0c 安装Publish Over SSH插件 xff0c 进入
  • java把文件流转base64,然后前端展示base64图片

    一 xff0c java端 1 xff0c 项目是基于springboot的 读取本地图片 xff0c 转成base64编码字节数组字符串 xff0c 传到前端 2 xff0c 这种传输图片的方式可以用于Java后台代码生成条形码二维码 x

随机推荐