STM32启动文件

一、复位电路

在了解启动文件之前需要明白STM32的复位中断流程,STM32的复位分为上电复位和手动复位,复位的电路图如下所示:

注意: 图中的复位电路是低电平复位,有的MCU是高电平复位。

  • 上电复位:顾名思义,上电复位就是STM32通电时,硬件自动复位的过程。从复位电路中可知,当芯片刚通电时电容两端没离子存在,所以处于充电过程,此时复位引脚等同于接地,这一过程成为上电复位。

  • 手动复位:手动复位是通过按键强行将复位引脚拉低,使芯片产生复位中断。

二、启动文件分析准备

  1. STM32的启动文件后缀是".s"的文件,打开项目是可以看到项目中有一个startup_stm32f103xb.s的文件,如下图所示:

    打开文件后可以很清晰的看到STM32的启动流程,不过这里需要一些简单的汇编知识。没学过汇编的小伙伴也不用怕,我们只需要简单的分析即可,这里只分析流程不进行汇编指令的编写。

  2. 启动文件中常用的汇编指令

    指令 作用
    EQU 定义字符常量,相当于C语言的 define
    AREA 汇编一个新的代码段或数据段
    SPACE 分配内存空间
    PRESERVE8 当前文件堆栈需按照 8 字节对其
    EXPORT 声明全局属性,可被外部文件使用
    DCD 以字为单位分配内存,要求4字节对齐,并初始化这些内存
    PROC 定义子程序,与 ENOP 成对使用,表示子程序结束
    WEAK 弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义,则使用当前位置的标号
    IMPORT 声明标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似
    B 跳转到一个标号
    END 到达文件的末尾,文件结束
    IF,ELSE,ENDIF 汇编条件分支语句
    LDR 从存储器中加载字到一个寄存器中
    BL 跳转到由寄存器/标号给出的地址,并把跳转前的下条指令地址保存到 LR
    BLX 跳转到由寄存器给出的地址,并根据寄存器的LSE确定处理器的状态,还要把跳转前的下条指令地址保存到LR
    BX 跳转到由寄存器/标号给出的地址,不用返回

    注意: ALIGN是编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示 4 字节对齐。需要注意的是:这个不是ARM的指令,是编译器的

  3. 启动流程
    (1) 硬件上电复位。
    (2) 初始化指针 SP=_initial_sp 和 PC == Reset_Handler。
    (3) 执行复位中断服务程序。

三、启动文件的作用

  1. 初始化堆栈指针SP;
  2. 初始化程序计数器指针 PC;
  3. 设置堆、栈的大小;
  4. 设置异常向量表的入口;
  5. 配置外部SRAM作为数据存储器(这个由用户配置,一般的开发板没有外部SRAM)
  6. 设置C库的分支入口__main(最终调用mian函数)
  7. 使用库函数项目时,启动文件还调用了SystemInit函数配置系统时钟。

四、启动代码详解

  1. 开辟栈(stack)空间,用于局部变量、函数调用、函数的参数等使用。栈的大小不能超过内部SRAM大小。

    Stack_Size EQU 0x400
    AREA STACK, NOINIT, READWRITE, ALIGN=3
    Stack_Mem SPACE Stack_Size
    __initial_sp
    • EQU:表示宏定义的伪指令,伪指令并不会生成二进制程序代码,也不会引起变量空间分配。0x400表示栈大小,注意这里是以字节为单位。
    • AREA:开辟一段可读写的数据空间,段名为stack,按照 8 字节对齐。AREA后面的关键字表示这个段的属性。
      (1) STACK:表示这个段的名字,可以任意命名。
      (2) NOINIT:表示此数据段不需要填入初始化数据。
      (3) READWRITE:表示此段可读写。
      (4) ALIGN=3:表示首地址按照 2 的 3次方对齐,也就是按照8字节对齐。
    • SPACE 这行指令告诉汇编器给STACK段分配0x400字节的连续内存空间。
    • __initial_sp紧挨SPACE放置,表示栈的结束机制,栈是从高往低使用,所以结束地址就是栈顶地址。
  2. 开辟堆(heap)空间,主要用于动态内存分配,也就是malloc、calloc、realloc等函数分配的变量空间是在堆上。

    Heap_Size EQU 0x200
    AREA HEAP, NOINIT, READWRITE, ALIGN=3
    __heap_base
    Heap_Mem SPACE Heap_Size
    __heap_limit
    • __heap_base:表示堆的开始地址
    • __heap_limit:表示堆的结束地址
  3. 文件属性定义

    PRESERVE8
    THUMB
    ; Vector Table Mapped to Address 0 at Reset
    AREA RESET, DATA, READONLY
    EXPORT __Vectors
    EXPORT __Vectors_End
    EXPORT __Vectors_Size
    • PRESERVE8:指定当前文件保持堆栈 8 字节对齐
    • THUMB:表示后面的指令是 THUMB 指令集,CM4 采用的是 THUMB - 2指令集
    • AREA:定义一块代码段,只读,段名是RESET。READONLY表示只读,缺省就表示代码段了
    • EXPORT:语句将3个标号声明为可被外部引用,主要提供给连接器用于连接库文件或其他文件
  4. 中断向量表

    __Vectors DCD __initial_sp ; Top of Stack
    DCD Reset_Handler ; Reset Handler
    DCD NMI_Handler ; NMI Handler
    DCD HardFault_Handler ; Hard Fault Handler
    DCD MemManage_Handler ; MPU Fault Handler
    DCD BusFault_Handler ; Bus Fault Handler
    DCD UsageFault_Handler ; Usage Fault Handler
    DCD 0 ; Reserved
    DCD 0 ; Reserved
    DCD 0 ; Reserved
    DCD 0 ; Reserved
    DCD SVC_Handler ; SVCall Handler
    DCD DebugMon_Handler ; Debug Monitor Handler
    DCD 0 ; Reserved
    DCD PendSV_Handler ; PendSV Handler
    DCD SysTick_Handler ; SysTick Handler
    ; External Interrupts
    DCD WWDG_IRQHandler ; Window Watchdog
    DCD PVD_IRQHandler ; PVD through EXTI Line detect
    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 High Priority or CAN1 TX
    DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
    DCD CAN1_RX1_IRQHandler ; CAN1 RX1
    DCD CAN1_SCE_IRQHandler ; CAN1 SCE
    DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
    DCD TIM1_BRK_IRQHandler ; TIM1 Break
    DCD TIM1_UP_IRQHandler ; TIM1 Update
    DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
    DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
    DCD TIM2_IRQHandler ; TIM2
    DCD TIM3_IRQHandler ; TIM3
    DCD TIM4_IRQHandler ; TIM4
    DCD I2C1_EV_IRQHandler ; I2C1 Event
    DCD I2C1_ER_IRQHandler ; I2C1 Error
    DCD I2C2_EV_IRQHandler ; I2C2 Event
    DCD I2C2_ER_IRQHandler ; I2C2 Error
    DCD SPI1_IRQHandler ; SPI1
    DCD SPI2_IRQHandler ; SPI2
    DCD USART1_IRQHandler ; USART1
    DCD USART2_IRQHandler ; USART2
    DCD USART3_IRQHandler ; USART3
    DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
    DCD RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line
    DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
    __Vectors_End
    __Vectors_Size EQU __Vectors_End - __Vectors
    • __Vectors:为向量表其实地址
    • __Vectors_End:为向量表结束地址
    • __Vectors_Size:为向量表的大小。
    • DCD:表示分配1个4字节的空间。每行DCD都会生成一个4字节的二进制代码。中断向量表存放的实际上是中断服务程序的入口地址,当异常(也就是中断实践)发生时,CPU的中断系统会将相应的入口地址赋值给PC程序计数器,之后就开始执行中断服务程序。
  5. 定义可读代码段

    AREA |.text|, CODE, READONLY
    • AREA:定义一个名为.test的可读代码段
  6. 复位程序

    ; Reset handler
    Reset_Handler PROC
    EXPORT Reset_Handler [WEAK]
    IMPORT __main
    IMPORT SystemInit
    LDR R0, =SystemInit
    BLX R0
    LDR R0, =__main
    BX R0
    ENDP
    • 复位子程序是系统上电后第一个执行的程序,调用SystemInit()函数初始化系统时钟,然后调用C库函数__main。
  7. 中断复位子程序

    ; Dummy Exception Handlers (infinite loops which can be modified)
    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
    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 RTC_Alarm_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
    RTC_Alarm_IRQHandler
    USBWakeUp_IRQHandler
    B .
    ENDP
    • WEAK:如果外部文件中定义了此中断函数,优先使用外部文件中的中断函数,反之使用当前中断函数。
    • ".":表示无限循环
    • 如果我们在使用某个外设的时候,开启了某个中断,但是又忘记编写配套的中断服务程序需或者函数名写错时,当相应的中断产生时,程序就会跳转到启动文件预先写好的空中断函数中,并且在这个空函数中无限循环,即程序就死在这里。
  8. 用户堆栈初始化

    ALIGN
    ;*******************************************************************************
    ; User Stack and Heap initialization
    ;*******************************************************************************
    IF :DEF:__MICROLIB
    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
    • ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示4字节对齐。
    • __user_initial_stackheap:初始化栈和堆(使用默认C库时,由__main函数进行调用)
    • 如果使用微库时,调用__MICROLIB部分的程序,反之使用默认的C库,然后初始化用户堆栈大小。微库的使用如下图所示:

参考文献

STM32启动文件————startup_stm32f10x_hd.s:https://wenku.baidu.com/view/3275eee00ba1284ac850ad02de80d4d8d15a0198.html

posted @   浇筑菜鸟  阅读(1256)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示