关于stm32上电之后的启动流程,梳理一下思路,如有错误,回头来改。

 

加载程序:当用户选择想单片机烧录程序的时候,BOOT0、BOOT1来决定代码存放的位置。并将这片存储空间重新映射到0x00000000开始的位置。
启动程序:然后程序从开始处执行,首先执行的不是main(),而是启动文件
异常信号处理:当用户按下复位键(或者产生其他异常信号)的时候,CPU查询中断向量表,跳转到相对应的处理。

 

stm32的启动模式
程序映像通常是从地址 0x00000000 开始的、系统复位后,处理器首先读取向量表中的前两个字(8 个字节),第一个字存入 MSP,第二个字为复位向量,它表示程序执行的起始地址(复位处理)

 

启动模式的设置
BOOT0和 BOOT1用于设置 STM32的启动方式,启动模式只决定程序烧录的位置,加载完 程序之后会有一个重映射(映射到0x00000000地址位置);真正产生复位信号的时候,CPU还是从开始位置执行。

 

C语言内存分布
在运行C用户程序的时候,内存将数据和代码分成几个部分


 

启动文件分析
以STM32F051启动文件为例,与其他系列的stm32单片机的启动过程都是差不多的。
首先声明堆栈空间,定义了异常向量表(其中有很多异常处理的入口地址),然后进行系统时钟初始化,堆栈空间的初始化,最后才跳转到C程序中的main()函数,执行用户程序。总言之,启动文件设置了用户程序的运行环境。

;******************** (C) COPYRIGHT 2016 STMicroelectronics ********************
;* File Name          : startup_stm32f051x8.s
;* Author             : MCD Application Team
;* Description        : STM32F051x4/STM32F051x6/STM32F051x8 devices vector table for MDK-ARM toolchain.
;*                      This module performs:
;*                      - Set the initial SP
;*                      - Set the initial PC == Reset_Handler
;*                      - Set the vector table entries with the exceptions ISR address
;*                      - Branches to __main in the C library (which eventually
;*                        calls main()).
;*                      After Reset the CortexM0 processor is in Thread mode,
;*                      priority is Privileged, and the Stack is set to Main.
;* <<< Use Configuration Wizard in Context Menu >>>
;*******************************************************************************
;*
;* Redistribution and use in source and binary forms, with or without modification,
;* are permitted provided that the following conditions are met:
;*   1. Redistributions of source code must retain the above copyright notice,
;*      this list of conditions and the following disclaimer.
;*   2. Redistributions in binary form must reproduce the above copyright notice,
;*      this list of conditions and the following disclaimer in the documentation
;*      and/or other materials provided with the distribution.
;*   3. Neither the name of STMicroelectronics nor the names of its contributors
;*      may be used to endorse or promote products derived from this software
;*      without specific prior written permission.
;*
;* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
;* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
;* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
;* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
;* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
;* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
;* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;
;*******************************************************************************

; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Stack_Size	 EQU     0x400									;定义栈的大小1K

                AREA    STACK, NOINIT, READWRITE, ALIGN=3	;AREA 是定义一个段,此处定义的是栈段,没有初始化的段,可读可写,align是说段的起始地址应该是8的倍数
Stack_Mem       SPACE   Stack_Size							;SPACE 是为栈分配空间,开辟栈空间大小为0x400
__initial_sp												;__initial_sp是个标号,这个标号指向栈的栈顶


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size      EQU     0x200								;定义堆的大小 512B

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base													;堆的起始地址
Heap_Mem        SPACE   Heap_Size							;堆的大小
__heap_limit												;堆的结束地址

                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY		;此处定义的数据段,数据段放的是中断向量表,只读
                EXPORT  __Vectors					;EXPORT在程序中声明一个全局的标号__Vectors,该标号可在其它的文件中引用
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size
											;DCD指令以字为单位分配内存,要求4字节对齐,并要求初始化这些内存。
											;在向量表中,DCD 分配了一堆内存,并且以 ESR 的入口地址初始化它们。
												
												
__Vectors       DCD     __initial_sp                   ; Top of Stack    存放__initial_sp,也就是堆栈栈顶的地址
                DCD     Reset_Handler                  ; Reset Handler
                DCD     NMI_Handler                    ; NMI Handler
                DCD     HardFault_Handler              ; Hard Fault Handler
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     0                              ; Reserved
                DCD     SVC_Handler                    ; SVCall Handler
                DCD     0                              ; Reserved
                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     RTC_IRQHandler                 ; RTC through EXTI Line
                DCD     FLASH_IRQHandler               ; FLASH
                DCD     RCC_IRQHandler                 ; RCC
                DCD     EXTI0_1_IRQHandler             ; EXTI Line 0 and 1
                DCD     EXTI2_3_IRQHandler             ; EXTI Line 2 and 3
                DCD     EXTI4_15_IRQHandler            ; EXTI Line 4 to 15
                DCD     TSC_IRQHandler                 ; TS
                DCD     DMA1_Channel1_IRQHandler       ; DMA1 Channel 1
                DCD     DMA1_Channel2_3_IRQHandler     ; DMA1 Channel 2 and Channel 3
                DCD     DMA1_Channel4_5_IRQHandler     ; DMA1 Channel 4 and Channel 5
                DCD     ADC1_COMP_IRQHandler           ; ADC1, COMP1 and COMP2 
                DCD     TIM1_BRK_UP_TRG_COM_IRQHandler ; TIM1 Break, Update, Trigger and Commutation
                DCD     TIM1_CC_IRQHandler             ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler                ; TIM2
                DCD     TIM3_IRQHandler                ; TIM3
                DCD     TIM6_DAC_IRQHandler            ; TIM6 and DAC
                DCD     0                              ; Reserved
                DCD     TIM14_IRQHandler               ; TIM14
                DCD     TIM15_IRQHandler               ; TIM15
                DCD     TIM16_IRQHandler               ; TIM16
                DCD     TIM17_IRQHandler               ; TIM17
                DCD     I2C1_IRQHandler                ; I2C1
                DCD     I2C2_IRQHandler                ; I2C2
                DCD     SPI1_IRQHandler                ; SPI1
                DCD     SPI2_IRQHandler                ; SPI2
                DCD     USART1_IRQHandler              ; USART1
                DCD     USART2_IRQHandler              ; USART2
                DCD     0                              ; Reserved
                DCD     CEC_IRQHandler                 ; CEC

__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY		 ;text  一般代表代码段

; Reset handler routine
Reset_Handler    PROC
                 EXPORT  Reset_Handler                 [WEAK]     ;“[WEAK]”是一个弱符号声明,就是告诉编译器,我这里声明的标号的优先权低于其它同名的标号,也就是说如果在工程中还有别的函数和我同名,那么你就调用和我同名的其它函数,如果没有同名的,那你就调用我。
        IMPORT  __main									 ;IMPORT则表明后面标号所代表的函数是调用其它文件的
        IMPORT  SystemInit  
                 LDR     R0, =SystemInit			;复位子程序是系统上电后第一个执行的程序,调用 SystemInit 函数初始化系统时钟
                 BLX     R0
                 LDR     R0, =__main                   ;__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,并在函数的最后调用main 函数去到 C 的世界
                 BX      R0
                 ENDP

; 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
SVC_Handler     PROC
                EXPORT  SVC_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  RTC_IRQHandler                 [WEAK]
                EXPORT  FLASH_IRQHandler               [WEAK]
                EXPORT  RCC_IRQHandler                 [WEAK]
                EXPORT  EXTI0_1_IRQHandler             [WEAK]
                EXPORT  EXTI2_3_IRQHandler             [WEAK]
                EXPORT  EXTI4_15_IRQHandler            [WEAK]
                EXPORT  TSC_IRQHandler                 [WEAK]
                EXPORT  DMA1_Channel1_IRQHandler       [WEAK]
                EXPORT  DMA1_Channel2_3_IRQHandler     [WEAK]
                EXPORT  DMA1_Channel4_5_IRQHandler     [WEAK]
                EXPORT  ADC1_COMP_IRQHandler           [WEAK]
                EXPORT  TIM1_BRK_UP_TRG_COM_IRQHandler [WEAK]
                EXPORT  TIM1_CC_IRQHandler             [WEAK]
                EXPORT  TIM2_IRQHandler                [WEAK]
                EXPORT  TIM3_IRQHandler                [WEAK]
                EXPORT  TIM6_DAC_IRQHandler            [WEAK]
                EXPORT  TIM14_IRQHandler               [WEAK]
                EXPORT  TIM15_IRQHandler               [WEAK]
                EXPORT  TIM16_IRQHandler               [WEAK]
                EXPORT  TIM17_IRQHandler               [WEAK]
                EXPORT  I2C1_IRQHandler                [WEAK]
                EXPORT  I2C2_IRQHandler                [WEAK]
                EXPORT  SPI1_IRQHandler                [WEAK]
                EXPORT  SPI2_IRQHandler                [WEAK]
                EXPORT  USART1_IRQHandler              [WEAK]
                EXPORT  USART2_IRQHandler              [WEAK]
                EXPORT  CEC_IRQHandler                 [WEAK]


WWDG_IRQHandler
PVD_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_1_IRQHandler
EXTI2_3_IRQHandler
EXTI4_15_IRQHandler
TSC_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_3_IRQHandler
DMA1_Channel4_5_IRQHandler
ADC1_COMP_IRQHandler
TIM1_BRK_UP_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM6_DAC_IRQHandler
TIM14_IRQHandler
TIM15_IRQHandler
TIM16_IRQHandler
TIM17_IRQHandler
I2C1_IRQHandler
I2C2_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
CEC_IRQHandler

                B       .

                ENDP

                ALIGN

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB       ;检查是否定义了__MICROLIB,如果定义了则条件成立。用户可以在配置软件中勾选。
									  ;用户栈和堆初始化,由C库函数_main来完成
                 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) COPYRIGHT STMicroelectronics *****END OF FILE*****

 

 


Q&A

Q:为什么驱动文件是汇编语言写而不是C语言?
A:汇编语言处理一些更加底层的工作,如设置堆栈空间等,这一点C这个"高级语言"无法做到。

Q:启动程序阶段,CPU对启动文件是从上向下足以读取吗?
A:编译之前的是源程序,编译之后的指令的机器码以地址进行排列。实际上按地址逐一进行读取,要看源码的组织结构。

Q:用户通过Keil软件配置堆栈空间和自己定义堆栈有什么区别?
A: