【STM32-启动文件 startup_stm32f103xe.s】

STM32-启动文件 startup_stm32f103xe.s

  • ■ STM32-启动文件
  • ■ STM32-启动文件主要做了以下工作:
  • ■ STM32-启动文件指令
  • ■ STM32-启动文件代码详解
    • ■ 栈空间的开辟
    • ■ 栈空间大小 Stack_Size
    • ■ .map 文件的详细介绍
      • ■ 打开map文件
    • ■ 堆空间
    • ■ PRESERVE8 和 THUMB 指令
  • ■ 中断向量表定义(简称:向量表)???????
  • ■ 复位程序
  • ■ weak
  • ■ _main 函数的分析
    • ■ __scatterload()函数
    • ■ __rt_entry()函数
  • ■ 中断服务程序
  • ■ ALIGN指令
  • ■ 用户堆栈初始化
  • ■ Use MicroLIB
  • ■ 系统启动流程
    • ■ 代码下载到内部 FLASH 的情况举例,即代码从地址 0x0800 0000 开始被执行分析
      • ■ 1.CM3 内核做的第一件事就是读取下列两个 32 位整数的值:
      • ■ 2.MSP 和 PC
      • ■ 3.代码从地址 0x0800 0000 开始被执行,讲解一下系统启动,初始化堆栈、 MSP 和 PC 后的内存情况。

■ STM32-启动文件

STM32 启动文件由 ST 官方提供
启动文件由汇编编写,是系统上电复位后第一个执行的程序。

■ STM32-启动文件主要做了以下工作:

1、初始化堆栈指针 SP = _initial_sp
2、初始化程序计数器指针 PC = Reset_Handler
3、设置堆和栈的大小
4、初始化中断向量表
5、配置外部 SRAM 作为数据存储器(可选)
6、配置系统时钟,通过调用 SystemInit 函数(可选)
7、调用 C 库中的 _main 函数初始化用户堆栈,最终调用 main 函数

■ STM32-启动文件指令

在这里插入图片描述

关于其他更多的 ARM 汇编,我们可以通过 MDK 的索引搜索工具中搜索找到。打开索引搜索工具的方法:
MDK->Help->uVision Help,

在这里插入图片描述

■ STM32-启动文件代码详解

例如:startup_stm32f103xe.s 把启动代码分成几个功能段进行详细的讲解

■ 栈空间的开辟

栈是从高往低生长,所以每使用一个栈空间地址,栈顶地址__initial_sp 就减一。
在这里插入图片描述
33 行 EQU:宏定义的伪指令, 给数字常量取一个符号名, 类似与 C 中的 define。
定义栈大小为 0x00000400 字节,即 1024B(1KB),常量的符号是 Stack_Size。
35 行 AREA 汇编一个新的代码段或者数据段。
段名为 STACK, 段名可以任意命名;NOINIT 表示不初始化; READWRITE 表示可读可写; ALIGN=3,表示按照 2^3 对齐,即 8 字节对齐。
36 行 SPACE 分配内存指令, 分配大小为 Stack_Size 字节连续的存储单元给栈空间。
37 行__initial_sp 紧挨着 SPACE 放置,表示栈的结束地址,栈是从高往低生长,所以结束地址就是栈顶地址

栈主要用于存放局部变量,函数形参等, 属于编译器自动分配和释放的内存, 栈的大小不能超过内部 SRAM 的大小。如果工程的程序量比较大,定义的局部变量比较多,那么就需要在启动代码中修改栈的大小,即修改 Stack_Size 的值。

如果程序出现了莫名其妙的错误,并进入了 HardFault 的时候,你就要考虑下是不是栈空间不够大,溢出了的问题。

■ 栈空间大小 Stack_Size

  1. 打开.map文件 搜索__initial_sp
    在这里插入图片描述
    栈顶地址 __initial_sp 的地址是 0x20000538
    栈低地址 STACK 0x20000138
    Stack_Size = 栈顶地址-栈低地址 Stack_Size 的大小是 0x00000400

■ .map 文件的详细介绍

■ 打开map文件

在这里插入图片描述

■ 堆空间

在这里插入图片描述
这部分代码的意思就是:开辟堆的大小为 0x00000200(512 字节),
段名为 HEAP,NOINIT不初始化,READWRITE可读可写, ALIGN=3,表示按照 2^3 对齐,即 8 字节对齐。
__heap_base表示堆的起始地址,
__heap_limit 表示堆的结束地址。
堆和栈的生长方向相反的,堆是由低向高生长,而栈是从高往低生长。

注意点:
在这里插入图片描述
由于不需要使用 C 库的 malloc 和 free 等函数,也就用不到堆空间,因此我们可以设置 Heap_Size 的大小为 0,以节省内存空间

■ PRESERVE8 和 THUMB 指令

在这里插入图片描述
PRESERVE8: 指示编译器按照 8 字节对齐。
THUMB: 指示编译器之后的指令为 THUMB 指令。

■ 中断向量表定义(简称:向量表)???

在这里插入图片描述
定义一个数据段,
RESET, READONLY 表示只读。
EXPORT 表示声明一个标号具有全局属性,可被外部的文件使用。
这里是声明了__Vectors、 __Vectors_End 和 __Vectors_Size 三个标号具有全局性,可被外部的文件使用。

__Vectors 为向量表起始地址,
__Vectors_End 为向量表结束地址,
__Vectors_Size 为向量表大小, __Vectors_Size = __Vectors_End - __Vectors。

向量表其实是一个 WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。
向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。
在复位后,该寄存器的值为 0。
因此,在地址 0 (即 FLASH 地址 0) 处必须包含一张向量表,用于初始时的异常分配。如下:
在这里插入图片描述
要注意的是这里有个另类: 地址 0x0000 0000 并不是什么入口地址,而是给出了复位后 MSP 的初值。

向量表格中灰色部分是系统内核异常
表格中位置 0 到 59 是外部中断
在这里插入图片描述

DCD: 分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。

???????????

■ 复位程序

在这里插入图片描述
定义一个段命为.text, 只读的代码段, 在 CODE 区。
在这里插入图片描述
利用 PROC、 ENDP 这一对伪指令把程序段分为若干个过程,使程序的结构加清晰。

关键词描述
145 行子程序开始
146 行声明复位中断向量 Reset_Handler 为全局属性,这样外部文件就可以调用此复位中断服务。
WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用外部定义的标号,如果外部文件没有声明也不会出错。
这里表示复位子程序可以由用户在其他文件重新实现,这里并不是唯一的。
147 行和 148 行IMPORT 表示该标号来自外部文件。
这里表示 SystemInit 和__main 这两个函数均来自外部的文件。
149 行LDR 表示从存储器中加载字到一个存储器中。
SystemInit 是一个标准的库函数,在 system_stm32f1xx.c 文件中定义,主要作用是配置系统时钟、还有就是初始化 FSMC/FMC总线上外挂的 SRAM(可选),前面说配置外部 SRAM 作为数据存储器(可选)就是这个。
150 行BLX 表示跳转到由寄存器给出的地址,并根据寄存器的 LSE 确定处理器的状态,还要把跳转前的下条指令地址保存到 LR。
151 行把__main 的地址给 R0。
__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈和变量等,最终调用 main 函数去到 C 的世界。
这就是为什么我们写的程序都有一个 main 函数的原因,如果不调用__main,那么程序最终就不会调用我们 C 文件里面的main,也就无法正常运行。
152 行BX 表示跳转到由寄存器/标号给出的地址,不用返回。
这里表示切换到__main地址,最终调用 main 函数,不返回,进入 C 的世界。
153 行ENDP 表示子程序结束。

LDR、 BLX、 BX 是内核的指令,可在《CM3 权威指南 CnR2》第四章-指令集里面查询到。

■ weak

weak 顾名思义是“弱”的意思,
在汇编中, 在函数名称后面加[WEAK]来表示
在 C语言中,在函数名称前面加上__weak 修饰符来表示, 这样的函数我们称为“弱函数”。

■ _main 函数的分析

_main 和 main 是两个完全不同的函数。
_main 代码是编译器自动创建的,因此无法找到_main 代码。
当编译器发现定义了 main 函数,那么就会自动创建_main。

程序经过汇编启动代码,执行到__main()后,可以看出有两个大的函数:

__scatterload():负责把 RW/RO 输出段从装载域地址复制到运行域地址,并完成了 ZI运行域的初始化工作。
__rt_entry():负责初始化堆栈,完成库函数的初始化,最后自动跳转向 main()函数。

■ __scatterload()函数

■ __rt_entry()函数

■ 中断服务程序

在这里插入图片描述

这些中断服务函数都被[WEAK]声明为弱定义函数
中断函数分为系统异常中断和外部中断,外部中断根据不同芯片有所变化。
B 指令是跳转到一个标号,这里跳转到一个‘.’,表示无限循环。

中断发生时,程序就会跳转到启动文件预先写好的弱定义的中断服务程序中,并且在 B 指令作用下跳转到一个‘.’中,无限循环。

■ ALIGN指令

在这里插入图片描述
ALIGN 表示对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示4 字节对齐。
要注意的是,这个不是 ARM 的指令,是编译器的。

■ 用户堆栈初始化

在这里插入图片描述
== IF, ELSE, ENDIF 是汇编的条件分支语句。==

关键词描述
331 行判断是否定义了__MICROLIB。 关于__MICROLIB 这个宏定义,我们是在 KEIL 里面配置。
勾选了 Use MicroLIB 就代表定义了__MICROLIB 这个宏。
333 行到 335 行如果定义__MICROLIB, 声明__initial_sp、 __heap_base 和__heap_limit 这三个标号具有全局属性,可被外部的文件使用。
__initial_sp 表示栈顶地址,
__heap_base 表示堆起始地址,
__heap_limit 表示堆结束地址。
337 行没有定义__MICROLIB,实际的情况就是我们没有定义__MICROLIB,所以使用默认的 C 库运行。
堆栈的初始化由 C 库函数__main 来完成。
339 行IMPORT 声明__use_two_region_memory 标号来自外部文件。
340 行EXPORT 声明__user_initial_stackheap 具有全局属性,可被外部的文件使用。
342 行标号__user_initial_stackheap,表示用户堆栈初始化程序入口。
接下来进行堆栈空间初始化,堆是从低到高生长,栈是从高到低生长,是两个互相独立的数据段,并且不能交叉使用。
344 行保存堆起始地址。
345 行保存栈大小。
346 行保存堆大小。
347 行保存栈顶指针。
348 行跳转到 LR 标号给出的地址,不用返回。
354 行END 表示到达文件的末尾,文件结束。

■ Use MicroLIB

在这里插入图片描述
MicroLIB 是 MDK 自带的微库,是缺省 C 库的备选库, MicroLIB 进行了高度优化使得
其代码变得很小,功能比缺省 C 库少。
MicroLIB 是没有源码的,只有库。
关于 MicroLIB 更多知识可以看官方介绍 http://www.keil.com/arm/microlib.asp 。

■ 系统启动流程

Cortex-M3内核复位后的起始地址和中断向量表的位置可以被重映射。充映射的方法是通过启动模式的
选择, 有以下 3 种情况:
1、 通过 boot 引脚设置可以将中断向量表定位于 SRAM 区,即起始地址为 0x2000000,同时复位后 PC 指针位于 0x2000000 处;
2、 通过 boot 引脚设置可以将中断向量表定位于 FLASH 区,即起始地址为 0x8000000,同时复位后 PC 指针位于 0x8000000 处;
3、 通过 boot 引脚设置可以将中断向量表定位于内置 Bootloader 区,本文不对这种情况做论述。

Cortex-M3 内核规定,**起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,**这样在 Cortex-M3 内核复位后,会自动从起始地址的下一个 32 位空间取出复位中断入口向量,跳转执行复位中断服务程序。

Cortex-M3 权威指南(中文)

■ 代码下载到内部 FLASH 的情况举例,即代码从地址 0x0800 0000 开始被执行分析

复位方式有三种:上电复位,硬件复位和软件复位当产生复位,并且离开 复位状态后,

■ 1.CM3 内核做的第一件事就是读取下列两个 32 位整数的值:

(1)从地址 0x0800 0000 处取出堆栈指针 MSP 的初始值,该值就是栈顶地址。
(2)从地址 0x0800 0004 处取出程序计数器指针 PC 的初始值,该值指向复位后执行的第一条指令。 下面用示意图表示

在这里插入图片描述

■ 2.MSP 和 PC

例如:
在这里插入图片描述
CM3内核是小端模式,所以倒着读。
0x08000000 的值是 0x20000788 堆栈指针 SP = 0x20000788
0x08000004 的值是 0x080001CD 程序计数器指针 PC = 0x080001CD

■ 3.代码从地址 0x0800 0000 开始被执行,讲解一下系统启动,初始化堆栈、 MSP 和 PC 后的内存情况。

在这里插入图片描述

ARM 规定: PC最低两位并不表示真实地址,最低位 LSB 用于表示是 ARM 指令( 0)还是 Thumb 指令( 1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/32468.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端实现对本地文件的IO操作

前言 在网页中,前端已经可以读取本地文件系统,对本地的文件进行IO读写,甚至可以制作一个简单的VScode编辑器。这篇文章以渐进式方式实现此功能,文末附上所有代码。 首先看整体功能演示 功能概述 我们将实现一个简单的 Web 应…

LabVIEW在脑机接口(BCI)研究中的应用

脑机接口(Brain-Computer Interface,BCI)技术通过解读大脑活动,将人类思维与计算机或其他设备连接起来,广泛应用于神经康复、认知研究和人机交互等领域。LabVIEW作为强大的图形化编程环境,在BCI研究中发挥着…

数据结构十三:2 - 3树和红黑树

一开始就接触这五点,会让人云里雾里,不利于了解这个数据结构。因为这种先给定义在推导的方式并不适合学习。它没有介绍红黑树的来源,而只是给你生硬的定义。 而学习红黑树的最好学习资料就是大名鼎鼎的《算法4》,如下&#xff1a…

【Android源码解析】一篇搞定“路由、网络层、UI层、通信层

资料获取 扫一扫下方二维码即可免费领取1880页的《Android百大框架源码解析》 《Android 百大框架源码解析》 1.Retrofit 2.0源码解析 2.Okhttp3源码解析 3.ButterKnife源码解析 4.MPAndroidChart 源码解析 5.Glide源码解析 6.Leakcanary 源码解析 7.Universal-lmage-Loa…

【必看】每个开发人员都应该知道的 10 个 GitHub 库

GitHub🌟:155K 被难题困住了?还是需要一些建议来指导你进入开发者行业?这个 仓库 将为你提供帮助。它拥有想要成为前端、后端或 DevOps 工程师需要的所有技术。你可以选择符合需求的或适合自己的,因为它提供了多种多…

数据结构历年考研真题对应知识点(栈)

目录 3.1栈 3.1.1栈的基本概念 【栈的特点(2017)】 【入栈序列和出栈序列之间的关系(2022)】 【特定条件下的出栈序列分析(2010、2011、2013、2018、2020)】 3.1.2栈的顺序存储结构 【出/入栈操作的模拟(2009)】 3.1栈 3.1.1栈的基本概念 【栈…

YOLOv10目标检测算法的使用

目录 一、环境安装 1、创建虚拟环境 2、安装依赖 二、数据集准备 1、预训练权重 2、数据划分 3、建立数据集的yaml文件 三、训练 1、终端运行指令 2、建立一个 python 文件运行 四、验证 1、终端运行指令 2、建立一个 python 文件运行 五、模型推理 1、单张图片推…

Android开发实用必备的几款插件,提高你的开发速度

1.GsonFormat 使用方法:快捷键AltS也可以使用AltInsert选择GsonFormat,作用:速将json字符串转换成一个Java Bean,免去我们根据json字符串手写对应Java Bean的过程。 2.ButterKnife Zelezny 又叫黄油刀 使用方法:CtrlS…

【Nodejs 日志库 】

总结了几个比较好用的Nodejs日志库,我认为一个 合格的日志库 需要 支持多种传输,如文件、控制台、HTTP 等。可定制的日志级别和格式。异步日志记录。 根据上述的需求,挑选出 几款比较好用的日志库, 1. Winston(Gith…

直流电机三级串电阻启动

直流电动机在工农业生产中拥有广泛的应用,这主要得益于其调速范围广、调速平稳、过载能力强以及启动和制动转矩大的优点。为了降低起动电流和起动转矩,研究者们探索了直流电动机串电阻起动方法。这种方法通过在直流电动机电枢绕组中串入电阻,…

192.回溯算法:电话号码的字母组合(力扣)

代码解决 class Solution { public:// 定义每个数字对应的字母映射const string letterMap[10] {"", // 0"", // 1"abc", // 2"def", // 3"ghi", // 4"jkl", // 5"mno", // 6"pqrs&…

vscode+picgo+gitee实现Markdown图床

vscode中编辑Markdown文件,复制的图片默认是保存在本地的。当文档上传csdn时,会提示图片无法识别 可以在gitee上创建图床仓库,使用picgo工具上传图片,在Markdown中插入gitee链接的方式来解决该问题。 一、 安装picgo工具 1.1 v…

Kimichat使用案例027:有效使用 kimichat 的15个高级技巧

文章目录 一、明确具体:表达清晰、避免使用模糊措辞。二、提供背景信息:提供相关的细节和背景信息。三、每次只问一个问题四、设定明确的标准五、要求解释六、管理期望七、确定问题类型八、调整语言水平九、提供范例十、及时提供反馈十一、明确对话角色十二、 保持对话的连贯…

Tableau数据可视化与仪表盘搭建

Tableau的主要目的 数据赋能和数据探索。 数据赋能: 1.分析师可以将数据看板发布到线上给其他部门使用 2.自动更新看板 3.自由下载数据 4.线上修改图表 5.邮件发送数据 6.设置数据预警 数据探索: 1.支持亿级数据的连接和处理 2.自由地对字段进行各种…

目前哪个充电宝品牌比较好?四款优质充电宝分享

在电量成为现代生活不可或缺的生产资源的时代,选择一款优质的充电宝无疑是保证移动设备持续运作的关键。面对市场上众多品牌和型号的充电宝,消费者在选择时可能会感到困惑和迷茫。本文将为您揭示哪些品牌真正代表了耐用性和质量的典范,让自己…

gbase8s获取表的serial字段下一个insert序列值

serial字段,有个函数可以获取到最后插入的序列值,但是好像只能获取到当前会话最后一次插入的序列值,不论是SELECT dbinfo(sqlca.sqlerrd1) FROM dual;,还是select dbinfo(bigserial) from dual;,或者select dbinfo(ser…

如果申请小程序地理位置接口权限之前刷到这一篇就好了

小程序地理位置接口有什么功能? 通常情况下,我们在开发小程序时,可能会用到获取用户地理位置信息的功能。小程序开发者开放平台的新规定指出,如果没有申请开通微信小程序地理位置接口(getLocation)&#xf…

4.XSS-反射型(get)利用:获取cookie

GET反射型XSS利用:获取cookie 修改一下配置文件\pikachu\pkxss\xcookie\cookie.php 我这里将对应的IP地址修改为本地pikachu的主站IP地址,这样给用户造成一种正常视觉上的欺骗,容易上当。重定向到pikachu主页面 基于IP搭建的pkxss平台(入侵…

合并有序链表

合并有序链表 图解代码如下 图解 虽然很复杂,但能够很好的理解怎么使用链表,以及对链表的指针类理解 代码如下 Node* merge_list_two_pointer(List& list1, List& list2) {Node* new_head1 list1.head;Node* new_head2 list2.head;Node* s…

认识微服务

单体架构 单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。 优点: 架构简单部署成本低缺点: 团队协作成本高系统发布效率低系统可用性差 总结: 单体架构适合开发功能相对简单,规模较小…