打印
[应用相关]

STM32启动流程

[复制链接]
313|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
15373042435|  楼主 | 2021-3-28 21:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 15373042435 于 2021-3-28 21:24 编辑

1.启动流程
  • 芯片在上电的时候,在内部时序逻辑的控制下,将0x0000 0000地址的内容读取到SP;将0x0000 0004地址的内容读取到PC。此时SP中存放的是栈顶地址,PC中存放的是Reset_Handler复位处理函数地址。在链接阶段,链接器就决定了程序中的各种数据和代码在FLASH中的存储地址。中断向量表存储在0x0000 0000空间上。
  • 芯片执行Reset_Handler函数,进行时钟设置,最后跳入用户程序执行main函数。
2.启动文件分析
2.1汇编语言
2.1.1伪指令
  • 伪指令是一条指令。它在程序中不是可有可无的,使用时受到严格的规范,与标准指令一样,在程序中占有固定的位置,有固定的书写格式。每条伪指令都与标准指令一样可实现特定的功能,伪指令是不能用标准指令替代的。
  • 伪指令不是一条真正的指令,没有指令代码。在程序编译过程中,伪指令的功能会被实现,但伪指令会被删除,在编译后的目标文件中,不会有伪指令的编码。
  • 也可以这样理解:指令是对计算机发出的命令,而伪指令则是对编译器发出的命令。在编译程序结束时,伪指令的使命就完成了。
  • 伪指令是为程序开发工程师提供辅助的程序表达,让编译器实现一些标准指令所不能表达的内容。
2.1.2启动文件中用到的伪指令
  • EQU:给数字常量取一个符号名,相当于C语言中的define
  • AREA:指示汇编程序汇编新的代码或数据段。
  • SPACE:分配内存空间
  • PRESERVE8:当前文件堆栈需按照8字节对齐
  • EXPORT:声明一个标号具有全局属性,可被外部的文件使用
  • DCD:以字为单位分配内存,要求4字节对齐,并要求初始化这些内存
  • PROC:定义子程序,与ENDP成对使用,表示子程序结束
  • MSR:Move to Special register from Register.  恢复到特殊寄存器
  • WEAK:弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。要注意的是:这个不是ARM的指令,是编译器的,这里放在一起只是为了方便。
  • IMPORT:声明标号来自外部文件,跟C语言中的EXTERN关键字类似
  • B:跳转到一个标号ALIGN编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示4字节对齐。要注意的是:这个不是ARM的指令,是编译器的,这里放在一起只是为了方便。
  • END:到达文件的末尾,文件结束
  • IF,ELSE,ENDIF:汇编条件分支语句,跟C语言的if else类似
V3.5.0库 startup_stm32f10x_hd.s 为例。版本不同,启动文件不同(hd.s or md.s),启动代码略有差异。

2.2建立栈
栈用于存放局部变量、函数形参和返回值,为函数运行的必要条件。

Stack_Size     EQU     0x00001400                                    AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem    SPACE   Stack_Size
__initial_sp                                        ; stack used for SystemInit_ExtMemCtl     
__initial_spTop  EQU    0x20000400     ; always internal RAM used
  • STACK_SIZE  EQU  0x00001400:给0x00001400起了个别名STACK_SIZE,类似于C语言的define
  • AREA  STACK,NOINIT,READWRITE,ALIGN=3:开辟一个数据段,名字是STACK,不初始化、可读写,2*2*2=8字节对齐。开辟的数据段空间为0,需要后面的内容把数据段空间撑起来。
  • Stack_Mem  SPACE  Stack_Size__initial_sp  :为STACK数据段分配一段以STACK_SIZE为长度的内存空间,栈顶地址是__initial_sp。栈的生长是由高地址向低地址生长的。
  • __initial_spTop EQU 0x20000400:给0x20000400起了个别名__initial_spTop 这部分代码建立了2个栈。
第一个栈
栈顶地址是__initial_spTop,用于启动代码文件中的SystemInit_ExtMemCtl函数。对于startup_stm32f10x_md.s,是没有这个函数的,所以也就没有建立这个栈。
SystemInit_ExtMemCtl:用来初始化外部ram。                                                   
下面的代码来自启动文件。            
SystemInit_ExtMemCtl     PROC               
                                     EXPORT  SystemInit_ExtMemCtl      [WEAK]            
                                     BX    LR  
                                     ENDP
  • SystemInit_ExtMemCtl:函数名
  • PROC:函数开始;ENDP:函数结束
  • EXPORT:此函数外部可引用,类似于extern
  • WEAK:弱定义;如果外部文件定义了SystemInit_ExtMemCtl,则使用外部函数。
  • BX:函数指针跳转
  • LR:函数调用的返回地址
#ifdef DATA_IN_ExtSRAM
void SystemInit_ExtMemCtl(void)
{  
  省略了代码
}#endif /* DATA_IN_ExtSRAM */
如果定义DATA_IN_ExtSRAM了,则使用外部文件定义的SystemInit_ExtMemCtl函数。

第二个栈
栈顶地址是__initial_sp,用于用户的应用程序。

2.3建立堆
堆用来存放程序运行中动态分布的内存段,比如malloc函数。
Heap_Size   EQU    0x00000400               
                 AREA   HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem  SPACE   Heap_Size
__heap_limit
  • __heap_base:堆的开始地址
  • __heap_limit:堆的结束地址
  • Heap_Size:堆的空间
2.4建立中断向量表
PRESERVE8THUMB
  • PRESERVE8:指定当前文件所占空间按照8字节对齐
  • THUMB:表示后面的指令兼容THUMB指令集
; Vector Table Mapped to Address 0 at Reset               
    AREA   RESET, DATA, READONLY               
    EXPORT  __Vectors               
    EXPORT  __Vectors_End               
    EXPORT  __Vectors_Size__Vectors      
    DCD       __initial_spTop            ; Top of Stack               
    DCD       Reset_Handler             ; Reset Handler               
    DCD       NMI_Handler               ; NMI Handler               
    中间内容有省略               
    DCD       DMA2_Channel3_IRQHandler  ; DMA2 Channel3               
    DCD       DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End
__Vectors_Size  EQU   __Vectors_End - __Vectors
  • AREA    RESET, DATA, READONLY:开辟数据段,段名是RESET,只读。
  • DCD:以字为单位分配内存。多个DCD,用于分配多个连续的空间。
  • __Vectors:连续空间的开始地址。
  • __Vectors_End:连续空间的结束地址。
  • __Vectors_Size:连续空间的大小。
  • __initial_spTop:为启动过程中,用到的SystemInit_ExtMemCtl函数分配的栈。当启动流程完成,切换到用户main函数之前,需要将栈修改为用户栈__initial_sp。
STM32有三个特殊的寄存器:SP、PC、LR。

SP:栈顶指针。栈用于存放局部变量、函数形参和返回值,为函数运行的必要条件。

PC:程序计数器。用于读取程序指令。

LR:保存函数调用的返回地址。

芯片在上电的时候,在内部时序逻辑的控制下,将0x0000 0000地址的内容保存到SP;将0x0000 0004地址的内容保存到PC

中断向量表存储在0x0000 0000空间上,所以SP将保存栈顶地址__initial_spTop,PC将保存复位处理函数地址Reset_Handler。所以芯片上电以后,首先执行的便是Reset_Handler函数。

; Reset handler routine
    Reset_Handler   PROC               
    EXPORT  Reset_Handler             [WEAK]               
    IMPORT  __main               
    LDR     R0, = SystemInit_ExtMemCtl ; initialize external memory controller               
    BLX     R0               
    LDR     R1, = __initial_sp        ; restore original stack pointer               
    MSR    MSP, R1                                   
    LDR    R0, =__main               
    BX      R0               
    ENDP
在Reset_Handler函数中:
  • 程序首先执行SystemInit_ExtMemCtl函数。如果没有定义DATA_IN_ExtSRAM,程序将会直接从SystemInit_ExtMemCtl函数跳出。
  • 接下来,程序顺序执行
    LDR     R1, = __initial_sp; restore original stack pointer      
   MSR     MSP, R1
将栈顶指针替换为__initial_sp。因为此时SystemInit_ExtMemCtl已经执行完成,需要将此时的栈顶地址修改为用户栈区。

3.执行__main函数。在__main函数中,将跳入用户主程序main。



使用特权

评论回复
沙发
小蜜蜂00| | 2021-3-30 04:08 | 只看该作者
很详细

使用特权

评论回复
板凳
17883657069| | 2021-3-31 09:17 | 只看该作者
谢谢楼主分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

39

主题

75

帖子

0

粉丝