找回密码
 注册

QQ登录

只需一步,快速开始

搜索

解析 STM32 的启动过程

[复制链接]
山海致远 发表于 2013-11-9 23:43:56 | 显示全部楼层 |阅读模式
当前的嵌入式应用程序开发过程里,并且C语言成为了绝大部分场合的最佳选择。如此一来main函数似乎成为了理所当然的起点——因为C程序往往从main函数开始执行。但一个经常会被忽略的问题是:微控制器(单片机)上电后,是如何寻找到并执行main函数的呢?很显然微控制器无法从硬件上定位main函数的入口地址,因为使用C语言作为开发语言后,变量/函数的地址便由编译器在编译时自行分配,这样一来main函数的入口地址在微控制器的内部存储空间中不再是绝对不变的。相信读者都可以回答这个问题,答案也许大同小异,但肯定都有个关键词,叫“启动文件”,用英文单词来描述是“Bootloader”。


无论性能高下,结构简繁,价格贵贱,每一种微控制器(处理器)都必须有启动文件,启动文件的作用便是负责执行微控制器从“复位”到“开始执行main函数”中间这段时间(称为启动过程)所必须进行的工作。最为常见的51,AVR或MSP430等微控制器当然也有对应启动文件,但开发环境往往自动完整地提供了这个启动文件,不需要开发人员再行干预启动过程,只需要从main函数开始进行应用程序的设计即可。


话题转到STM32微控制器,无论是keil
uvision4还是IAR EWARM开发环境,ST公司都提供了现成的直接可用的启动文件,程序开发人员可以直接引用启动文件后直接进行C应用程序的开发。这样能大大减小开发人员从其它微控制器平台跳转至STM32平台,也降低了适应STM32微控制器的难度(对于上一代ARM的当家花旦ARM9,启动文件往往是第一道难啃却又无法逾越的坎)。

相对于ARM上一代的主流ARM7/ARM9内核架构,新一代Cortex内核架构的启动方式有了比较大的变化。ARM7/ARM9内核的控制器在复位后,CPU会从存储空间的绝对地址0x000000取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为0x000000(PC = 0x000000)同时中断向量表的位置并不是固定的。而Cortex-M3内核则正好相反,有3种情况:
1、        通过boot引脚设置可以将中断向量表定位于SRAM区,即起始地址为0x2000000,同时复位后PC指针位于0x2000000处;
2、        通过boot引脚设置可以将中断向量表定位于FLASH区,即起始地址为0x8000000,同时复位后PC指针位于0x8000000处;
3、        通过boot引脚设置可以将中断向量表定位于内置Bootloader区,本文不对这种情况做论述;
而Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。对比ARM7/ARM9内核,Cortex-M3内核则是固定了中断向量表的位置而起始地址是可变化的。
有了上述准备只是后,下面以STM32的2.02固件库提供的启动文件“stm32f10x_vector.s”为模板,对STM32的启动过程做一个简要而全面的解析。
程序清单一:
;文件“stm32f10x_vector.s”,其中注释为行号
DATA_IN_ExtSRAM  EQU     0                                        ;1
Stack_Size         EQU     0x00000400                        ;2
AREA    STACK, NOINIT, READWRITE, ALIGN = 3                ;3
Stack_Mem        SPACE   Stack_Size                        ;4
__initial_sp                                                                        ;5
Heap_Size         EQU     0x00000400                        ;6
AREA    HEAP, NOINIT, READWRITE, ALIGN = 3                ;7
__heap_base                                                                        ;8
Heap_Mem        SPACE   Heap_Size                        ;9
__heap_limit                                                                        ;10
THUMB                                                                                ;11
PRESERVE8                                                                        ;12
IMPORT  NMIException                                                ;13
IMPORT  HardFaultException                         ;14
IMPORT  MemManageException                                ;15
IMPORT  BusFaultException                                ;16
IMPORT  UsageFaultException                         ;17
IMPORT  SVCHandler                                        ;18
IMPORT  DebugMonitor                                 ;19
IMPORT  PendSVC                                         ;20
IMPORT  SysTickHandler                                 ;21
IMPORT  WWDG_IRQHandler                         ;22
IMPORT  PVD_IRQHandler                                                ;23
IMPORT  TAMPER_IRQHandler                                        ;24
IMPORT  RTC_IRQHandler                                                ;25
IMPORT  FLASH_IRQHandler                                        ;26
IMPORT  RCC_IRQHandler                                                ;27
IMPORT  EXTI0_IRQHandler                                                ;28
IMPORT  EXTI1_IRQHandler                                                ;29
IMPORT  EXTI2_IRQHandler                                                ;30
IMPORT  EXTI3_IRQHandler                                                ;31
IMPORT  EXTI4_IRQHandler                                                ;32
IMPORT  DMA1_Channel1_IRQHandler                        ;33
IMPORT  DMA1_Channel2_IRQHandler                        ;34
IMPORT  DMA1_Channel3_IRQHandler                        ;35
IMPORT  DMA1_Channel4_IRQHandler                        ;36
IMPORT  DMA1_Channel5_IRQHandler                        ;37
IMPORT  DMA1_Channel6_IRQHandler                        ;38
IMPORT  DMA1_Channel7_IRQHandler                        ;39
IMPORT  ADC1_2_IRQHandler                                        ;40
IMPORT  USB_HP_CAN_TX_IRQHandler                        ;41
IMPORT  USB_LP_CAN_RX0_IRQHandler                        ;42
IMPORT  CAN_RX1_IRQHandler                                        ;43
IMPORT  CAN_SCE_IRQHandler                                        ;44
IMPORT  EXTI9_5_IRQHandler                                        ;45
IMPORT  TIM1_BRK_IRQHandler                                        ;46
IMPORT  TIM1_UP_IRQHandler                                        ;47
IMPORT  TIM1_TRG_COM_IRQHandler                        ;48
IMPORT  TIM1_CC_IRQHandler                                        ;49
IMPORT  TIM2_IRQHandler                                                ;50
IMPORT  TIM3_IRQHandler                                                ;51
IMPORT  TIM4_IRQHandler                                                ;52
IMPORT  I2C1_EV_IRQHandler                                        ;53
IMPORT  I2C1_ER_IRQHandler                                        ;54
IMPORT  I2C2_EV_IRQHandler                                        ;55
IMPORT  I2C2_ER_IRQHandler                                        ;56
IMPORT  SPI1_IRQHandler                                                ;57
IMPORT  SPI2_IRQHandler                                                ;58
IMPORT  USART1_IRQHandler                                        ;59
IMPORT  USART2_IRQHandler                                        ;60
IMPORT  USART3_IRQHandler                                        ;61
IMPORT  EXTI15_10_IRQHandler                                        ;62
IMPORT  RTCAlarm_IRQHandler                                        ;63
IMPORT  USBWakeUp_IRQHandler                                ;64
IMPORT  TIM8_BRK_IRQHandler                                        ;65
IMPORT  TIM8_UP_IRQHandler                                        ;66
IMPORT  TIM8_TRG_COM_IRQHandler                        ;67
IMPORT  TIM8_CC_IRQHandler                                        ;68
IMPORT  ADC3_IRQHandler                                                ;69
IMPORT  FSMC_IRQHandler                                                ;70
IMPORT  SDIO_IRQHandler                                                ;71
IMPORT  TIM5_IRQHandler                                                ;72
IMPORT  SPI3_IRQHandler                                                ;73
IMPORT  UART4_IRQHandler                                        ;74
IMPORT  UART5_IRQHandler                                        ;75
IMPORT  TIM6_IRQHandler                                                ;76
IMPORT  TIM7_IRQHandler                                                ;77
IMPORT  DMA2_Channel1_IRQHandler                        ;78
IMPORT  DMA2_Channel2_IRQHandler                        ;79
IMPORT  DMA2_Channel3_IRQHandler                        ;80
IMPORT  DMA2_Channel4_5_IRQHandler                          ;81
AREA    RESET, DATA, READONLY                                        ;82
EXPORT  __Vectors                                                        ;83
__Vectors                                                                          ;84
DCD  __initial_sp                                                       ;85
DCD  Reset_Handler                                                        ;86
DCD  NMIException                                                        ;87
DCD  HardFaultException                                                ;88
DCD  MemManageException                                        ;89
DCD  BusFaultException                                                ;90
DCD  UsageFaultException                                                ;91
DCD  0                                                                                ;92
DCD  0                                                                                ;93
DCD  0                                                                                ;94
DCD  0                                                                                ;95
DCD  SVCHandler                                                                ;96
DCD  DebugMonitor                                                        ;97
DCD  0                                                                                ;98
DCD  PendSVC                                                                ;99
DCD  SysTickHandler                                                        ;100
DCD  WWDG_IRQHandler                                                ;101
DCD  PVD_IRQHandler                                                        ;102
DCD  TAMPER_IRQHandler                                                ;103
DCD  RTC_IRQHandler                                                        ;104
DCD  FLASH_IRQHandler                                                ;105
DCD  RCC_IRQHandler                                                        ;106
DCD  EXTI0_IRQHandler                                                ;107
DCD  EXTI1_IRQHandler                                                ;108
DCD  EXTI2_IRQHandler                                                ;109
DCD  EXTI3_IRQHandler                                                ;110
DCD  EXTI4_IRQHandler                                                ;111
DCD  DMA1_Channel1_IRQHandler                                ;112
DCD  DMA1_Channel2_IRQHandler                                ;113
DCD  DMA1_Channel3_IRQHandler                                ;114
DCD  DMA1_Channel4_IRQHandler                                ;115
DCD  DMA1_Channel5_IRQHandler                                ;116
DCD  DMA1_Channel6_IRQHandler                                ;117
DCD  DMA1_Channel7_IRQHandler                                ;118
DCD  ADC1_2_IRQHandler                                                ;119
DCD  USB_HP_CAN_TX_IRQHandler                                ;120
DCD  USB_LP_CAN_RX0_IRQHandler                                ;121
DCD  CAN_RX1_IRQHandler                                                ;122
DCD  CAN_SCE_IRQHandler                                                ;123
DCD  EXTI9_5_IRQHandler                                                ;124
DCD  TIM1_BRK_IRQHandler                                        ;125
DCD  TIM1_UP_IRQHandler                                                ;126
DCD  TIM1_TRG_COM_IRQHandler                                ;127
DCD  TIM1_CC_IRQHandler                                                ;128
DCD  TIM2_IRQHandler                                                ;129
DCD  TIM3_IRQHandler                                                ;130
DCD  TIM4_IRQHandler                                                ;131
DCD  I2C1_EV_IRQHandler                                                ;132
DCD  I2C1_ER_IRQHandler                                                ;133
DCD  I2C2_EV_IRQHandler                                                ;134
DCD  I2C2_ER_IRQHandler                                                ;135
DCD  SPI1_IRQHandler                                                        ;136
DCD  SPI2_IRQHandler                                                        ;137
DCD  USART1_IRQHandler                                                ;138
DCD  USART2_IRQHandler                                                ;139
DCD  USART3_IRQHandler                                                ;140
DCD  EXTI15_10_IRQHandler                                        ;141
DCD  RTCAlarm_IRQHandler                                                ;142
DCD  USBWakeUp_IRQHandler                                        ;143
DCD  TIM8_BRK_IRQHandler                                        ;144
DCD  TIM8_UP_IRQHandler                                                ;145
DCD  TIM8_TRG_COM_IRQHandler                                ;146
DCD  TIM8_CC_IRQHandler                                                ;147
DCD  ADC3_IRQHandler                                                ;148
DCD  FSMC_IRQHandler                                                ;149
DCD  SDIO_IRQHandler                                                        ;150
DCD  TIM5_IRQHandler                                                ;151
DCD  SPI3_IRQHandler                                                        ;152
DCD  UART4_IRQHandler                                                ;153
DCD  UART5_IRQHandler                                                ;154
DCD  TIM6_IRQHandler                                                ;155
DCD  TIM7_IRQHandler                                                ;156
DCD  DMA2_Channel1_IRQHandler                                ;157
DCD  DMA2_Channel2_IRQHandler                                ;158
DCD  DMA2_Channel3_IRQHandler                                ;159
DCD  DMA2_Channel4_5_IRQHandler            ;160
AREA    |.text|, CODE, READONLY                                ;161
Reset_Handler    PROC                                                ;162
EXPORT  Reset_Handler                                                ;163
IF      DATA_IN_ExtSRAM == 1                                        ;164
LDR R0,= 0x00000114                                                        ;165
LDR R1,= 0x40021014                                                        ;166
STR R0,[R1]                                                         ;167
LDR R0,= 0x000001E0                                                        ;168
LDR R1,= 0x40021018                                                        ;169
STR R0,[R1]                                                                        ;170
LDR R0,= 0x44BB44BB                                                        ;171
LDR R1,= 0x40011400                                                        ;172
STR R0,[R1]                                                                        ;173
LDR R0,= 0xBBBBBBBB                                                         ;174
LDR R1,= 0x40011404                                                        ;175
STR R0,[R1]                                                                        ;176
LDR R0,= 0xB44444BB                                                         ;177
LDR R1,= 0x40011800                                                        ;178
STR R0,[R1]                                                                        ;179
LDR R0,= 0xBBBBBBBB                                                         ;180
LDR R1,= 0x40011804                                                        ;181
STR R0,[R1]                                                                        ;182
LDR R0,= 0x44BBBBBB                                                         ;183
LDR R1,= 0x40011C00                                                        ;184
STR R0,[R1]                                                                        ;185
LDR R0,= 0xBBBB4444                                                         ;186
LDR R1,= 0x40011C04                                                        ;187
STR R0,[R1]                                                                        ;188
LDR R0,= 0x44BBBBBB                                                        ;189
LDR R1,= 0x40012000                                                        ;190
STR R0,[R1]                                                                        ;191
LDR R0,= 0x44444B44                                                        ;192
LDR R1,= 0x40012004                                                        ;193
STR R0,[R1]                                                                      ;194
LDR R0,= 0x00001011                                                        ;195
LDR R1,= 0xA0000010                                                        ;196
STR R0,[R1]                                                                        ;197
LDR R0,= 0x00000200                                                         ;198
LDR R1,= 0xA0000014                                                        ;199
STR R0,[R1]                                                                        ;200
ENDIF                                                                       ;201
IMPORT  __main                                                                ;202
LDR     R0, =__main                                                        ;203
BX      R0                                                                        ;204
ENDP                                                                                ;205
ALIGN                                                                                ;206
IF      :DEF:__MICROLIB                                   ;207
EXPORT  __initial_sp                                                        ;208
EXPORT  __heap_base                                                        ;209
EXPORT  __heap_limit                                                        ;210
ELSE                                                                                        ;211
IMPORT  __use_two_region_memory                                ;212
EXPORT  __user_initial_stackheap                                ;213
__user_initial_stackheap                                                ;214
LDR     R0, = Heap_Mem                                                ;215
LDR     R1, = (Stack_Mem + Stack_Size)                        ;216
LDR     R2, = (Heap_Mem +  Heap_Size)                        ;217
LDR     R3, = Stack_Mem                                                ;218
BX      LR                                                                        ;219
ALIGN                                                                                ;220
ENDIF                                                                                ;221
END                                                                                        ;222
ENDIF                                                                                ;223
END                                                                                        ;224
        如程序清单一,STM32的启动代码一共224行,使用了汇编语言编写,这其中的主要原因下文将会给出交代。现在从第一行开始分析:
        第1行:定义是否使用外部SRAM,为1则使用,为0则表示不使用。此语行若用C语言表达则等价于:
#define        DATA_IN_ExtSRAM         0
        第2行:定义栈空间大小为0x00000400个字节,即1Kbyte。此语行亦等价于:
#define        Stack_Size                        0x00000400
        第3行:伪指令AREA,表示
        第4行:开辟一段大小为Stack_Size的内存空间作为栈。
        第5行:标号__initial_sp,表示栈空间顶地址。
        第6行:定义堆空间大小为0x00000400个字节,也为1Kbyte。
        第7行:伪指令AREA,表示
        第8行:标号__heap_base,表示堆空间起始地址。
        第9行:开辟一段大小为Heap_Size的内存空间作为堆。
        第10行:标号__heap_limit,表示堆空间结束地址。
        第11行:告诉编译器使用THUMB指令集。
        第12行:告诉编译器以8字节对齐。
        第13—81行:IMPORT指令,指示后续符号是在外部文件定义的(类似C语言中的全局变量声明),而下文可能会使用到这些符号。
        第82行:定义只读数据段,实际上是在CODE区(假设STM32从FLASH启动,则此中断向量表起始地址即为0x8000000)
        第83行:将标号__Vectors声明为全局标号,这样外部文件就可以使用这个标号。
        第84行:标号__Vectors,表示中断向量表入口地址。
        第85—160行:建立中断向量表。
        第161行:
        第162行:复位中断服务程序,PROC…ENDP结构表示程序的开始和结束。
        第163行:声明复位中断向量Reset_Handler为全局属性,这样外部文件就可以调用此复位中断服务。
        第164行:IF…ENDIF为预编译结构,判断是否使用外部SRAM,在第1行中已定义为“不使用”。
        第165—201行:此部分代码的作用是设置FSMC总线以支持SRAM,因不使用外部SRAM因此此部分代码不会被编译。
        第202行:声明__main标号。
        第203—204行:跳转__main地址执行。
        第207行:IF…ELSE…ENDIF结构,判断是否使用DEF:__MICROLIB(此处为不使用)。
        第208—210行:若使用DEF:__MICROLIB,则将__initial_sp,__heap_base,__heap_limit亦即栈顶地址,堆始末地址赋予全局属性,使外部程序可以使用。
        第212行:定义全局标号__use_two_region_memory。
        第213行:声明全局标号__user_initial_stackheap,这样外程序也可调用此标号。
        第214行:标号__user_initial_stackheap,表示用户堆栈初始化程序入口。
        第215—218行:分别保存栈顶指针和栈大小,堆始地址和堆大小至R0,R1,R2,R3寄存器。
        第224行:程序完毕。
以上便是STM32的启动代码的完整解析,接下来对几个小地方做解释:
1、        AREA指令:伪指令,用于定义代码段或数据段,后跟属性标号。其中比较重要的一个标号为“READONLY”或者“READWRITE”,其中“READONLY”表示该段为只读属性,联系到STM32的内部存储介质,可知具有只读属性的段保存于FLASH区,即0x8000000地址后。而“READONLY”表示该段为“可读写”属性,可知“可读写”段保存于SRAM区,即0x2000000地址后。由此可以从第3、7行代码知道,堆栈段位于SRAM空间。从第82行可知,中断向量表放置与FLASH区,而这也是整片启动代码中最先被放进FLASH区的数据。因此可以得到一条重要的信息:0x8000000地址存放的是栈顶地址__initial_sp,0x8000004地址存放的是复位中断向量Reset_Handler(STM32使用32位总线,因此存储空间为4字节对齐)。
2、        DCD指令:作用是开辟一段空间,其意义等价于C语言中的地址符“&”。因此从第84行开始建立的中断向量表则类似于使用C语言定义了一个指针数组,其每一个成员都是一个函数指针,分别指向各个中断服务函数。
3、        标号:前文多处使用了“标号”一词。标号主要用于表示一片内存空间的某个位置,等价于C语言中的“地址”概念。地址仅仅表示存储空间的一个位置,从C语言的角度来看,变量的地址,数组的地址或是函数的入口地址在本质上并无区别。
4、        第202行中的__main标号并不表示C程序中的main函数入口地址,因此第204行也并不是跳转至main函数开始执行C程序。__main标号表示C/C++标准实时库函数里的一个初始化子程序__main的入口地址。该程序的一个主要作用是初始化堆栈(对于程序清单一来说则是跳转__user_initial_stackheap标号进行初始化堆栈的),并初始化映像文件,最后跳转C程序中的main函数。这就解释了为何所有的C程序必须有一个main函数作为程序的起点——因为这是由C/C++标准实时库所规定的——并且不能更改,因为C/C++标准实时库并不对外界开发源代码。因此,实际上在用户可见的前提下,程序在第204行后就跳转至.c文件中的main函数,开始执行C程序了。
至此可以总结一下STM32的启动文件和启动过程。首先对栈和堆的大小进行定义,并在代码区的起始处建立中断向量表,其第一个表项是栈顶地址,第二个表项是复位中断服务入口地址。然后在复位中断服务程序中跳转¬¬C/C++标准实时库的__main函数,完成用户堆栈等的初始化后,跳转.c文件中的main函数开始执行C程序。假设STM32被设置为从内部FLASH启动(这也是最常见的一种情况),中断向量表起始地位为0x8000000,则栈顶地址存放于0x8000000处,而复位中断服务入口地址存放于0x8000004处。当STM32遇到复位信号后,则从0x80000004处取出复位中断服务入口地址,继而执行复位中断服务程序,然后跳转__main函数,最后进入mian函数,来到C的世界。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|ELEOK |网站地图

GMT+8, 2024-11-21 18:34

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表