zoukankan      html  css  js  c++  java
  • STM32启动过程--启动文件--分析

    一、概述

    1、说明

      每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道。通过了解启动文件,我们可以体会到处理器的架构、指令集、中断向量安排等内容,是非常值得玩味的。

      STM32作为一款高端Cortex-M3系列单片机,有必要了解它的启动文件。打好基础,为以后优化程序,写出高质量的代码最准备。

      本文以一个实际测试代码--START_TEST为例进行阐述。

    2、整体过程概括

      STM整个启动过程是指从上电开始,一直到运行到main函数之间的这段过程,步骤为(以使用微库为例):

    ①上电后硬件设置SP、PC

    ②设置系统时钟

    ③软件设置SP

    ④加载.data、.bss,并初始化栈区

    ⑤跳转到C文件的main函数

    3、整个启动过程涉及的代码

      启动过程涉及的文件不仅包含startup_stm32f10x_hd.s,还涉及到了MDK自带的连接库文件entry.o、entry2.o、entry5.o、entry7.o等(从生成的map文件可以看出来)。

    二、程序在Flash上的存储结构

      在真正讲解启动过程之前,先要讲解程序下载到Flash上的结构和程序运行时(执行到main函数)时的SRAM数据结构。程序在用户Flash上的结构如下图所示。下图是通过阅读hex文件和在MDK下调试综合提炼出来的。

    MSP初始值        编译器生成,主堆栈的初始值

    异常向量表        不多说

    外部中断向量表      不多说

    代码段          存放代码

    初始化数据段       .data

    未初始化数据段      .bss 

    加载数据段和初始化栈的参数

      加载数据段和初始化栈的参数分别有4个,这里只讲解加载数据段的参数,至于初始化栈的参数类似。

    0x0800 033c  Flash上的数据段(初始化数据段和未初始化数据段)起始地址

    0x2000 0000  加载到SRAM上的目的地址

    0x0000 000c  数据段的总大小

    0x0800 02f4  调用函数_scatterload_copy

      需要说明的是初始化栈的函数--0x0800 0304与加载数据段的函数不一样,为_scatterload_zeroinit,它的目的就是将栈空间清零。

    三、数据在SRAM上的结构

      程序运行时(执行到main函数)时的SRAM数据结构

    四、详细过程分析

      有了以上的基础,现在详细分析启动过程。

    1、上电后硬件设置SP、PC

      刚上电复位后,硬件会自动根据向量表偏移地址找到向量表,向量表偏移地址的定义如下:

      调试现象如下:

      看看我们的向量表内容(通过J-Flash打开hex文件)

      硬件这时自动从0x0800 0000位置处读取数据赋给栈指针SP,然后自动从0x0800 0004位置处读取数据赋给PC,完成复位,结果为:

    SP = 0x0200 0810

    PC = 0x0800 0145

     2、设置系统时钟

      上一步中令PC=0x0800 0145的地址没有对齐,硬件自动对齐到0x0800 0144,执行SystemInit函数初始化系统时钟。

    3、软件设置SP

      LDR   R0,=__main
      BX   R0

      执行上两条之类,跳转到__main程序段运行,注意不是main函数,___main的地址是0x0800 0130。

      可以看到指令LDR.W sp,[pc,#12],结果SP=0x2000 0810。

    4、加载.data、.bss,并初始化栈区

     BL.W     __scatterload_rt2 

      进入 __scatterload_rt2代码段。

    __scatterload_rt2:
    0x08000168 4C06      LDR      r4,[pc,#24]  ; @0x08000184
    0x0800016A 4D07      LDR      r5,[pc,#28]  ; @0x08000188
    0x0800016C E006      B        0x0800017C
    0x0800016E 68E0      LDR      r0,[r4,#0x0C]
    0x08000170 F0400301  ORR      r3,r0,#0x01
    0x08000174 E8940007  LDM      r4,{r0-r2}
    0x08000178 4798      BLX      r3
    0x0800017A 3410      ADDS     r4,r4,#0x10
    0x0800017C 42AC      CMP      r4,r5
    0x0800017E D3F6      BCC      0x0800016E
    0x08000180 F7FFFFDA  BL.W     _main_init (0x08000138)

       这段代码是个循环(BCC 0x0800016e),实际运行时候循环了两次。第一次运行的时候,读取“加载数据段的函数(_scatterload_copy)”的地址并跳转到该函数处运行(注意加载已初始化数据段和未初始化数据段用的是同一个函数);第二次运行的时候,读取“初始化栈的函数(_scatterload_zeroinit)”的地址并跳转到该函数处运行。 相应的代码如下:

    0x0800016E 68E0      LDR      r0,[r4,#0x0C]
    0x08000170 F0400301  ORR      r3,r0,#0x01
    0x08000174  
    0x08000178 4798 BLX r3
      
      当然执行这两个函数的时候,还需要传入参数。至于参数,我们在“加载数据段和初始化栈的参数”环节已经阐述过了。当这两个函数都执行完后,结果就是“数据在SRAM上的结构”所展示的图。最后,也把事实加载和初始化的两个函数代码奉上如下:
                     __scatterload_copy:
    0x080002F4 E002      B        0x080002FC
    0x080002F6 C808      LDM      r0!,{r3}
    0x080002F8 1F12      SUBS     r2,r2,#4
    0x080002FA C108      STM      r1!,{r3}
    0x080002FC 2A00      CMP      r2,#0x00
    0x080002FE D1FA      BNE      0x080002F6
    0x08000300 4770      BX       lr
                     __scatterload_null:
    0x08000302 4770      BX       lr
                     __scatterload_zeroinit:
    0x08000304 2000      MOVS     r0,#0x00
    0x08000306 E001      B        0x0800030C
    0x08000308 C101      STM      r1!,{r0}
    0x0800030A 1F12      SUBS     r2,r2,#4
    0x0800030C 2A00      CMP      r2,#0x00
    0x0800030E D1FB      BNE      0x08000308
    0x08000310 4770      BX       lr

    5、跳转到C文件的main函数

                     _main_init:
    0x08000138 4800      LDR      r0,[pc,#0]  ; @0x0800013C
    0x0800013A 4700      BX       r0

    五、异常向量与中断向量表 

    ; Vector Table Mapped to Address 0 at Reset
                    AREA    RESET, DATA, READONLY
                    EXPORT  __Vectors
                    EXPORT  __Vectors_End
                    EXPORT  __Vectors_Size
    
    __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 & ADC2
                    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     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI Line
                    DCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
                    DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                    DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                    DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                    DCD     TIM8_CC_IRQHandler         ; TIM8 Capture Compare
                    DCD     ADC3_IRQHandler            ; ADC3
                    DCD     FSMC_IRQHandler            ; FSMC
                    DCD     SDIO_IRQHandler            ; SDIO
                    DCD     TIM5_IRQHandler            ; TIM5
                    DCD     SPI3_IRQHandler            ; SPI3
                    DCD     UART4_IRQHandler           ; UART4
                    DCD     UART5_IRQHandler           ; UART5
                    DCD     TIM6_IRQHandler            ; TIM6
                    DCD     TIM7_IRQHandler            ; TIM7
                    DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                    DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                    DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                    DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
    __Vectors_End
    View Code

       这段代码就是定义异常向量表,在之前有一个“J-Flash打开hex文件”的图片跟这个表格是一一对应的。编译器根据我们定义的函数 Reset_Handler、NMI_Handler等,在连接程序阶段将这个向量表填入这些函数的地址。

    startup_stm32f10x_hd.s内容:
    
    NMI_Handler     PROC
                    EXPORT  NMI_Handler                [WEAK]
                    B       .
                    ENDP
    
    
    stm32f10x_it.c中内容:
    void NMI_Handler(void)
    {
    }

      在启动汇编文件中已经定义了函数NMI_Handler,但是使用了“弱”,它允许我们再重新定义一个NMI_Handler函数,程序在编译的时候会将汇编文件中的弱函数“覆盖掉”--两个函数的代码在连接后都存在,只是在中断向量表中的地址填入的是我们重新定义函数的地址。 

    六、使用微库与不使用微库的区别

     

      使用微库就意味着我们不想使用MDK提供的库函数,而想用自己定义的库函数,比如说printf函数。那么这一点是怎样实现的呢?我们以printf函数为例进行说明。

    1、不使用微库而使用系统库

      在连接程序时,肯定会把系统中包含printf函数的库拿来调用参与连接,即代码段有系统库的参与。

      在启动过程中,不使用微库而使用系统库在初始化栈的时候,还需要初始化堆(猜测系统库需要用到堆),而使用微库则是不需要的。

                     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

      另外,在执行__main函数的过程中,不仅需要完成“使用微库”情况下的所有工作,额外的工作还需要进行库的初始化,才能使用系统库(这一部分我还没有深入探讨)。附上__main函数的内容:

                      __main:
    0x08000130 F000F802  BL.W     __scatterload_rt2_thumb_only (0x08000138)
    0x08000134 F000F83C  BL.W     __rt_entry_sh (0x080001B0)
                     __scatterload_rt2_thumb_only:
    0x08000138 A00A      ADR      r0,{pc}+4  ; @0x08000164
    0x0800013A E8900C00  LDM      r0,{r10-r11}
    0x0800013E 4482      ADD      r10,r10,r0
    0x08000140 4483      ADD      r11,r11,r0
    0x08000142 F1AA0701  SUB      r7,r10,#0x01
                     __scatterload_null:
    0x08000146 45DA      CMP      r10,r11
    0x08000148 D101      BNE      0x0800014E
    0x0800014A F000F831  BL.W     __rt_entry_sh (0x080001B0)
    0x0800014E F2AF0E09  ADR.W    lr,{pc}-0x07  ; @0x08000147
    0x08000152 E8BA000F  LDM      r10!,{r0-r3}
    0x08000156 F0130F01  TST      r3,#0x01
    0x0800015A BF18      IT       NE
    0x0800015C 1AFB      SUBNE    r3,r7,r3
    0x0800015E F0430301  ORR      r3,r3,#0x01
    0x08000162 4718      BX       r3
    0x08000164 0298      LSLS     r0,r3,#10
    0x08000166 0000      MOVS     r0,r0
    0x08000168 02B8      LSLS     r0,r7,#10
    0x0800016A 0000      MOVS     r0,r0
                     __scatterload_copy:
    0x0800016C 3A10      SUBS     r2,r2,#0x10
    0x0800016E BF24      ITT      CS
    0x08000170 C878      LDMCS    r0!,{r3-r6}
    0x08000172 C178      STMCS    r1!,{r3-r6}
    0x08000174 D8FA      BHI      __scatterload_copy (0x0800016C)
    0x08000176 0752      LSLS     r2,r2,#29
    0x08000178 BF24      ITT      CS
    0x0800017A C830      LDMCS    r0!,{r4-r5}
    0x0800017C C130      STMCS    r1!,{r4-r5}
    0x0800017E BF44      ITT      MI
    0x08000180 6804      LDRMI    r4,[r0,#0x00]
    0x08000182 600C      STRMI    r4,[r1,#0x00]
    0x08000184 4770      BX       lr
    0x08000186 0000      MOVS     r0,r0
                     __scatterload_zeroinit:
    0x08000188 2300      MOVS     r3,#0x00
    0x0800018A 2400      MOVS     r4,#0x00
    0x0800018C 2500      MOVS     r5,#0x00
    0x0800018E 2600      MOVS     r6,#0x00
    0x08000190 3A10      SUBS     r2,r2,#0x10
    0x08000192 BF28      IT       CS
    0x08000194 C178      STMCS    r1!,{r3-r6}
    0x08000196 D8FB      BHI      0x08000190
    0x08000198 0752      LSLS     r2,r2,#29
    0x0800019A BF28      IT       CS
    0x0800019C C130      STMCS    r1!,{r4-r5}
    0x0800019E BF48      IT       MI
    0x080001A0 600B      STRMI    r3,[r1,#0x00]
    0x080001A2 4770      BX       lr
                     __rt_lib_init:
    0x080001A4 B51F      PUSH     {r0-r4,lr}
    0x080001A6 F3AF8000  NOP.W    
                     __rt_lib_init_user_alloc_1:
    0x080001AA BD1F      POP      {r0-r4,pc}
                     __rt_lib_shutdown:
    0x080001AC B510      PUSH     {r4,lr}
                     __rt_lib_shutdown_user_alloc_1:
    0x080001AE BD10      POP      {r4,pc}
                     __rt_entry_sh:
    0x080001B0 F000F82F  BL.W     __user_setup_stackheap (0x08000212)
    0x080001B4 4611      MOV      r1,r2
                     __rt_entry_postsh_1:
    0x080001B6 F7FFFFF5  BL.W     __rt_lib_init (0x080001A4)
                     __rt_entry_postli_1:
    0x080001BA F000F919  BL.W     main (0x080003F0)
    View Code

    2、使用微库而不使用系统库

      在程序连接时,不会把包含printf函数的库连接到终极目标文件中,而使用我们定义的库。

      启动时需要完成的工作就是之前论述的步骤1、2、3、4、5,相比使用系统库,启动过程步骤更少。

    附测试代码:START_TEST.zip 

  • 相关阅读:
    URAL 2067 Friends and Berries (推理,数学)
    URAL 2070 Interesting Numbers (找规律)
    URAL 2073 Log Files (模拟)
    URAL 2069 Hard Rock (最短路)
    URAL 2068 Game of Nuts (博弈)
    URAL 2066 Simple Expression (水题,暴力)
    URAL 2065 Different Sums (找规律)
    UVa 1640 The Counting Problem (数学,区间计数)
    UVa 1630 Folding (区间DP)
    UVa 1629 Cake slicing (记忆化搜索)
  • 原文地址:https://www.cnblogs.com/amanlikethis/p/3719529.html
Copyright © 2011-2022 走看看