STM32-启动文件

server/2025/2/7 3:45:20/

STM32-启动文件

  • 简介
  • 启动文件
    • 栈空间开辟
    • 堆空间开辟
    • 中断向量表定义
    • 复位程序
  • 系统启动流程

简介

STM32 启动文件由 ST 官方提供,由汇编编写,是系统上电复位后执行的第一个程序。
启动文件主要做的工作。
1.初始化堆栈指针 SP = _initial_sp
2.初始化程序计数器指针 PC = Reset_Handler
3.设置堆和栈的大小
4.初始化中断向量表
5.配置外部 SRAM 作为数据存储器(可选)
6.配置系统时钟,通过调用 SystemInit 函数(可选)
7.调用 C 库中的 _main 函数初始化用户堆栈,最终调用 main 函数

启动文件

栈空间开辟

在这里插入图片描述
33 行EQU: 宏定义的伪指令,给数字常量取一个符号名,类似与C中的define。定义栈的大小为0x0000 0400字节,即1024B(1KB),常量的符号为Stack_Size。
35 行 AREA 汇编一个新的代码段或者数据段。段名为STACK;NOINIT为不初始化;READWRITE为可读可写;ALIGN=3.表示按照2^3对齐,即8字节对齐。
36 行 SPACE 分配内存指令,分配大小为 Stack_Size 字节连续的存储单元给栈空间。
37 行__initial_sp 紧挨着 SPACE 放置,表示栈的结束地址,栈是从高往低生长,所以结束地址就是栈顶地址。
栈主要用于存放局部变量,函数形参等,属于编译器自动分配和释放的内存,栈的大小不能超过内部 SRAM 的大小。如果工程比较大,定义的局部变量比较多时,可以在这修改栈的空间大小。

堆空间开辟

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/457b13b632d8480988ed42f4c3be5b98.png
这段代码和上面类似,定义Heap_Size为512个字节,段名为HEAP,不初始化,可读可写,8字节对齐。
__heap_base表示起始地址,__heap_limit表示结束地址。堆和栈的生长方向相反,堆是由低向高生长,而栈是从高往低生长。
堆主要由于动态内存的分配,由程序员分配和释放,如malloc(),calloc()。
51行和 52行这两句代码分别是指示编译器按照8字节对齐,以及指示编译器之后的指令为THUMB指令。

中断向量表定义

在这里插入图片描述
定义一个数据段,名字为RESET,READONLY表示只读。EXPORT表示一个符号具有全局属性,可以被外部的文件使用。
在这里插入图片描述

在这里插入图片描述
当内核响应了一个发送的异常后,对应的异常服务例程(ESR)就会执行,为了决定ESR的入口地址,内核使用了向量表查表机制。向量表其实是一个WORD(32位)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。
在这里插入图片描述
__Vectors 为向量表起始地址, __Vectors_End 为向量表结束地址,__Vectors_Size 为向量表大小,__Vectors_Size = __Vectors_End - __Vectors。
DCD:分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。
中断向量表被放置在代码段的最前面。当我们的程序在FLASH运行时,那么向量表的起始地址是:0x0800 0000。DCD:以四字节对齐分配内存,也就是下个地址是0x0800 0004,存放的是Reset_Handler中断函数入口地址。
从代码上看,向量表中存放的都是中断服务函数的函数名,所以 C 语言中的函数名对芯片来说实际上就是一个地址

复位程序

在这里插入图片描述
125行 定义一个段名为.text的只读代码段,在CODE区。
程序从129行开始,130行声明一个复位中断向量Reset_Handler为全局属性,外部文件可以调用复位中断服务。WEAK:表示弱定义,如果外部文件也定义了该标号则首先引用外部定义的标号,如果外部没有声明则使用这个。
131行和132行 IMPORT表示该标号来自外部文件,__main和SystemInit均来自外部文件。
133行 LDR表示从存储器中加载字到一个存储器中。SystemInit是一个标准的库函数,在system_stm32f1xx.c文件中定义,主要作用是配置系统时钟,还有初始化FSMC/FMC总线上外挂的SRAM(可选),前面说配置外部SRAM作为数据存储器(可选)就是这个。
134行 BLX表示跳转到由寄存器给出的地址,并根据寄存器的LSE确定处理器的状态,还要把跳转前的下条指令地址保存到LR。
135行把__main的地址给R0__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈和变量等,最终调用 main 函数去到 C 的世界。这就是为什么我们写的程序都有一个 main 函数的原因,如果不调用__main,那么程序最终就不会调用我们 C 文件里面的main,也就无法正常运行。
136行 BX 表示跳转到由寄存器/标号给出的地址,不用返回。这里表示切换到__main地址,最终调用 main 函数,不返回,进入 C 的世界。
137 行 ENDP 表示子程序结束。LDR、BLX、BX 是内核的指令,可在《CM3 权威指南 CnR2》第四章-指令集里面查
询到。

系统启动流程

在以前A7/A9内核的控制器在复位后,CPU会从存储空间的绝对地址0x0000 0000取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为0x0000 0000同时中断向量表的位置也是固定的。而Cortex-M3内核复位后的起始地址和中断向量表的位置可以被重映射。重映射的方式为,boot引脚的选择。
1.将中断向量表定位在SRAM区,即起始地址为0x2000 0000,同时复位后PC指针位于0x2000 0000处。
2.将中断向量表定位在FLASH区,即起始地址为0x8000 0000,同时复位后PC指针位于0x2000 0000处。
3.将中断向量表定位于内置 Bootloader 区。
Cortex-M3 内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在 Cortex-M3 内核复位后,会自动从起始地址的下一个 32 位空间取出复位中断入口向量,跳转执行复位中断服务程序。
以将代码下载到内部FLASH为例,即代码从0x0800 0000开始被执行。
当产生并立刻复位状态后,CM3内核做的第一件事就是读取下列两个32位整数的值:
(1)从地址 0x0800 0000 处取出堆栈指针 MSP 的初始值,该值就是栈顶地址。
(2)从地址 0x0800 0004 处取出程序计数器指针 PC 的初始值,该值指向复位后执行的第一条指令。
在这里插入图片描述
在这里插入图片描述


http://www.ppmy.cn/server/165576.html

相关文章

自动驾驶---两轮自行车的自主导航

1 背景 无人驾驶汽车最早出现在DARPA的比赛中,从那个时刻开始,逐渐引起全球学者的注意,于是从上个世纪开始各大高校院所开始了无人汽车的研发。直到这两年,无人驾驶汽车才开始走进寻常百姓家,虽然目前市面上的乘用车还…

04树 + 堆 + 优先队列 + 图(D1_树(D6_B树(B)))

目录 一、学习前言 二、基本介绍 三、特性 1. 从概念上说起 2. 举个例子 四、代码实现 节点准备 大体框架 实现分裂 实现新增 实现删除 五、完整源码 一、学习前言 前面我们已经讲解过了二叉树、二叉搜索树(BST)、平衡二叉搜索树&#xff08…

C基础寒假练习(6)

一、终端输入行数&#xff0c;打印倒金字塔 #include <stdio.h> int main() {int rows;printf("请输入倒金字塔的行数: ");scanf("%d", &rows);for (int i rows; i > 0; i--) {// 打印空格for (int j 0; j < rows - i; j) {printf(&qu…

IOC三种实现方式的区别

在Spring框架中&#xff0c;IOC&#xff08;控制反转&#xff09;通过依赖注入&#xff08;DI&#xff09;来实现&#xff0c;而依赖注入主要有三种实现方式&#xff1a;构造器注入、Setter注入和字段注入。每种方式都有其特点、适用场景和优缺点。以下是它们的详细对比&#x…

高级java每日一道面试题-2025年01月28日-框架篇[SpringBoot篇]-如何使用Spring Boot实现异常处理?

如果有遗漏,评论区告诉我进行补充 面试官: 如何使用Spring Boot实现异常处理? 我回答: 在 Java 高级面试中讨论如何使用 Spring Boot 实现异常处理时&#xff0c;我们可以从多个角度进行详细阐述。这包括全局异常处理、特定异常处理、使用 ResponseStatus 注解、自定义异常…

解决浏览器播放音频声音,没交互前不播放问题

方法一&#xff1a;通过显示“需要播放音频”弹窗&#xff0c;进行交互之后播放。 <audio ref"audioRef"><source src"./mp3/tishi.mp3" /></audio><script setup>import { ref } from vuelet audioRef ref(null)onMounted(() &g…

【数据结构-字典树】力扣211. 添加与搜索单词 - 数据结构设计

请你设计一个数据结构&#xff0c;支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。 实现词典类 WordDictionary &#xff1a; WordDictionary() 初始化词典对象 void addWord(word) 将 word 添加到数据结构中&#xff0c;之后可以对它进行匹配 bool search(…

深度学习中,文本分类任务怎么做

一、处理流程 前置步骤&#xff1a; 标注数据得到数据集数据清理&#xff1a;将特殊字符、特殊格式、无效字符去除 正式步骤&#xff1a; 1、分词或分字&#xff1a;英文一般都分词&#xff0c;中文有分词也有分字。分词还是分字取决于你模型的embedding。 2、将字或词编辑ID…