STM32的啟動文件
STM32作為一款單片機,它的啟動方式很簡單,即當Boot配置了從內部Flash啟動模式之後,一上電程序就會從0x8000000地址處開始執行文件,因此我們在使用Keil設置程序起始地址的時候,需要將這個Flash地址設置成0x8000000,只有將這個地址設置成0x8000000,生成的hex文件才可以被正常燒錄到此地址,單片機上電之後才可以正常啟動。而如果使用J-Flash工具燒寫Hex文件時,這個地址會自動根據Hex文件解析出來。然而如果當你燒寫二進位Bin文件時,還需要手動將單片機的起始地址制定出來,關於Hex文件和Bin文件的異同點,這個又是可以長篇大論一番了,我們下次特別寫文章來講。
圖1 Keil設置起始地址和空間
STM32啟動文件
;********************* (C) COPYRIGHT 2017 STMicroelectronics ********************;* File Name : startup_stm32l151xb.s;* Author : MCD Application Team;* Description : STM32L151XB Devices vector 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 system clock;* - Branches to __main in the C library (which eventually;* calls main()).;* After Reset the Cortex-M3 processor is in Thread mode,;* priority is Privileged, and the Stack is set to Main.;********************************************************************************;*;* Copyright (c) 2017 STMicroelectronics. All rights reserved.;*;* This software component is licensed by ST under BSD 3-Clause license,;* the "License"; You may not use this file except in compliance with the;* License. You may obtain a copy of the License at:;* opensource.org/licenses/BSD-3-Clause;*;*******************************************************************************;* <<< Use Configuration Wizard in Context Menu >>>;; 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
AREA STACK, NOINIT, READWRITE, ALIGN=3Stack_Mem SPACE Stack_Size__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_baseHeap_Mem SPACE Heap_Size__heap_limit
PRESERVE8 THUMB
; 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_STAMP_IRQHandler ; Tamper and Time Stamp DCD RTC_WKUP_IRQHandler ; RTC Wakeup 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_IRQHandler ; ADC1 DCD USB_HP_IRQHandler ; USB High Priority DCD USB_LP_IRQHandler ; USB Low Priority DCD DAC_IRQHandler ; DAC DCD COMP_IRQHandler ; COMP through EXTI Line DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 DCD 0 ; Reserved DCD TIM9_IRQHandler ; TIM9 DCD TIM10_IRQHandler ; TIM10 DCD TIM11_IRQHandler ; TIM11 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 USB_FS_WKUP_IRQHandler ; USB FS Wakeup from suspend DCD TIM6_IRQHandler ; TIM6 DCD TIM7_IRQHandler ; TIM7 __Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset handler routineReset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDPHardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] B . ENDPMemManage_Handler\ PROC EXPORT MemManage_Handler [WEAK] B . ENDPBusFault_Handler\ PROC EXPORT BusFault_Handler [WEAK] B . ENDPUsageFault_Handler\ PROC EXPORT UsageFault_Handler [WEAK] B . ENDPSVC_Handler PROC EXPORT SVC_Handler [WEAK] B . ENDPDebugMon_Handler\ PROC EXPORT DebugMon_Handler [WEAK] B . ENDPPendSV_Handler PROC EXPORT PendSV_Handler [WEAK] B . ENDPSysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK] EXPORT PVD_IRQHandler [WEAK] EXPORT TAMPER_STAMP_IRQHandler [WEAK] EXPORT RTC_WKUP_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_IRQHandler [WEAK] EXPORT USB_HP_IRQHandler [WEAK] EXPORT USB_LP_IRQHandler [WEAK] EXPORT DAC_IRQHandler [WEAK] EXPORT COMP_IRQHandler [WEAK] EXPORT EXTI9_5_IRQHandler [WEAK] EXPORT TIM9_IRQHandler [WEAK] EXPORT TIM10_IRQHandler [WEAK] EXPORT TIM11_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 USB_FS_WKUP_IRQHandler [WEAK] EXPORT TIM6_IRQHandler [WEAK] EXPORT TIM7_IRQHandler [WEAK]
WWDG_IRQHandlerPVD_IRQHandlerTAMPER_STAMP_IRQHandlerRTC_WKUP_IRQHandlerFLASH_IRQHandlerRCC_IRQHandlerEXTI0_IRQHandlerEXTI1_IRQHandlerEXTI2_IRQHandlerEXTI3_IRQHandlerEXTI4_IRQHandlerDMA1_Channel1_IRQHandlerDMA1_Channel2_IRQHandlerDMA1_Channel3_IRQHandlerDMA1_Channel4_IRQHandlerDMA1_Channel5_IRQHandlerDMA1_Channel6_IRQHandlerDMA1_Channel7_IRQHandlerADC1_IRQHandlerUSB_HP_IRQHandlerUSB_LP_IRQHandlerDAC_IRQHandlerCOMP_IRQHandlerEXTI9_5_IRQHandlerTIM9_IRQHandlerTIM10_IRQHandlerTIM11_IRQHandlerTIM2_IRQHandlerTIM3_IRQHandlerTIM4_IRQHandlerI2C1_EV_IRQHandlerI2C1_ER_IRQHandlerI2C2_EV_IRQHandlerI2C2_ER_IRQHandlerSPI1_IRQHandlerSPI2_IRQHandlerUSART1_IRQHandlerUSART2_IRQHandlerUSART3_IRQHandlerEXTI15_10_IRQHandlerRTC_Alarm_IRQHandlerUSB_FS_WKUP_IRQHandlerTIM6_IRQHandlerTIM7_IRQHandler
B .
ENDP
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
;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****首先讓我們來看下STM32啟動文件,當MCU上電復位之後,整個程序會跳轉到以0x8000000為基址,偏移0的地址處,即還是0x8000000。但是STM32的0x8000000地址處存放的並不是整個晶片的第一句指令,而是整個晶片的堆棧初始化程序,如圖2所示。
圖2 0x8000000偏移0地址處的堆棧初始化程序指針
由於STM32的地址空間都是4位元組對齊的,因此這個棧頂指針的存放空間為4位元組,所以STM32復位之後跳轉的地址應該是0x8000000基址偏移4個字節,即0x8000004。如同3所示。
圖3 STM32復位跳轉地址
圖3中的程序非常淺顯易懂,第136和137行,即將程序跳轉到SystemInit處,這是個C語言函數,定義在「system_stm32l1xx.c」文件裡,它的目的就是對中斷向量表起始地址進行指定,也就是圖2中的「__Vector」處。當然CM3內核和CM0內核關於SCB(系統控制塊)的定義有些許差別,CM0不在本文討論中,但是CM3和CM4的中斷向量表映射機制還是很相似的。
圖4 SystemInit函數映射中斷向量表
圖4中我們可以看到,SCB中關於Vector的地址是通過符號FLASH_BASE和VECT_TAB_OFFSET計算出來的,我們可以找到關於它們的定義,如圖5所示。
圖5 FLASH_BASE和VECT_TAB_OFFSET的定義
通過圖5中的計算,正好可以得出整個中斷向量表被映射到了0x8000000地址處。
STM32的FLASH分配
前面的大段文章內容中,頻繁提及了一個關鍵的數值,即0x8000000,那麼這個0x8000000到底是怎麼來的呢?這個數值並不是平白無故拍腦袋想出來的。之前我們就說過,ARM體系的存儲器結構是其一大特色,而這個0x8000000正是整個STM32內置FLASH的起始地址。我們隨便打開一份STM32的數據手冊,在存儲器章節裡面就可以看到STM32全部的存儲器定義。如圖6所示。
圖6 STM32內部FLASH的起始地址
STM32的Bootloader思路
拋開所有的Bootloader高級功能來說,我們設計STM32 Bootloader的主要目的有兩個,第一個為方便程序燒寫和更新,第二個目前是從Bootloader程序中跳轉(引導)用戶的應用程式。這兩個目的中,對於Bootloader來說程序跳轉尤其重要,因為程序跳轉成不成功將會嚴重影響整個用戶程序的運行狀態。因而,怎麼跳,何時跳,跳到哪裡,則是下篇文章的著重討論部分。
前面一個FLASH燒寫,可以根據自己的特殊要求來定製,只要嚴格安裝HEX文件指定的地址和數據的關係,一般不會出錯。
本文分析了STM32啟動時比較重要的一些定義和函數跳轉,下篇將會開始著手設計一個STM32 Bootloader。