STM32启动文件代码解析
目錄
- 啟動流程
- 代碼詳解
- 啟動文件使用的 ARM 匯編指令匯總
- 關(guān)于與啟動文件有關(guān)的一些問題思考
下面是F1固件庫V3.5.0的啟動文件startup_stm32f10x_hd.s,以此為例做解析,其余的型號的啟動文件都差不多,主要區(qū)別在于因片上外設(shè)不同,支持的中斷就不一樣,所以在啟動文件的中斷向量表不同型號會有差別。
啟動流程
啟動文件由匯編編寫,是系統(tǒng)上電復(fù)位后第一個執(zhí)行的程序。主要做了以下工作:
1、初始化堆棧指針 SP=_initial_sp
2、初始化 PC指針=Reset_Handler
3、初始化中斷向量表
4、配置系統(tǒng)時鐘
5、調(diào)用 C庫函數(shù)_main 初始化用戶堆棧,從而最終調(diào)用 main 函數(shù)去到 C的世界
代碼詳解
下面是用于KEIL中啟動文件,加入了較詳細(xì)的注釋:
; 分配給堆棧的內(nèi)存量(以字節(jié)為單位) ; 根據(jù)應(yīng)用程序的需要定制此值 ; <h> Stack Configuration 棧配置 ; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h> Stack_Size EQU 0x00000400 ;類似于:#define Stack_Size 0x00000400AREA STACK, NOINIT, READWRITE, ALIGN=3 ;偽指令A(yù)REA,表示名字為 STACK,NOINIT 即不初始化,可讀可寫,8(2^3)字節(jié)對齊。 Stack_Mem SPACE Stack_Size ;開辟一段大小為Stack_Size的內(nèi)存空間作為棧,Stack_Size已經(jīng)定義為了0x00000400(1KB) __initial_sp ;標(biāo)號__initial_sp緊挨著SPACE語句放置,表示棧的結(jié)束地址,即棧頂?shù)刂?#xff0c;棧是由高向低生長的。; <h> Heap Configuration 堆配置 ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h>Heap_Size EQU 0x00000200 ;類似于:#define Heap_Size 0x00000200AREA HEAP, NOINIT, READWRITE, ALIGN=3 ;偽指令A(yù)REA,表示名字為 HEAP,NOINIT即不初始化,可讀可寫,8(2^3)字節(jié)對齊 __heap_base ; __heap_base 表示對的起始地址 Heap_Mem SPACE Heap_Size ;開辟堆的大小為 0X00000200(512字節(jié)) __heap_limit ; __heap_limit 表示堆的結(jié)束地址。堆是由低向高生長的,跟棧的生長方向相反PRESERVE8 ; 指定當(dāng)前文件的堆棧按照 8字節(jié)對齊THUMB ; 表示后面指令兼容 THUMB 指令,THUBM 是 ARM 以前的指令集, 16bit,現(xiàn)在 Cortex-M 系列的都使用 THUMB-2 指令集, THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。; Vector Table Mapped to Address 0 at ResetAREA RESET, DATA, READONLY ;定義一個數(shù)據(jù)段,名字為 RESET,可讀。EXPORT __Vectors ;聲明 __Vectors、__Vectors_End 和__Vectors_Size這三個標(biāo)號具有全局屬性,可供外部的文件調(diào)用。EXPORT __Vectors_End ;EXPORT :聲明一個標(biāo)號可被外部的文件使用,使標(biāo)號具有全局屬性。EXPORT __Vectors_Size;__Vectors 為向量表起始地址 DCD: 以字為單位分配內(nèi)存,要求 4字節(jié)對齊,并要求初始化這些內(nèi)存 __Vectors DCD __initial_sp ; Top of Stack 棧頂?shù)刂稭SPDCD Reset_Handler ; Reset Handler 復(fù)位程序地址DCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; Reserved 0表示保留DCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler; External Interrupts 以下是外部中斷DCD WWDG_IRQHandler ; Window WatchdogDCD PVD_IRQHandler ; PVD through EXTI Line detectDCD TAMPER_IRQHandler ; TamperDCD RTC_IRQHandler ; RTCDCD FLASH_IRQHandler ; FlashDCD RCC_IRQHandler ; RCCDCD EXTI0_IRQHandler ; EXTI Line 0DCD EXTI1_IRQHandler ; EXTI Line 1DCD EXTI2_IRQHandler ; EXTI Line 2DCD EXTI3_IRQHandler ; EXTI Line 3DCD EXTI4_IRQHandler ; EXTI Line 4DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7DCD ADC1_2_IRQHandler ; ADC1 & ADC2DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TXDCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0DCD CAN1_RX1_IRQHandler ; CAN1 RX1DCD CAN1_SCE_IRQHandler ; CAN1 SCEDCD EXTI9_5_IRQHandler ; EXTI Line 9..5DCD TIM1_BRK_IRQHandler ; TIM1 BreakDCD TIM1_UP_IRQHandler ; TIM1 UpdateDCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and CommutationDCD TIM1_CC_IRQHandler ; TIM1 Capture CompareDCD TIM2_IRQHandler ; TIM2DCD TIM3_IRQHandler ; TIM3DCD TIM4_IRQHandler ; TIM4DCD I2C1_EV_IRQHandler ; I2C1 EventDCD I2C1_ER_IRQHandler ; I2C1 ErrorDCD I2C2_EV_IRQHandler ; I2C2 EventDCD I2C2_ER_IRQHandler ; I2C2 ErrorDCD SPI1_IRQHandler ; SPI1DCD SPI2_IRQHandler ; SPI2DCD USART1_IRQHandler ; USART1DCD USART2_IRQHandler ; USART2DCD USART3_IRQHandler ; USART3DCD EXTI15_10_IRQHandler ; EXTI Line 15..10DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI LineDCD USBWakeUp_IRQHandler ; USB Wakeup from suspendDCD TIM8_BRK_IRQHandler ; TIM8 BreakDCD TIM8_UP_IRQHandler ; TIM8 UpdateDCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and CommutationDCD TIM8_CC_IRQHandler ; TIM8 Capture CompareDCD ADC3_IRQHandler ; ADC3DCD FSMC_IRQHandler ; FSMCDCD SDIO_IRQHandler ; SDIODCD TIM5_IRQHandler ; TIM5DCD SPI3_IRQHandler ; SPI3DCD UART4_IRQHandler ; UART4DCD UART5_IRQHandler ; UART5DCD TIM6_IRQHandler ; TIM6DCD TIM7_IRQHandler ; TIM7DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 __Vectors_End ; __Vectors_End 為向量表結(jié)束地址__Vectors_Size EQU __Vectors_End - __Vectors ;__Vectors 為向量表起始地址, __Vectors_End 為向量表結(jié)束地址,兩個相減即可算出向量表大小。AREA |.text|, CODE, READONLY ;定義一個名稱為.text 的代碼段,可讀; Reset handler復(fù)位程序 Reset_Handler PROC ;PROC 定義子程序,與 ENDP 成對使用,表示子程序結(jié)束EXPORT Reset_Handler [WEAK] ;WEAK表示弱定義IMPORT __main ;IMPORT類似于extern,表示__main和SystemInit都來自外部文件,__main 是一個標(biāo)準(zhǔn)的 C 庫函數(shù),主要作用是初始化用戶堆棧,并在函數(shù)的最后調(diào)用main 函數(shù)去到 C 的世界IMPORT SystemInit ;SystemInit()是一個標(biāo)準(zhǔn)的庫函數(shù),在system_stm32f10x.c這個庫文件中定義。主要作用是配置系統(tǒng)時鐘LDR R0, =SystemInitBLX R0 LDR R0, =__mainBX R0ENDP; Dummy Exception Handlers (infinite loops which can be modified無限循環(huán)可以被修改)NMI_Handler PROCEXPORT NMI_Handler [WEAK]B . ;跳轉(zhuǎn)到一個標(biāo)號。這里跳轉(zhuǎn)到一個‘.’,即表示無線循環(huán)ENDP HardFault_Handler\PROCEXPORT HardFault_Handler [WEAK]B .ENDP MemManage_Handler\PROCEXPORT MemManage_Handler [WEAK]B .ENDP BusFault_Handler\PROCEXPORT BusFault_Handler [WEAK]B .ENDP UsageFault_Handler\PROCEXPORT UsageFault_Handler [WEAK]B .ENDP SVC_Handler PROCEXPORT SVC_Handler [WEAK]B .ENDP DebugMon_Handler\PROCEXPORT DebugMon_Handler [WEAK]B .ENDP PendSV_Handler PROCEXPORT PendSV_Handler [WEAK]B .ENDP SysTick_Handler PROCEXPORT SysTick_Handler [WEAK]B .ENDPDefault_Handler PROCEXPORT 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_IRQHandlerB .ENDPALIGN;******************************************************************************* ; User Stack and Heap initialization 用戶堆棧初始化 ;*******************************************************************************IF :DEF:__MICROLIB ;判斷是否定義了__MICROLIBEXPORT __initial_sp;如果定義了這個宏則賦予標(biāo)號__initial_sp(棧頂?shù)刂?#xff09;、__heap_base(堆起始地址)、__heap_limit(堆結(jié)束地址)全局屬性,可供外部文件調(diào)用。EXPORT __heap_baseEXPORT __heap_limitELSE ;如果沒有定義__MICROLIB,則插入標(biāo)號__use_two_region_memory,這個函數(shù)需要用戶自己實(shí)現(xiàn)IMPORT __use_two_region_memoryEXPORT __user_initial_stackheap ;聲明標(biāo)號__user_initial_stackheap 具有全局屬性,可供外部文件調(diào)用,下面實(shí)現(xiàn)這個標(biāo)號的內(nèi)容。__user_initial_stackheapLDR R0, = Heap_MemLDR R1, =(Stack_Mem + Stack_Size)LDR R2, = (Heap_Mem + Heap_Size)LDR R3, = Stack_MemBX LRALIGNENDIFEND ;文件結(jié)束啟動文件使用的 ARM 匯編指令匯總
【EQU】 給數(shù)字常量取一個符號名,相當(dāng)于 C語言中的 define
【AREA】 匯編一個新的代碼段或者數(shù)據(jù)段
【SPACE】 分配內(nèi)存空間
【PRESERVE8 】當(dāng)前文件堆棧需按照 8字節(jié)對齊
【EXPORT】聲明一個標(biāo)號具有全局屬性,可被外部的文件使用
【DCD 】以字為單位分配內(nèi)存,要求 4字節(jié)對齊,并要求初始化這些內(nèi)存
【PROC 】定義子程序,與 ENDP 成對使用,表示子程序結(jié)束
【W(wǎng)EAK 】弱定義,如果外部文件聲明了一個標(biāo)號,則優(yōu)先使用外部文件定義的標(biāo)號,如果外部文件沒有定義也不出錯。要注意的是:這個不是ARM的指令,是編譯器的,這里放在一起只是為了方便。
【IMPORT】聲明標(biāo)號來自外部文件,跟 C 語言中的 EXTERN關(guān)鍵字類似
【B】 跳轉(zhuǎn)到一個標(biāo)號
【ALIGN】 編譯器對指令或者數(shù)據(jù)的存放地址進(jìn)行對齊,一般需要跟一個立即數(shù),缺省表示 4字節(jié)對齊。要注意的是:這個不是 ARM的指令,是編譯器的,這里放在一起只是為了方便。
【END】 到達(dá)文件的末尾,文件結(jié)束
【IF,ELSE,ENDIF】匯編的條件分支語句,跟 C 語言的 if ,else 類似
LDR、BLX、BX 是 CM4 內(nèi)核的指令,可在《CM3 權(quán)威指南 CnR2》第四章-指令集里面查詢到
【LDR】 從存儲器中加載字到一個寄存器中
【BL】 跳轉(zhuǎn)到由寄存器/標(biāo)號給出的地址,并把跳轉(zhuǎn)前的下條指令地址保存到 LR
【BLX】 跳轉(zhuǎn)到由寄存器給出的地址,并根據(jù)寄存器的 LSE 確定處理器的狀態(tài),還要把跳轉(zhuǎn)前的下條指令地址保存到 LR
【BX】 跳轉(zhuǎn)到由寄存器/標(biāo)號給出的地址,不用返回
關(guān)于與啟動文件有關(guān)的一些問題思考
在啟動文件里面已經(jīng)幫我們寫好所有中斷的中斷服務(wù)函數(shù),跟我們平時寫的中斷服務(wù)函數(shù)不一樣的就是這些函數(shù)都是空的,真正的中斷復(fù)服務(wù)程序需要我們在外部的 C 文件里面重新實(shí)現(xiàn),這里只是提前占了一個位置而已。
如果我們在使用某個外設(shè)的時候,開啟了某個中斷,但是又忘記編寫配套的中斷服務(wù)程序或者函數(shù)名寫錯,那當(dāng)中斷來臨的時,程序就會跳轉(zhuǎn)到啟動文件預(yù)先寫好的空的中斷服務(wù)程序中,并且在這個空函數(shù)中無線循環(huán),即程序就死在這里。
B:跳轉(zhuǎn)到一個標(biāo)號。這里跳轉(zhuǎn)到一個‘.’,即表示無限循環(huán)
如果你在調(diào)試程序時發(fā)現(xiàn)死在了啟動文件中的B .這個位置,大概率是你使能了某個中斷但沒有編寫中斷函數(shù)。
棧的作用是用于局部變量,函數(shù)調(diào)用,函數(shù)形參等的開銷,棧的大小不能超過內(nèi)部SRAM 的大小。如果編寫的程序比較大,定義的局部變量很多,那么就需要修改棧的大小。如果某一天,你寫的程序出現(xiàn)了莫名奇怪的錯誤,并進(jìn)入了硬 fault 的時候,這時你就要考慮下是不是棧不夠大,溢出了。
修改啟動文件中的Stack_Size EQU 0x00000400可以修改棧大小
修改啟動文件中的Heap_Size EQU 0x00000200可以修改堆大小
在3.5版F1標(biāo)準(zhǔn)庫中的啟動文件還調(diào)用了在system_stm32f10x.c文件中的
SystemInit()函數(shù)配置系統(tǒng)時鐘,在舊版本的工程中要用戶進(jìn)入main函數(shù)自己調(diào)用SystemInit()函數(shù)。
無論性能高下,結(jié)構(gòu)簡繁,價格貴賤,每一種微控制器(處理器)都必須有啟動文件,啟動文件的作用便是負(fù)責(zé)執(zhí)行微控制器從“復(fù)位”到“開始執(zhí)行main函數(shù)”中間這段時間(稱為啟動過程)所必須進(jìn)行的工作。最為常見的51,AVR或MSP430等微控制器當(dāng)然也有對應(yīng)的啟動文件,但開發(fā)環(huán)境往往自動完整地提供了這個啟動文件,不需要開發(fā)人員再行干預(yù)啟動過程,只需要從main函數(shù)開始進(jìn)行應(yīng)用程序的設(shè)計(jì)即可。
Cortex-M3內(nèi)核規(guī)定,起始地址必須存放堆頂指針,而第二個地址則必須存放復(fù)位中斷入口向量地址,這樣在Cortex-M3內(nèi)核復(fù)位后,會自動從起始地址的下一個32位空間取出復(fù)位中斷入口向量,跳轉(zhuǎn)執(zhí)行復(fù)位中斷服務(wù)程序。
Cortex-M3內(nèi)核可通過boot引腳設(shè)置中斷向量表的位置(即可以設(shè)置從不同位置啟動),有3種情況:
1、 通過boot引腳設(shè)置可以將中斷向量表定位于SRAM區(qū),即起始地址為0x2000000,同時復(fù)位后PC指針位于0x2000000處;
2、 通過boot引腳設(shè)置可以將中斷向量表定位于FLASH區(qū),即起始地址為0x8000000,同時復(fù)位后PC指針位于0x8000000處;
3、 通過boot引腳設(shè)置可以將中斷向量表定位于內(nèi)置Bootloader區(qū)
參考鳴謝
https://www.cnblogs.com/wenshinlee/p/8859227.html
https://www.cnblogs.com/amanlikethis/p/3719529.html
總結(jié)
以上是生活随笔為你收集整理的STM32启动文件代码解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OBD技术速成——J1850协议解析软件
- 下一篇: STM32F1如何切换到不同的型号