STM32的启动过程以及启动文件详解

        有一个现象值得一说,对于大多数人来说,他们开发单片机程序的主要工作就是搞单片机应用开发,也就是大多数都是main函数之后的事,于是很多人以为单片机上电复位后直接就是从main函数开始执行程序的。如果他只是一个学生,我多多少少还能理解,毕竟他年幼无知尚未步入单片机大门,但是如果他是一个所谓的久经沙场的老工程师,这是会被笑话的。

         也许有人说,我不清楚单片机怎么启动的,我不照样可以开发单片机软件,写的代码不照样可以让产品正常工作吗?我有必要知道单片机是怎么启动的吗?有如下几点可以说明你很有必要,而且,甚至就是必须,明白单片机是如何启动的。

         首先,这是一个素养问题,既然他是一位专业单片机工程师,总会有外行或者想入行的人想请教他关于单片机的各种问题,这种情况下,他却无法回答或者给出错误解答,难免显得尴尬有失体面,还会让人觉得他不懂装懂还装大咖的感觉。

         其次,这是一个水平问题,如果他连单片机怎么启动这么基本的问题都没搞清楚,他让同行怎么相信你搞过单片机,更别说他曾经开发过多个项目,拥有丰富经验了。

         今天我就花点时间来谈谈MCU的启动整个过程,由于目前STM32大行其道,所以我以STM32为例对此进行详细阐述,以达到抛砖引玉的目的。

         STM32执行代码是以PC寄存器为指引的,PC指到哪CPU就取哪里的指令来执行,真所谓指哪打哪。上电复位之后,PC寄存器指向了中断向量表中的复位中断向量这个位置,CPU会随之取出复位中断向量中的数据,这个数据是什么呢?它不是复位中断服务函数的入口地址,而是跳转到复位中断服务函数的可执行代码。再重申一遍,复位中断向量中存放的是一条跳转指令,而不是地址CPU执行这条代码后,PC寄存器的值就被改变成了复位中断服务函数的入口地址,至此,程序运行到了复位中断服务函数。

下面就是复位中断服务函数的代码:

; Reset handler

Reset_Handler    PROC

                EXPORT  Reset_Handler             [WEAK]

     IMPORT  __main

     IMPORT  SystemInit

                 LDR     R0, =SystemInit

                 BLX     R0

                 LDR     R0, =__main

                 BX      R0

                 ENDP

简单说明这几行代码的含义:

①PROC...ENDP是定义函数(或说子程序)的伪指令,PROC代表函数的开始,而ENDP代表函数结束,所以它们总是成对出现。在这里就是定义了名为Reset_Handler的子程序。

②EXPORT是表示其后紧跟的标号提供给外部使用,所以EXPORT  Reset_Handler就代表外部文件可以调用Reset_Handler函数。

③IMPORT表示其后紧跟的标号是在别的文件中定义的,并不是本地文件(它是舶来品,不是国产的^o^),所以“IMPORT  __main”就代表对_main这个标号进行了声明,接下来就可以直接用_main了;同理,“IMPORT  SystemInit”就代表了对SystemInit这个编号进行了声明,接下来就可以直接用SystemInit了。在汇编语言中,一个标号就代表了一个程序地址,所以__main和SystemInit其实就代表了外部函数。

④LDR就是装在寄存器了,LDR  R0, =SystemInit的意思就是把SystemInit标号所代表的地址装在给R0寄存器,BLX R0的意思就是直接跳转到R0所指向的代码空间去执行代码,由于R0此时是指向SystemInit,所以就是跳转到SystemInit处执行代码;同理 LDR  R0, =__main表示把_main标号装载进R0中,其后的BX  R0代表跳转到_main处执行代码。细心的同学可能发现了一个问题,为什么一个是BLX,一个是BX呢?关于它们的区别就是如下:

BLX 指令从ARM 指令集跳转到指令中所指定的目标地址,并将处理器的工作状态有ARM 状态切换到Thumb 状态,该指令同时将PC 的当前内容保存到寄存器R14 中。因此,当子程序使用Thumb 指令集,而调用者使用ARM 指令集时,可以通过BLX 指令实现子程序的调用和处理器工作状态的切换。BX 指令跳转到指令中所指定的目标地址,目标地址处的指令既可以是ARM 指令,也可以是Thumb指令。两者的区别在于程序跳转后要不要切换工作状态。

通过上述的介绍,可见复位中断服务函数并没有传说中的那么神秘,它只不过是执行了在别的文件中定义的__main代码段和SystemInit代码段。下面简单说明__main和SystemInit分别是什么东西。

①SystemInit函数是定义在外部文件中的,那到底是定义在哪个文件中呢?

它就位于system_stm32fxxx.c中,具体代码如下:

void SystemInit(void)

{

  /* FPU settings ------------------------------------------------------------*/

  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)

    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */

  #endif

  /* Reset the RCC clock configuration to the default reset state ------------*/

  /* Set MSION bit */

  RCC->CR |= RCC_CR_MSION;

 

  /* Reset CFGR register */

  RCC->CFGR = 0x00000000;

 

  /* Reset HSEON, CSSON , HSION, and PLLON bits */

  RCC->CR &= (uint32_t)0xEAF6FFFF;

 

  /* Reset PLLCFGR register */

  RCC->PLLCFGR = 0x00001000;

 

  /* Reset HSEBYP bit */

  RCC->CR &= (uint32_t)0xFFFBFFFF;

 

  /* Disable all interrupts */

  RCC->CIER = 0x00000000;

  /* Configure the Vector Table location add offset address ------------------*/

#ifdef VECT_TAB_SRAM

  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */

#else

  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */

#endif

}

②_main属于C/C++库函数,其作用如下,该函数的具体源代码可以通过单步调试的方法查看,这里我不详细介绍了。

·完成全局/静态变量的初始化工作

·初始化堆栈

·库函数的初始化

·程序的跳转,进入main()函数。

定义了一段大小为1KB的堆栈空间,并初始化为0。(堆栈也叫栈)

//;定义一个变量Stack_Size,并赋值为0x00000400;
Stack_Size      EQU     0x00000400 
//;定义一个数据段(或数据节)STACK,不零初始化,可读可写,并以8个字节对齐;
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
//;开辟一个大小为0x00000400(即1KB)的初始化为零的连续的内存空间,并命名为Stack_Mem; 
Stack_Mem       SPACE   Stack_Size
//;_initial_sp是标号,表示堆栈的栈顶地址。
__initial_sp

定义了一段大小为0.5KB的堆空间,并初始化为0。由于未用到编译器自带的内存管理(malloc , free等),不会用到堆,故可以将堆大小设置为0。

//;定义一个变量Heap_Size,并赋值为0x00000200;
Heap_Size       EQU     0x00000200
//;定义一个数据段HEAP,不初始化为零,可读可写,并以8个字节对齐;
                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
//;_heap_base是标号,表示堆基地址
__heap_base
//;开辟一个大小为0x000000200(即512Bytes)的初始化为零的内存空间,并命名为Heap_Mem;
Heap_Mem        SPACE   Heap_Size
//;_heap_limit是标号,表示堆顶地址。
__heap_limit 

                PRESERVE8  //;表示当前文件中的堆栈区按8字节对齐
                THUMB   //;指示汇编器将THUMB后的指令翻译成T32。

系统复位时,向量表被映射到零地址。

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY  //;定一个代码段RESET,只可读;
                EXPORT  __Vectors //;定义1个全局变量 _Vectors 
                EXPORT  __Vectors_End //;定义1个全局变量 _Vectors_End 
                EXPORT  __Vectors_Size//;定义1个全局变量 _Vectors_Size
//;分别定义了多个连续的字存储单元,这些存储单元用于存储向量表。标号_Vectors是栈顶地址 //;_initial_sp,__Vectors_End表示向量表的结尾地址。
__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_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_2
                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
__Vectors_End
//;定义一个变量__Vectors_Size 用于表示向量表的大小;
__Vectors_Size  EQU  __Vectors_End - __Vectors
//;定义一个代码段|.text|,只可读;
                AREA    |.text|, CODE, READONLY

复位处理程序,可根据实际情况进行修改。

; Reset handler
Reset_Handler    PROC   
                 EXPORT  Reset_Handler             [WEAK]
     IMPORT  __main
     IMPORT  SystemInit
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

空异常处理程序(函数入口)。其中,”B .”表示跳转到当前指令,所以也就是进入无限循环当中。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  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]

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

                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

//IF…ELSE…ENDIF结构。如果使用(或定义)_MICROLIB,那么,定义了3个全局属性变量。
//否则,定义一个全局属性的标号__user_initial_stackheap,并且通知编译器本文件中
//应用的_use_two_region_memory在其他文件中定义了。注意:MiCROLIB缺省的情况下使用的是Keil C库。

分别将栈顶地址,栈底地址,堆顶地址及堆基地址存放在寄存器R1,R3,R0,R2中。

__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

//注意:堆区和栈区的名称分别代表堆区和栈区的起始地址,由于栈逆生长,所以它的名称代表栈区的栈底地址。

 

  • 11
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值