STM32启动过程分析

    

硬件: STM32F1系列

软件环境:Keil 4.54

注:本文中提到的RTOS以RT-Thread为例,不涵盖所有RTOS的情况

 

在Keil MDK中新建工程时会根据所选的device自动生成启动代码文件startup.s,该文件的作用可根据其头部的注释看出

This module performs:
;* - Set the initial SP
;* - Set the initial PC == Reset_Handler
;* - Set the vector table entries with the exceptions ISR address
;* - Configure the clock system
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the CortexM3 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.

 在startup.s中,完成了堆栈大小和中断向量表的设置。默认的栈大小为400字节,堆大小为200字节,可自行更改。这个栈在bare-metal系统中为全局所使用,在带RTOS的系统中被操作系统内核和中断所使用,如果无多层函数嵌套调用,通常是够用的。堆在使用malloc()的时候会被用到。堆栈的设置必须用汇编语言完成,因为C语言通常会用到函数,而函数调用是依赖于堆栈的。关于startup.s的详细分析请参考004:STM32启动文件详解及SystemInit函数分析一文

 

 系统上电后,默认从地址为0的地方开始执行。在STM32中,若根据boot引脚选择从主闪存存储器启动,则主闪存存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址(0x0800 0000)访问它。0x08000000开始的一段区域存放的是中断向量表(即startup.s中__Vectors开始的部分)
Assembly代码   收藏代码
  1. ; Vector Table Mapped to Address 0 at Reset    
  2.                 AREA    RESET, DATA, READONLY  
  3.                 EXPORT  __Vectors  
  4.                 EXPORT  __Vectors_End  
  5.                 EXPORT  __Vectors_Size  
  6.   
  7. __Vectors       DCD     __initial_sp                    ; Top of Stack  
  8.                 DCD     Reset_Handler                   ; Reset Handler     
 先是执行__initial_sp设置主堆栈指针MSP(相关概念请参考宋岩翻译的《Cortex-M3权威指南》),而后执行复位操作Reset_Handler
Assembly代码   收藏代码
  1. Reset_Handler    PROC  
  2.                  EXPORT  Reset_Handler             [WEAK]  
  3.      IMPORT  __main  
  4.      IMPORT  SystemInit  
  5.                  LDR     R0, =SystemInit  
  6.                  BLX     R0  
  7.                  LDR     R0, =__main  
  8.                  BX      R0  
  9.                  ENDP  
 Reset_Handler中首先执行SystemInit()函数(在system_stm32f10x.c文件中定义),该函数主要完成了RCC时钟的设置。接着执行__main()处的代码。在Keil IDE的工程窗口中,是无法搜索到__main()的定义的,但在汇编代码和工程map文件中可以找到它的身影
Assembly代码   收藏代码
  1. __main                  0x08000121   Thumb Code     0  entry.o(.ARM.Collect$$$$00000000)  

 推测应该是在entry.c文件中,而entry.c文件应该是在Keil自带的library里。 

 参考MDK __main()代码执行过程分析一文,__main()中主要通过

1. __scatterload()把RW/RO输出段从装载域地址复制到运行域地址,并完成ZI运行域的初始化工作。

2. __rt_entry()初始化堆栈,完成库函数的初始化,最后自动跳转向main()函数。其中__user_initial_stackheap()是在startup.s中定义的

Assembly代码   收藏代码
  1. EXPORT  __user_initial_stackheap                   
  2. __user_initial_stackheap  
  3.                  LDR     R0, =  Heap_Mem  
  4.                  LDR     R1, =(Stack_Mem + Stack_Size)  
  5.                  LDR     R2, = (Heap_Mem +  Heap_Size)  
  6.                  LDR     R3, = Stack_Mem  
  7.                  BX      LR  

根据AAPCS的规定,栈任何时候都得4字节对齐,在调用入口得8字节对齐。对于带RTOS的系统,该函数根据

Assembly代码   收藏代码
  1. AREA STACK, NOINIT, READWRITE, ALIGN=3  
  2. AREA    HEAP, NOINIT, READWRITE, ALIGN=3  
  3. PRESERVE8  
 保证了主堆栈指针MSP是遵守规定的,而线程堆栈指针PSP全靠自己来保证每次进入C世界时是8字节对齐,通常的做法是在程序中使用__attribute__((aligned(8)))来告知编译器在分配空间时采用8字节对齐。在发生中断时,如果当前正在使用的栈指针不是8字节对齐,则先把SP-4,调整为8字节对齐,参考 cortex-m3 栈的8字节对齐一文

补充:startup.s定义了中断处理函数

Assembly代码   收藏代码
  1. NMI_Handler     PROC        ;过程的开始  
  2.                 EXPORT  NMI_Handler  [WEAK]  
  3.                 B    .      ;原地跳转(即无限循环),  
  4.                 ENDP        ;过程的结束  

 其中WEAK作为若定义,意思是如果在别处也定义该symbol,在链接时用别处的地址,而stm3210x_it.c这个文件通常会进行这些中断处理函数的重新定义

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值