1. Detailed analysis of STM32 startup files
Detailed analysis of STM32 startup files (V3.5.0) Take: startup_stm32f10x_hd.s as an example
;******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
;* File Name : startup_stm32f10x_hd.s
;* Author : MCD Application Team
;* Version : V3.5.0
;* Date : 11-March-2011
;* Description : STM32F10x High Density 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
;* - Configure the clock system and also configure the external
;* SRAM mounted on STM3210E-EVAL board to be used as data
;* memory (optional, to be enabled by user)
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the CortexM3 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.
;* <<< Use Configuration Wizard in Context Menu >>>
;*******************************************************************************
; THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
; WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
; AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
; INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
; CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
; INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
;*******************************************************************************
; 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 0x00000400 ;EQU伪指令,作用是左边的符号名代表右边的表达式</span>
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;定义栈段:名称为STACK,未初始化,可读写,ELF 的栈段按2^3=8对齐
Stack_Mem SPACE Stack_Size ;分配一片连续的存储区域并初始化为 0,栈空间:0x400个字节
__initial_sp ;栈空间顶地址
; <h> Heap Configuration ;堆定义
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base ;堆空间起始地址
Heap_Mem SPACE Heap_Size ;堆空间:0x200个字节
__heap_limit ;堆空间结束地址
PRESERVE8 ;PRESERVE8 指令指定当前文件保持堆栈八字节对齐
THUMB ;告诉汇编器下面是32位的Thumb指令,如果需要汇编器将插入位以保证对齐
; Vector Table Mapped to Address 0 at Reset ;中断向量表定义
;实际上是在CODE区(假设STM32从FLASH启动,则此中断向量表起始地址即为0x8000000)
AREA RESET, DATA, READONLY ;定义一块数据段<DATA>,只可读<READONLY,默认READWRITE>,段名字是RESET
EXPORT __Vectors ;EXPORT:在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用
EXPORT __Vectors_End ;在程序中声明一个全局的标号__Vectors_End
EXPORT __Vectors_Size ;在程序中声明一个全局的标号__Vectors_Size
;DCD(DCDU)用于分配一片连续的字存储单元并用指定的数据初始化。
__Vectors DCD __initial_sp ; Top of Stack ;该处物理地址值存储__initial_sp所表示的地址值,即为 __Vetors 标号所表示的值
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 ;Vectors结束
__Vectors_Size EQU __Vectors_End - __Vectors ;得到向量表的大小,304个字节也就是0x130个字节
AREA |.text|, CODE, READONLY ;定义一个代码段,可读,段名字是.text
;|.text| 用于表示由 C 编译程序产生的代码段,或用于以某种方式与 C 库关联的代码段
; Reset handler ;利用PROC、ENDP这一对伪指令标记程序开始、结束,把程序段分为若干个过程,使程序的结构加清晰
Reset_Handler PROC
EXPORT Reset_Handler [WEAK];WEAK声明其他的同名标号优先于该标号被引用,就是说如果外面声明了的话,调用外面的对应函数
IMPORT __main ;IMPORT:伪指令用于通知编译器要使用的标号在其他的源文件中定义
IMPORT SystemInit
LDR R0, =SystemInit ;系统初始化
BLX R0 ;带链接的跳转,切换指令集,跳到SystemInit
LDR R0, =__main ;__main为运行时库提供的函数;完成堆栈,堆的初始化等工作,会调用下面定义的__user_initial_stackheap
BX R0 ;切换指令集,main函数不返回跳到__main,进入C的世界
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
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
;输出异常向量表标号,方便外部实现异常的具体功能,[WEAK]是弱定义的意思,如果外部定义了,优先执行外部定义,否则下面的函数定义
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]
EXPORT TIM8_BRK_IRQHandler [WEAK]
EXPORT TIM8_UP_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT ADC3_IRQHandler [WEAK]
EXPORT FSMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Channel1_IRQHandler [WEAK]
EXPORT DMA2_Channel2_IRQHandler [WEAK]
EXPORT DMA2_Channel3_IRQHandler [WEAK]
EXPORT DMA2_Channel4_5_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
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
ADC3_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler
B .
ENDP
ALIGN ;默认是字对齐方式,也说明了代码是4字节对齐的
;*******************************************************************************
; User Stack and Heap initialization 用户堆栈初始化
;*******************************************************************************
IF :DEF:__MICROLIB ;判断是否使用DEF:__MICROLIB(micro lib),如果勾选了micro lib
EXPORT __initial_sp ;将栈顶地址、堆起始地址、堆结束地址赋予全局属性,使外部程序可用
EXPORT __heap_base
EXPORT __heap_limit
ELSE ;如果没有勾选micro lib
IMPORT __use_two_region_memory ;两区堆栈空间,堆和栈有各自的空间地址
EXPORT __user_initial_stackheap
__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 ;END命令指示汇编器,已到达一个源文件的末尾
;******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE*****
2. The position and redirection of the STM32 interrupt vector table
Know how to jump to the main function, then, after the interrupt occurs, how do you go to the interrupt entry address?
As can be seen from stm32f10x.s, a large number of interrupt response functions have been defined. This is the interrupt vector table, labeled __Vectors, indicating the entry address of the interrupt vector table, for example:
AREA RESET, DATA, READONLY ; Define the read-only data segment, which is actually in the CODE area (assuming that STM32 starts from FLASH, the starting address of this interrupt vector table is 0x8000000)
EXPORT __Vectors
IMPORT OS_CPU_SysTickHandler
IMPORT OS_CPU_PendSVHandler
__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
The writing of this vector table is particular, it corresponds to the hardware one-to-one and cannot be written randomly. It is up to the CPU to find the entry address. The beginning of the bin file is their address. You can see the arrangement in section 10.1.2 of the reference manual RM0008.
Let’s combine the characteristics of CORTEX-M3. After it is powered on, it determines the position of the PC according to the boot pin. For example, if the boot is set to flash startup, the PC will jump to 0x08000000 after startup. At this time, the CPU will first take 2 addresses, the first is the top address of the stack, and the second is the reset exception address, so with the above writing method, it will jump to reset_handler.
So what is the actual address of this reset_handler.? What is the address of the following piles such as Nmi_handler? How did the interrupt go to this address? Let's explain one by one below.
1. We can know these entry addresses through the reverse direction. You can see the map file under the project. This address is closely related to the target->flash start address set in keil. In fact, we don’t need to care about it. Let’s compile Device allocation, the interrupt vector table puts their addresses.
2. Compared with the ARM7/ARM9 core, the Cortex-M3 core has fixed the position of the interrupt vector table and the starting address can be changed.
3. After entering the C language, the NVIC will be configured first. The start address and offset of the interrupt vector table can be configured in NVIC_SetVectorTable(), which mainly tells the CPU whether the vector table is located in Flash or Ram, and what the offset is. For example, if it is set to be located in Flash, the offset is the program address to burn in, which can be set in Keil target. In this way, the CPU knows the entry address.
4. After an interrupt occurs, the CPU finds the address of the interrupt vector table, and then finds the interrupt address according to the offset (check mark), and then skips it.
Let's take a screenshot to illustrate, map file:
For the corresponding bin file, see if it is placed at the above address:
Obviously, 200039c0 is the address of the top of the stack, and 08006F21 is the address of reset_handler!
How to locate? Take putting 0x20000000 as an example
1. Keil sets the start of ram to 0x20000100, we put the interrupt vector table at 0x20000000~0x20000100, and others are used by the program
2. Set NVIC_SetVectorTable(NVIC_VectTab_FLASH,0);
3. When jumping to C, copy the interrupt vector table to 0x20000000