最近终于闲了下来了准备好好学习下Cortex-M3/M4系列处理器的架构,经过各种资料的折磨也没法对它的整个工作过程能有个完整的认知,最后看到一片博客打算从程序的运行过程开始探究,所以首先就找到了启动文件因为它可是你的程序开始跑之前的一些重要的准备工作,想要更好的使用某一款芯片了解程序在芯片内运行的始末就显得十分重要了。就像一个博客中这么写到:“每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道。通过了解启动文件,我们可以体会到处理器的架构、指令集、中断向量安排等内容,是非常值得玩味的”。
其中DCD 相当于C语言力的&,定义地址。
Stack_Size EQU 0x00000400 :0x00000400=1024
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
其中第二行汇编了数据段STACK 其中 NOINIT 表示未初始化,READWRITE可读可写,ALLGN 2*3 = 8 八字节对齐,第三行为STACK分配了大小为1KB的内存空间,紧跟着后面的__initial_sp 因为其是紧跟着栈分配内存后所以其为栈顶(递减栈)。最终__init_sp为1KB空间栈的栈顶,栈主要用于函数局部变量和形参的及调用过程的临时存储,属于编译器自动分配和释放的内存,所以这里需要注意如果你的函数所占的内存大过这个空间应调整其大小但一定要小于内部SARM的大小。堆是程序员进行分配和释放的,如果程序中未释放最后由系统回收。
Heap_Size EQU 0x00000200 ;0x00000200 = 1000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
其中第二行汇编了一个数据段HEAP NOINIT 未初始化,READWRITE可读可写,ALLGN 2*3 = 8 八字节对齐,第三行定义堆的起始地址 __heap_base 接下来就按汇编的数据类型和大小分配堆的1000B内存同样 __heap_limit就为堆的最后一个地址。对于内存可以看这个博客:http://blog.csdn.net/qq_29119171/article/details/53764823
PRESERVE8 编译器指令8字节对齐
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 detection
DCD TAMP\_STAMP\_IRQHandler ; Tamper and TimeStamps through the EXTI line
DCD RTC\_WKUP\_IRQHandler ; RTC Wakeup through the EXTI line
DCD FLASH\_IRQHandler ; FLASH
DCD RCC\_IRQHandler ; RCC
DCD EXTI0\_IRQHandler ; EXTI Line0
DCD EXTI1\_IRQHandler ; EXTI Line1
DCD EXTI2\_IRQHandler ; EXTI Line2
DCD EXTI3\_IRQHandler ; EXTI Line3
DCD EXTI4\_IRQHandler ; EXTI Line4
DCD DMA1\_Stream0\_IRQHandler ; DMA1 Stream 0
DCD DMA1\_Stream1\_IRQHandler ; DMA1 Stream 1
DCD DMA1\_Stream2\_IRQHandler ; DMA1 Stream 2
DCD DMA1\_Stream3\_IRQHandler ; DMA1 Stream 3
DCD DMA1\_Stream4\_IRQHandler ; DMA1 Stream 4
DCD DMA1\_Stream5\_IRQHandler ; DMA1 Stream 5
DCD DMA1\_Stream6\_IRQHandler ; DMA1 Stream 6
DCD ADC\_IRQHandler ; ADC1, ADC2 and ADC3s
DCD CAN1\_TX\_IRQHandler ; CAN1 TX
DCD CAN1\_RX0\_IRQHandler ; CAN1 RX0
DCD CAN1\_RX1\_IRQHandler ; CAN1 RX1
DCD CAN1\_SCE\_IRQHandler ; CAN1 SCE
DCD EXTI9\_5\_IRQHandler ; External Line\[9:5\]s
DCD TIM1\_BRK\_TIM9\_IRQHandler ; TIM1 Break and TIM9
DCD TIM1\_UP\_TIM10\_IRQHandler ; TIM1 Update and TIM10
DCD TIM1\_TRG\_COM\_TIM11\_IRQHandler ; TIM1 Trigger and Commutation and TIM11
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 ; External Line\[15:10\]s
DCD RTC\_Alarm\_IRQHandler ; RTC Alarm (A and B) through EXTI Line
DCD OTG\_FS\_WKUP\_IRQHandler ; USB OTG FS Wakeup through EXTI line
DCD TIM8\_BRK\_TIM12\_IRQHandler ; TIM8 Break and TIM12
DCD TIM8\_UP\_TIM13\_IRQHandler ; TIM8 Update and TIM13
DCD TIM8\_TRG\_COM\_TIM14\_IRQHandler ; TIM8 Trigger and Commutation and TIM14
DCD TIM8\_CC\_IRQHandler ; TIM8 Capture Compare
DCD DMA1\_Stream7\_IRQHandler ; DMA1 Stream7
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\_DAC\_IRQHandler ; TIM6 and DAC1&2 underrun errors
DCD TIM7\_IRQHandler ; TIM7
DCD DMA2\_Stream0\_IRQHandler ; DMA2 Stream 0
DCD DMA2\_Stream1\_IRQHandler ; DMA2 Stream 1
DCD DMA2\_Stream2\_IRQHandler ; DMA2 Stream 2
DCD DMA2\_Stream3\_IRQHandler ; DMA2 Stream 3
DCD DMA2\_Stream4\_IRQHandler ; DMA2 Stream 4
DCD ETH\_IRQHandler ; Ethernet
DCD ETH\_WKUP\_IRQHandler ; Ethernet Wakeup through EXTI line
DCD CAN2\_TX\_IRQHandler ; CAN2 TX
DCD CAN2\_RX0\_IRQHandler ; CAN2 RX0
DCD CAN2\_RX1\_IRQHandler ; CAN2 RX1
DCD CAN2\_SCE\_IRQHandler ; CAN2 SCE
DCD OTG\_FS\_IRQHandler ; USB OTG FS
DCD DMA2\_Stream5\_IRQHandler ; DMA2 Stream 5
DCD DMA2\_Stream6\_IRQHandler ; DMA2 Stream 6
DCD DMA2\_Stream7\_IRQHandler ; DMA2 Stream 7
DCD USART6\_IRQHandler ; USART6
DCD I2C3\_EV\_IRQHandler ; I2C3 event
DCD I2C3\_ER\_IRQHandler ; I2C3 error
DCD OTG\_HS\_EP1\_OUT\_IRQHandler ; USB OTG HS End Point 1 Out
DCD OTG\_HS\_EP1\_IN\_IRQHandler ; USB OTG HS End Point 1 In
DCD OTG\_HS\_WKUP\_IRQHandler ; USB OTG HS Wakeup through EXTI
DCD OTG\_HS\_IRQHandler ; USB OTG HS
DCD DCMI\_IRQHandler ; DCMI
DCD CRYP\_IRQHandler ; CRYP crypto
DCD HASH\_RNG\_IRQHandler ; Hash and Rng
DCD FPU\_IRQHandler ; FPU
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
这是一个中断向量表,向量表从FLASH 的0x00000000 地址开始放置,以4 个字节为一个单位,地址0 存放的是栈顶(sp_init)地址,0X00000004 存放的是复位程序的地址,以此类推。从代码上看,向量表中存放的都是中断服务函数的函数名,可我们知道C 语言中的函数名就是一个地址。(由此知道,中断函数的函数名都已经知道了)。 从那里对应地址取出服务例程的入口地址并跳入。要注意的是这里有个另类:0 号类型并不是什么入口地址,而是给出了复位后MSP 的初值。
AREA |.text|, CODE, READONLY
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
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 .
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
EXPORT WWDG\_IRQHandler \[WEAK\]
EXPORT PVD\_IRQHandler \[WEAK\]
EXPORT TAMP\_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\_Stream0\_IRQHandler \[WEAK\]
EXPORT DMA1\_Stream1\_IRQHandler \[WEAK\]
EXPORT DMA1\_Stream2\_IRQHandler \[WEAK\]
EXPORT DMA1\_Stream3\_IRQHandler \[WEAK\]
EXPORT DMA1\_Stream4\_IRQHandler \[WEAK\]
EXPORT DMA1\_Stream5\_IRQHandler \[WEAK\]
EXPORT DMA1\_Stream6\_IRQHandler \[WEAK\]
EXPORT ADC\_IRQHandler \[WEAK\]
EXPORT CAN1\_TX\_IRQHandler \[WEAK\]
EXPORT CAN1\_RX0\_IRQHandler \[WEAK\]
EXPORT CAN1\_RX1\_IRQHandler \[WEAK\]
EXPORT CAN1\_SCE\_IRQHandler \[WEAK\]
EXPORT EXTI9\_5\_IRQHandler \[WEAK\]
EXPORT TIM1\_BRK\_TIM9\_IRQHandler \[WEAK\]
EXPORT TIM1\_UP\_TIM10\_IRQHandler \[WEAK\]
EXPORT TIM1\_TRG\_COM\_TIM11\_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 RTC\_Alarm\_IRQHandler \[WEAK\]
EXPORT OTG\_FS\_WKUP\_IRQHandler \[WEAK\]
EXPORT TIM8\_BRK\_TIM12\_IRQHandler \[WEAK\]
EXPORT TIM8\_UP\_TIM13\_IRQHandler \[WEAK\]
EXPORT TIM8\_TRG\_COM\_TIM14\_IRQHandler \[WEAK\]
EXPORT TIM8\_CC\_IRQHandler \[WEAK\]
EXPORT DMA1\_Stream7\_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\_DAC\_IRQHandler \[WEAK\]
EXPORT TIM7\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream0\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream1\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream2\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream3\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream4\_IRQHandler \[WEAK\]
EXPORT ETH\_IRQHandler \[WEAK\]
EXPORT ETH\_WKUP\_IRQHandler \[WEAK\]
EXPORT CAN2\_TX\_IRQHandler \[WEAK\]
EXPORT CAN2\_RX0\_IRQHandler \[WEAK\]
EXPORT CAN2\_RX1\_IRQHandler \[WEAK\]
EXPORT CAN2\_SCE\_IRQHandler \[WEAK\]
EXPORT OTG\_FS\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream5\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream6\_IRQHandler \[WEAK\]
EXPORT DMA2\_Stream7\_IRQHandler \[WEAK\]
EXPORT USART6\_IRQHandler \[WEAK\]
EXPORT I2C3\_EV\_IRQHandler \[WEAK\]
EXPORT I2C3\_ER\_IRQHandler \[WEAK\]
EXPORT OTG\_HS\_EP1\_OUT\_IRQHandler \[WEAK\]
EXPORT OTG\_HS\_EP1\_IN\_IRQHandler \[WEAK\]
EXPORT OTG\_HS\_WKUP\_IRQHandler \[WEAK\]
EXPORT OTG\_HS\_IRQHandler \[WEAK\]
EXPORT DCMI\_IRQHandler \[WEAK\]
EXPORT CRYP\_IRQHandler \[WEAK\]
EXPORT HASH\_RNG\_IRQHandler \[WEAK\]
EXPORT FPU\_IRQHandler \[WEAK\]
//如下定义一个空函数
WWDG_IRQHandler
PVD_IRQHandler
TAMP_STAMP_IRQHandler
RTC_WKUP_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Stream0_IRQHandler
DMA1_Stream1_IRQHandler
DMA1_Stream2_IRQHandler
DMA1_Stream3_IRQHandler
DMA1_Stream4_IRQHandler
DMA1_Stream5_IRQHandler
DMA1_Stream6_IRQHandler
ADC_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_TIM9_IRQHandler
TIM1_UP_TIM10_IRQHandler
TIM1_TRG_COM_TIM11_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
RTC_Alarm_IRQHandler
OTG_FS_WKUP_IRQHandler
TIM8_BRK_TIM12_IRQHandler
TIM8_UP_TIM13_IRQHandler
TIM8_TRG_COM_TIM14_IRQHandler
TIM8_CC_IRQHandler
DMA1_Stream7_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_DAC_IRQHandler
TIM7_IRQHandler
DMA2_Stream0_IRQHandler
DMA2_Stream1_IRQHandler
DMA2_Stream2_IRQHandler
DMA2_Stream3_IRQHandler
DMA2_Stream4_IRQHandler
ETH_IRQHandler
ETH_WKUP_IRQHandler
CAN2_TX_IRQHandler
CAN2_RX0_IRQHandler
CAN2_RX1_IRQHandler
CAN2_SCE_IRQHandler
OTG_FS_IRQHandler
DMA2_Stream5_IRQHandler
DMA2_Stream6_IRQHandler
DMA2_Stream7_IRQHandler
USART6_IRQHandler
I2C3_EV_IRQHandler
I2C3_ER_IRQHandler
OTG_HS_EP1_OUT_IRQHandler
OTG_HS_EP1_IN_IRQHandler
OTG_HS_WKUP_IRQHandler
OTG_HS_IRQHandler
DCMI_IRQHandler
CRYP_IRQHandler
HASH_RNG_IRQHandler
FPU_IRQHandler
AREA |.text|, CODE, READONLY定义一个名称为.text 的代码段,可读程序段。
PROC与ENDP组合在汇编中定义一段子函数
类似与这种形式:EXPORT FPU_IRQHandler [WEAK]的用法,其中WEAK是弱定义的意思,如果外部定义了就先使用外部定义的内容,若未定义则会执行地下的空函数且返回到空函数(死循环)。
这以部分也就是把初始化的堆栈地址赋值给单片机的对应寄存器以方便C程序进行分配释放使用。
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
使用微库就意味着我们不想使用MDK提供的库函数,而想用自己定义的库函数,比如说printf函数。那么这一点是怎样实现的呢?我们以printf函数为例进行说明。不使用微库而使用系统库在连接程序时,肯定会把系统中包含printf函数的库拿来调用参与连接,即代码段有系统库的参与。在启动过程中,不使用微库而使用系统库在初始化栈的时候,还需要初始化堆(猜测系统库需要用到堆),而使用微库则是不需要的。另外,在执行__main函数的过程中,不仅需要完成“使用微库”情况下的所有工作,额外的工作还需要进行库的初始化,才能使用系统库(这一部分我还没有深入)。使用微库而不使用系统库在程序连接时,不会把包含printf函数的库连接到终极目标文件中,而使用我们定义的库。启动时需要完成的工作就是之前论述的步骤相比使用系统库,启动过程步骤更少。
启动文件XXX.s从头开始执行,(C语言上分为栈、堆、.bss、.data、.code段)依次定义了栈,堆,代码段|.text|,初始化向量表后又初始化了系统的时钟的及C函数的跳转,其中__main 是一个标准的C 库函数,主要作用是初始化用户堆栈(初始化数据段在.data段,未初始化数据段在.bss数据段上)最终调用main 函数。还需要清楚的一点是假设STM32被设置为从内部FLASH启动中断向量表起始地位为0x8000000; 则栈顶地址存放于0x8000000处,而复位中断服务入口地址存放于0x8000004处。当STM32到复位信号后,则从0x80000004处取出复位中断服务入口地址继而执行复位中断服务程序,; 然后跳转__main函数最后在——main()函数的引导下进入C语言的用户main()函数开始执行。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章