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启动文件详解 的相关文章

  • VUE3入门基础:input元素的type属性值说明

    说明 在Vue 3中 xff0c lt input gt 元素的type属性可以设置不同的类型 xff0c 以适应不同的输入需求 常见的type属性取值如下 xff1a text xff1a 默认值 xff0c 用于输入文本 passwor
  • Docker WIndows最新版(4.17.x)修改镜像存储路径

    一般情况 在 Docker Desktop 4 17 0 版本中 xff0c 可以按照以下步骤来修改 Docker 镜像存储路径 打开 Docker Desktop 应用程序 xff0c 单击顶部菜单栏中的 Docker Desktop 菜
  • Nginx限制请求方式

    在nginx的配置文件中 xff0c 将location 下的IF条件中的 request method参数设置允许的请求类型 仅允许Get server span class token punctuation span span cla
  • Nginx实现一个IP访问总流量限制

    在 Nginx 中 xff0c 可以使用 ngx http limit req module 模块配合 ngx http limit conn module 模块实现基于 IP 的流量和连接数控制 具体来说 xff0c 可以按照如下步骤实现
  • CentOS - node命令not found解决

    背景 xff1a 前几天在阿里云部署了一个node项目 xff0c node版本 v12 10 0 每次重新远程连接 xff0c 输入node v pm2 v 显示 command not found xff1b 每次必须先输入 nvm u
  • 用Java实现天天酷跑(附源码),这个真的有点强了!

    点击上方 阿拉奇学Java xff0c 选择 置顶或者星标 每天早晨00点00分 与你相约 xff01 首先 xff0c 写一个需求文档 xff1a 一 项目名称 xff1a 天天酷跑 xff08 RunDay xff09 二 功能介绍 x
  • SpringBoot开源在线考试系统

    推荐一款在线考试系统 xff1b 基于 SpringBoot 43 Mybatis 43 Shiro 43 mysql 43 redis构建的智慧云智能教育平台 架构上使用完全前后端分离 支持多种题型 xff1a 选择题 多选题 判断题 填
  • 微信闪退Bug罪魁祸首竟是二维码引擎,附源代码分析

    建议别尝试 xff1a 转发这个二维码到群里 xff0c 3秒后你会回来骂我 xff08 抖m求骂 xff09 近日 xff0c 网传微信识别上方二维码就会出现闪退BUG xff0c 小编也忍不住尝试了一下 xff0c 果然 xff0c 一
  • java多线程之线程安全(重点,难点)

    由于操作系统中 线程的调度是抢占式执行的 或者说是随机的 这就造成线程调度执行时 线程的执行顺序是不确定的 虽然有一些代码在这种执行顺序不同的情况下也不会运行出错 但是还有一部分代码会因为执行顺序发生改变而受到影响 这就会造成程序出现Bug
  • React-Native: DatePickerIOS设置选择24小时

    原帖 xff1a https github com mmazzarolo react native modal datetime picker DatePickerIOS选择24小时的方法 xff1a How to set 24 hours
  • 使用chatgpt实现微信聊天小程序(秒回复),github开源(附带链接)

    前言 我在前一段时间突发奇想 xff0c 就使用java来调用chatgpt的接口 xff0c 然后写了一个简单小程序 xff0c 也上了热榜第一 xff0c java调用chatgpt接口 xff0c 实现专属于自己的人工智能助手 xff
  • HttpPost 携带参数的请求方式

    一 HTTP请求 Http的几种请求方式对应程序包中的HttpGet HttpHead HttpPost HttpPut HttpDelete HttpTrace and HttpOptions xff0c 这些类均实现了HttpUriRe
  • mysql 根据时间范围查询

    时间格式为 第一种写法 xff1a select from test where create time between 39 2019 03 05 13 04 07 39 and 39 2019 03 08 13 04 07 39 第二种
  • java 根据条件从List中筛选出符合条件的集合

    1 list 你要在里面筛选的对象集合 存放格式例如 list add user1 list add user2 list add user3 2 xff1a tableColumnName xff1a user 里面的属性字段 xff1a
  • Es QueryBuilder 简单查询

    1 matchQuery String name Object text matchQuery 34 filedname 34 34 value 34 匹配单个字段 xff0c 匹配字段名为filedname 值为value的文档 单个匹配
  • Mysql 查询区分大小写的两种方法

    oracle中查询默认是区分大小写的 xff0c 但是在mysql中默认不区分大小写 解决办法 xff1a mysql可以在SQL语句中加入 binary来区分大小写 BINARY不是函数 xff0c 是类型转换运算符 xff0c 它用来强
  • 微信小程序实现登录页面

    wxml文件 xff1a lt view class 61 34 container 34 gt lt view class 61 34 login icon 34 gt lt image class 61 34 login img 34
  • 锂电池充电IC-TP4056电路设计详解

    首先 xff0c 先介绍下TP4056 TP4056是一款完整的单节锂离子电池采用恒定电流 恒定电压线性充电器 其底部带有散热片的SOP8封装与较少的外部元件数目使得TP4056成为便携式应用的理想选择 TP4056可以适合USB电源和适配
  • mysql无法插入中文的解决办法:修改数据库编码为utf-8

    mysql无法插入中文的解决办法 1 无法插入中文原因 mysql数据库的默认编码是latin1 xff0c 可以使用下面代码查看数据库编码 show variables like 34 character 34 发现有两处的编码是lati
  • 为Debian 10.2 安装图形化桌面环境

    首先先下载x window的内核 xff1a apt get u install x window system core 下载登录管理界面gdm或kdm xff1a apt get u install gdm gdm themes 下载G

随机推荐

  • C# System.BadImageFormatException 解决方法

    出现System BadImageFormatException 异常有两种情况 xff1a 程序目标平台不一致 amp 引用dll文件的系统平台不一致 异常参考 xff1a BadImageFormatException 程序目标平台不一
  • leetcode452

    题目 xff1a 在二维空间中有许多球形的气球 对于每个气球 xff0c 提供的输入是水平方向上 xff0c 气球直径的开始和结束坐标 由于它是水平的 xff0c 所以y坐标并不重要 xff0c 因此只要知道开始和结束的x坐标就足够了 开始
  • UVA-11300

    span class token macro property span class token directive hash span span class token directive keyword include span spa
  • UVA-11520 Fill the Square

    思路 xff1a 因为要求是字典序 xff0c 这道题的第一反应就是从A Z选取字母 xff0c 在正方形中从上到下 xff0c 从左到右这样的顺序去填字母 xff0c 一旦判断这个字母旁边没有和它一样的 xff0c 那么就证明这个字母就是
  • UVA10881 Piotr‘s Ants

    span class token macro property span class token directive hash span span class token directive keyword include span spa
  • -bash: conda: command not found

    其实是因为你没有加路径 执行一条 export PATH 61 PATH usr local miniconda2 bin 就OK啦 额 其实还不行 你要给conda加一个软链接 转换python2和3要创建虚拟环境
  • Sequence2Sequence 学习

    转载至 https blog csdn net MebiuW article details 52832847 1 前言 这个深度学习 xff0c 其实是来自每周Paper笔记的整理版 xff0c 即文章的主要内容其实是我对一篇文章的整理
  • Foreign Exchange (UVA - 10763)

    include lt iostream gt include lt bits stdc 43 43 h gt define maxn 500002 using namespace std int N1 maxn int N2 maxn in
  • 如何正确安装Microsoft Office 2019

    昨天作死 xff0c 因为Excel经常弹出一些奇奇怪怪的弹窗 xff0c 我去百度搜索 xff0c 没有找到答案 然后我发现大家都说最有效的办法是卸载了重新安装 xff0c 于是一键就卸载完了 然而 xff0c 最让我担心的事情发生了 x
  • 如何用java打印出JSON文件

    应老师要求 xff0c 需要打印出被剪枝的结点 xff0c 临时上网上查了资料 xff0c 我们需要下面的东西 xff1a 1 org json jar 下载之后把所有文件单独放在项目新建的文件夹org json下即可 2 我们需要知道两个
  • 关于apt update 的产生的一个问题

    E Release file for http mirrors 163 com ubuntu dists bionic updates InRelease is not valid yet invalid for another 1天 12
  • python在打开GBK格式的txt文件时无法用UTF-8格式读取

    如标题 xff0c 可以曲线救国 xff0c 把GBK文件转换成UTF 8文件 方法 xff1a 打开记事本 xff0c 点击另存为 xff0c 下面有编码 xff0c 选择UTF 8即可 美滋滋
  • dpkg和pip在ubuntu下查找所安装的包

    目录 原因结论 原因 这些天在弄ubuntu的时候 xff0c 想查看一些包的版本 xff0c 然后上网查了一下如何去做 一开始 xff0c 我就搜到利用下面这个语句 dpkg span class token operator span
  • Docker容器挂载本地共享文件夹

    Docker挂载本地目录的方法 Docker容器启动时 xff0c 我们可以使用 v参数来挂载主机下的一个目录 比如 xff0c 我需要启动一个ubuntu的容器 xff0c 并把 opt文件挂载在这个容器上做共享文件夹 a3551444f
  • 【论文解读 ICEIT2022】Heterogeneous Graph Based Knowledge Tracing基于异构图的知识追踪

    文章目录 摘要1 引言2 相关工作2 1 知识追踪2 2 异构图嵌入 3 基于异构图嵌入的知识追踪4 实验5 结论 依然是两阶段 摘要 最近 xff0c 随着在线辅导系统的发展 xff0c 对知识追踪 Knowledge Tracing 的
  • 【AAAI22】Interpretable Knowledge Tracing: Simple and Efficient Student Modeling with Causal Relations

    文章目录 摘要1 引言 可解释的知识追踪 xff1a 简单高效的因果关系学生建模 摘要 智能辅导系统在未来的学习环境中已变得至关重要 知识追踪是该系统的重要组成部分 它是关于推断学生的技能掌握和预测他们的表现 xff0c 以相应地调整课程
  • stm32CubeIDE 在自己工程中添加.c 和.h文件

    stm32CubeIDE发布已经有一段时间了 xff0c 网上也出现了好多使用教程 xff0c 但是大多数教程都是从软件的安装 gt 汉化 gt 改软件皮肤 gt 新建工程 gt 在工程的main 函数添加自己的测试代码 gt 设置调试配置
  • 快充芯片IP5328P的寄存器数据读写[用于DIY数显快充充电宝]

    本帖DIY因为有一定的危险性 xff0c 非专业人员请勿自行尝试 如有侵权 联系删除 IP5328P是一款最大18W的快充芯片 xff0c 主要用于快充充电宝的产品 xff0c 基本支持市面上绝大部分主流的快充协议 因为能看到本帖的想必都是
  • 使用汇编开发STM32

    使用汇编开发STM32系列文章 xff0c 会长期连载 xff0c 本文作为跳转用的目录 目录 一 说明二 系列文章跳转链接1 STM32涉及到的汇编基础知识2 STM32启动文件详解3 STM32不使用启动文件点亮一个LED灯并闪烁4 S
  • STM32F10x启动文件详解

    本文为使用汇编开发STM32系列文章之 启动文件详解篇 xff0c 全部文章目录点此跳转 本文不会像其他文章一样只是简单的说一下启动文件的每个部分是什么 xff0c 说了很多却又像没说一样 本文将对启动文件中的每句话的作用及其如此编写的原因