STM32的启动过程

先看一下STM32启动的总体过程:

1、设置堆栈

2、跳转到Reset_Handler

3、Reset_Handler调用SystemInit完成时钟、中断向量偏移的初始工作。然后跳转到__main,__main函数会完成RW、ZI数据段的重定位工作,即将ROM中的RW数据拷贝到RAM文件中,然后将ZI段清零。

4、跳转到真正的main函数

然后我们来具体看一下ST官方提供的启动文件,即startup_stm32fxxx.s。我们以stm32F767为例,分析这个文件的具体内容。

首先来看一下启动文件设计的几个汇编指令。

然后再来看这个文件

一、栈(Stack)的设置

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

首先使用EQU给0x00000400取名称为Stack_Size,然后汇编一个数据段,名称为STACK,不初始化,可读可写,8(2^3)字节对齐,然后将Stack_Size大小字节的内存分配给Stakce_Mem,最后_initial_sp为栈顶地址。

这段代码的含义就是开辟大小为0x00000400(1KB)的空间,名称为STACK,不初始化,可读可写,8字节对齐。

二、堆(Heap)的设置

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

和上面一样,首先使用EQU给0x00000200取名称为Heap_Size,然后汇编一个数据段,名称为HEAP,不初始化,可读可写,8字节对齐,然后_heap_base为堆的起始地址,_heap_limit为堆的终止地址。中间那个为将上面设置的内存大小分配出来。

这段代码的含义是开辟大小为0x00000200(512B)的空间为堆,名称为HEAP,不初始化,可读可写,8字节对齐。并且提供了堆起始和结束位置的标签。

三、中断向量表

                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     CAN3_TX_IRQHandler                ; CAN3 TX
                DCD     CAN3_RX0_IRQHandler               ; CAN3 RX0
                DCD     CAN3_RX1_IRQHandler               ; CAN3 RX1
                DCD     CAN3_SCE_IRQHandler               ; CAN3 SCE
                DCD     JPEG_IRQHandler                   ; JPEG
                DCD     MDIOS_IRQHandler                  ; MDIOS
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

PRESERVE8:指定当前文件的堆栈按照8字节对齐

THUMB:表示后面的指令兼容THUMB指令。THUMB是以前的ARM的指令集,16bit,现在Cotex-M系列都使用THUMB-2指令集,是32位的,兼容THUMB指令集。

AREA    RESET, DATA, READONLY

定义了一个名为“RESET”的内存区域,并且只读。

DATA:指示该内存区域中包含数据。这表示该区域中存储的内容是在程序运行时可以读取的数据,而不是程序的指令代码。

READONLY:指示该内存区域中的数据是只读的,不能被修改。这意味着在程序运行时,不能对该区域中的数据进行写入操作,只能进行读取操作。

然后声明了几个全局属性的标号。

__Vectors:向量表起始地址

__Vectors_End:向量表终止地址

__Vectors_Size  EQU  __Vectors_End - __Vectors:向量表大小

对于Cotex-M4内核,ARM规定向量表的起始地址存放的是栈的入口地址(这里为__initial_sp),然后存放复位中断的入口地址,上电之后,硬件会根据向量表的地址找到向量表的具体位置(可以在NVIC寄存器中设置),根据这两个地址设置SP,PC的值,之后CPU就可以从复位中断的入口函数中去指令运行。

中间那一大坨就是各个中断的入口地址,最开始的两个就是堆栈指针和复位中断。

四、复位中断函数

                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
                省略
Default_Handler PROC

                EXPORT  WWDG_IRQHandler                   [WEAK]                                        
                EXPORT  PVD_IRQHandler                    [WEAK]                      
                EXPORT  TAMP_STAMP_IRQHandler             [WEAK]         
                EXPORT  RTC_WKUP_IRQHandler               [WEAK]   
                省略
SDMMC2_IRQHandler
CAN3_TX_IRQHandler
CAN3_RX0_IRQHandler
CAN3_RX1_IRQHandler
CAN3_SCE_IRQHandler
JPEG_IRQHandler
MDIOS_IRQHandler
                B       .

                ENDP

                ALIGN

AREA    |.text|, CODE, READONLY:定义了一个只读的.text的代码段。

然后实现了Reset_Handler函数,PROC到ENDP为函数内容,声明一个弱定义函数Reset_Handler,然后导入SystemInit函数和__main函数,在SystemInit中完成时钟和中断向量偏移的工作,在__mian中完成RW和ZI的重定位工作,这个__mian最后跳到main函数中。

后面是很长的中断函数的编写,都是弱定义。

五、堆栈的初始化

                 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

对堆栈做一些设置,主要是清零。

IF,ELSE和C语言的一样。

有两条分支,首先判断是否定义了_MICROLIB,如果定义了,就调用C库完成堆栈的初始化,赋予__initial_sp,__heap_base,__heap_limit全局属性。可以在Keil中设置。

否则就采用双段存储的模式,即堆区和栈区是分开的(如果不采用双段模式,因为堆和栈的增长方向是相反的,如果装上了会导致程序崩溃),并且声明_user_initial_stackheap具有全局属性,让用户自己初始化堆栈。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值