STM32开发(4)----系统启动流程

news/2024/12/23 5:41:24/

系统启动流程

  • 前言
  • 一、系统启动方式
  • 二、启动汇编文件分析
    • 1. 初始化堆栈指针
    • 2. 初始化中断向量表
    • 3. 在Reset_Handler中调用 SystemInit
    • 4.进入main函数前,初始化用户堆栈
  • 总结


前言

本章介绍STM32系统启动流程的相关内容,包括对系统启动方式,启动汇编文件的分析,启动原理的介绍等。

一、系统启动方式

在介绍系统启动流程之前,我们先来了解下启动模式,就像我们对电脑进行刷机一样,我们从硬盘或是U盘去引导系统启动是不同的,STM32有三种启动模式,即从FLASH启动,从SRAM启动,从系统存储启动。
在这里插入图片描述
系统是从 0x0000 0000 和 0x0000 0004 两个的地址获取堆栈指针 SP 和程序计数器指针 PC。而 0x0000 0000 和 0x0000 0004 两个的地址可以被重映射到其他的地址空间,映射的空间地址由BOOT0和BOOT1共同决定。如果内部 FLASH 启动:会将 0x0800 0000 映射到 0x0000 0000,会将 0x0800 0004 映射到 0x0000 0004。如果内部 SRAM 启动:会将 0x0200 0000 映射到 0x0000 0000,会将 0x0200 0004 映射到 0x0000 0004。如果用系统存储器启动:会将 0x01FF FF00 映射到 0x0000 0000,会将 0x01FF FF04 映射到 0x0000 0004

随后从地址 0x0800 0000/ 0x0200 0000 /0x01FF FF00 处取出堆栈指针 MSP 的初始值,从地址 0x0800 0004/ 0x0200 0004 /0x01FF FF04处取出程序计数器指针PC 的初始值。CPU 会从 PC 寄存器指向的地址空间取出的第 1 条指令开始执行程序,就是开始执行复位中断服务程序 Reset_Handler。

其中,我们最常用的启动方式是从FLASH启动。当然选择了启动模式后也要将固件下载到对应的地址空间才能引导成功,
在keil进行烧写固件时可以看到对固件烧写起始地址的设置,如下图所示:
在这里插入图片描述

二、启动汇编文件分析

前面介绍过通过STM32CubeMX生成了一个简单的工程,如下图,生成的项目中是一些官方提供的基本库和文件:

  1. 用户程序:应用程序,中断配置,用户配置文件等
  2. 外设驱动,这STM32CubeMX根据你配置的引脚功能涉及的功能库进行自动添加的
  3. 标准接口文件:ARM Cortex 微控制器软件接口标准文件
  4. 芯片的启动文件

在这里插入图片描述
启动文件主要做了以下工作:

  1. 初始化堆栈指针
  2. 初始化中断向量表
  3. 在Reset_Handler中调用 SystemInit
  4. 调用 C 库中的 _main 函数初始化用户堆栈,最终调用 main 函数进入C程序

下面我们对stm32f10系列芯片的启动文件startup_stm32f10x_ld.s分段进行分析,在分析的过程中我们不对汇编指令进行介绍,因为很少用到,都是简写,也不容易记忆,所以我们仅仅对大致功能做一个说明。

1. 初始化堆栈指针

; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>Stack_Size      EQU     0x00000400AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

这段代码的目的是初始化栈空间:开辟一段大小为 0x0000 0400(1KB)的栈空间,其中 ALIGN=3,表示按照 2^3 对齐,即 8 字节对齐,栈是从高到低排布的。

我们知道,栈由编译器自动分配和释放的内存,不需要用户参与管理,主要作用是存放局部变量,函数形参等,比如在程序中定义局部变量较多或者较大,占用占空间较多时,可能会导致栈空间溢出报 HardFault错误。这时,我们就需要修改启动代码中修改栈的大小Stack_Size,但是栈空间Stack_Size的大小不能大于内存SRAM的大小。

; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>Heap_Size       EQU     0x00000200AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

这段代码的目的是初始化堆空间:开辟一段大小为 0x0000 0200(512 字节)的堆空间,其中 ALIGN=3,表示按照 2^3 对齐,即 8 字节对齐,__heap_base 表示堆的起始地址, __heap_limit 表示堆的结束地址,堆是由低到高排布的。

我们知道,堆由程序员自行管理,动态分配和释放,如使用 malloc()、 calloc()和 realloc()等函数申请的内存就在堆上面。如果程序员没有释放,程序结束时可能由操作系统回收。

2. 初始化中断向量表

; Vector Table Mapped to Address 0 at ResetAREA    RESET, DATA, READONLYEXPORT  __VectorsEXPORT  __Vectors_EndEXPORT  __Vectors_Size__Vectors       DCD     __initial_sp               ; Top of StackDCD     Reset_Handler              ; Reset HandlerDCD     NMI_Handler                ; NMI HandlerDCD     HardFault_Handler          ; Hard Fault Handler......略DCD     PendSV_Handler             ; PendSV HandlerDCD     SysTick_Handler            ; SysTick Handler; External InterruptsDCD     WWDG_IRQHandler            ; Window WatchdogDCD     PVD_IRQHandler             ; PVD through EXTI Line detectDCD     TAMPER_IRQHandler          ; Tamper.......略DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10DCD     RTCAlarm_IRQHandler        ; RTC Alarm through EXTI LineDCD     USBWakeUp_IRQHandler       ; USB Wakeup from suspend
__Vectors_End__Vectors_Size  EQU  __Vectors_End - __VectorsAREA    |.text|, CODE, READONLY

这段代码的目的是初始化中断向量表: __Vectors 为向量表起始地址, __Vectors_End 为向量表结束地址,__Vectors_Size 为向量表大小。

__Vectors       DCD     __initial_sp               ; Top of StackDCD     Reset_Handler              ; Reset Handler

中断向量表被放置在代码段的最前面。起始地址是: 0x0800 0000。如上以DCD开头的为中断向量表中的内容,后面跟着的是中断服务函数的函数名,DCD 以四字节对齐分配内存,所以 Reset_Handler 中断函数入口地址为 0x0800 0004。

3. 在Reset_Handler中调用 SystemInit

; Reset handler routine
Reset_Handler    PROCEXPORT  Reset_Handler             [WEAK]IMPORT  __mainIMPORT  SystemInitLDR     R0, =SystemInitBLX     R0LDR     R0, =__mainBX      R0ENDP; Dummy Exception Handlers (infinite loops which can be modified)

这段代码的目的是定义复位子程序:从中断向量表中我们可以看到,Reset_Handler 是初始化栈空间后的第一个子程序,也是复位后第一个要执行的子程序,因此非常重要。子程序中首先调用SystemInit 函数对系统进行初始化的配置,然后在调用 C 库函数__main,这里最终会调用到 main 函数,至此,将进入我们用c语言写的程序代码中执行。

NMI_Handler     PROCEXPORT  NMI_Handler                [WEAK]B       .ENDP
HardFault_Handler\PROCEXPORT  HardFault_Handler          [WEAK]B       .ENDP
MemManage_Handler\PROCEXPORT  MemManage_Handler          [WEAK]B       .ENDP
BusFault_Handler\PROCEXPORT  BusFault_Handler           [WEAK]B       .ENDP
UsageFault_Handler\PROCEXPORT  UsageFault_Handler         [WEAK]B       .ENDP
SVC_Handler     PROCEXPORT  SVC_Handler                [WEAK]B       .ENDP
DebugMon_Handler\PROCEXPORT  DebugMon_Handler           [WEAK]B       .ENDP
PendSV_Handler  PROCEXPORT  PendSV_Handler             [WEAK]B       .ENDP
SysTick_Handler PROCEXPORT  SysTick_Handler            [WEAK]B       .ENDP......

这段代码的目的是声明各个中断服务子程序,只有函数名,具体的实现会在stm32f10x_it.c中,如下图所示
在这里插入图片描述

4.进入main函数前,初始化用户堆栈

 ;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************IF      :DEF:__MICROLIBEXPORT  __initial_spEXPORT  __heap_baseEXPORT  __heap_limitELSEIMPORT  __use_two_region_memoryEXPORT  __user_initial_stackheap__user_initial_stackheapLDR     R0, =  Heap_MemLDR     R1, =(Stack_Mem + Stack_Size)LDR     R2, = (Heap_Mem +  Heap_Size)LDR     R3, = Stack_MemBX      LRALIGNENDIFEND

这段代码的目的是初始化用户堆栈空间,里面值得注意的是 IF :DEF:__MICROLIB(即判断是否使用MICROLIB库)。

MicroLib是一个高度优化的库,适用于用C语言编写的基于ARM的嵌入式应用程序。与ARM编译器工具链中包含的标准C库相比,MicroLib提供了许多嵌入式系统所需的代码是其显著优势。
MicroLib与标准C库的主要区别在于:

  1. MicroLib专为深度嵌入式应用程序设计。
  2. MicroLib经过优化,比使用ARM标准库使用更少的代码和数据内存。
  3. MicroLib被设计为在没有操作系统的情况下工作,但这并不妨碍它与任何OS或RTOS(如Keil RTX)一起使用。
  4. MicroLib不包含文件I/O或宽字符支持。
  5. 由于MicroLib已被优化以最小化代码大小,一些函数的执行速度将比ARM编译工具中的标准C库例程慢。

要在嵌入式应用程序中使用MicroLib,请选中keil5中的MicroLib复选框并编译应用程序。keil5将您的程序与MicroLib连接起来,快速轻松地缩小程序大小。
在这里插入图片描述

总结

本章内容介绍了STM32三种常用的启动方式和系统启动引导的原理,然后介绍了启动文件的启动流程, 启动文件起到的作用:初始化堆栈指针,初始化中断向量表 ,在Reset_Handler中调用 SystemInit ,调用 C 库中的 _main 函数初始化用户堆栈,最终调用 main 函数进入C程序。

至此,STM32的开发前的一些储备知识到此结束,下面我们通过STM32CubeMX配置功能引脚,进行开发逐一介绍。


http://www.ppmy.cn/news/22619.html

相关文章

Java基础知识(基础篇 适合小白)

Hello World public 访问修饰符 static 关键字 void 返回类型 String 类 args字符串数组 public class HelloWorld {/* 第一个Java程序* 它将输出字符串 Hello World*/public static void main(String[] args) {System.out.println("Hello World"); // 输出 Hello Wo…

栈与队列 || leetcode 20. 有效的括号

给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有…

测试开发之Django实战示例 第一章 创建博客应用

第一章 创建博客应用欢迎来到Django 2 by example的教程。你看到的是目前全网唯一翻译该书的教程。本书将介绍如何创建可以用于生产环境的完整Django项目。如果你还没有安装Django&#xff0c;本章在第一部分中将介绍如何安装Django&#xff0c;之后的内容还包括创建一个简单的…

Python 处理键映射值操作

作为一个学完Python基础知识的测试&#xff0c;暗喜终于可以像RD们自己写脚本处理任何场景吧&#xff0c;如何优雅地写出来代码&#xff0c;接下来开启进阶版的Python。 本期浅谈一下&#xff0c;collection模块关于键值常用的方法&#xff0c;跟着我一起涨知识吧~ 1. 问题背…

springboot,vue教务管理系统

开发工具&#xff1a;IDEA服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8项目构建&#xff1a;maven数据库&#xff1a;mysql5.7前端技术&#xff1a;vue elementUI服务端技术&#xff1a;springbootmybatis本系统拥有三种角色&#xff1a;管理员、教师和学生&#xff0c;项目…

前端男神尤雨溪传奇

论中国对开源世界的贡献&#xff0c;尤雨溪一定占用一席之地。 文章目录前言成长之路从小学到初中高中三年大学四年入职Google加盟Meteorvue创世纪本文参考前言 本文探寻尤雨溪的成才之路&#xff0c;解密vue背后的动人故事。尤雨溪以一介文科生&#xff0c;而成长为计算机界的…

【游戏逆向】FPS网络游戏自动瞄准漏洞分析以及实现

了解FPS游戏自瞄漏洞 经常玩游戏的朋友&#xff0c;应该知道FPS游戏&#xff0c;例如&#xff1a;穿越火线&#xff0c;逆战等等&#xff0c;他们的特点就是以第一人称视角进行操作人物&#xff0c;屏幕中间会有一个准星&#xff0c;通过准星瞄准敌人进行攻击以达到击杀效果和…

2022年第十一届认证杯数学中国数学建模国际赛小美赛:D题野生动物贸易是否应该长期禁止建模 38页一等奖论文及代码

相关链接 &#xff08;1&#xff09;2022年第十一届认证杯数学中国数学建模国际赛小美赛&#xff1a;D题野生动物贸易是否应该长期禁止建模方案及代码实现 &#xff08;2&#xff09;一等奖论文下载 1 题目 野生动物市场被怀疑是当前疫情和2002年SARS疫情的源头&#xff0c;…