F407/103启动文件and启动过程

news2024/11/28 4:48:01

STM32 启动文件简介

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

接下来结合这些指令说明对启动文件进行注释

栈空间的开辟

栈空间的开辟,源码如图 2.1.1 所示:

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 的时候,你就要考虑下是不是栈空间不够大,溢出了的问题。

关于ARM的栈设计

ARM使用的是满减栈

点击以上链接可查看详情。

关于栈顶地址,我们 可以通过.map 文件查看,方法如图 2.1.2 所示。关于 .map 文件的详细介绍,大家可以查看正点原子团队编写的:《MAP 文件浅析 .pdf 》这个文档 。
2.1.2 通过 .map 文件查看栈顶地址
我们定义 Stack_Size 的大小是 0x00000400 ,图 2.1.2 中可以看到栈顶地址 __initial_sp 的 地址是 0x20000788 ,那栈底地址是多少呢?从图中可以知道是 0x20000388 。所以栈顶地址
0x20000788 到栈底地址 0x20000388 的内存大小刚好就是 Stack_Size 的大小。栈是从高往低生长,所以每使用一个栈空间地址,栈顶地址__initial_sp 就减一。

堆空间的开辟

堆空间的开辟,源码如图 2.2.1 所示:
堆空间开辟代码跟栈空间开辟代码是类似的了。这部分代码的意思就是:开辟堆的大小为 0x00000200 512 字节),段名为 HEAP 不初始化,可读可写, 8 字节对齐。 __heap_base表示堆的起始地址,__heap_limit 表示堆的结束地址。堆和栈的生长方向相反的,堆是由低向高生长,而 栈是从高往低生长
堆主要用于动态内存的分配,像 malloc() calloc() realloc() 等函数申请的内存就在堆上。堆中的内存一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。

接下来是 PRESERVE8 THUMB 指令两行代码。如图 2.2.2 所示。
PRESERVE8 :指示编译器按照 8 字节对齐。
THUMB :指示编译器之后的指令为 THUMB 指令。

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

中断向量表定义代码,如图 2.3.1 所示:

 

定义一个数据段,名字为 RESET, READONLY 表示只读。EXPORT 表示声明一个标号具有全局属性,可被外部的文件使用。这里是声明了__Vectors__Vectors_End 和__Vectors_Size 三个标号具有全局性,可被外部的文件使用。

当内核响应了一个发生的异常后,对应的异常服务例程(ESR)就会执行。为了决定 ESR的入口地址,内核使用了向量表查表机制。向量表其实是一个 WORD32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为 0。因此,在地址 0 (即 FLASH 地址 0) 处必须包含一张向量表,用于初始时的异常分配。

注意,这里是逻辑位置,就是说,如果Flash中代码段的真实起始地址为A,则实际的向量表地址为A+相对偏移。

__Vectors 为向量表起始地址,__Vectors_End 为向量表结束地址, __Vectors_Size 为向量表大小,__Vectors_Size = __Vectors_End - __Vectors
DCD :分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。
中断向量表被放置在代码段的最前面。例如:当我们的程序在 FLASH 运行时,那么向量表的起始地址是:0x0800 0000 。结合图 2.3.2 可以知道,地址 0x0800 0000 存放的是栈顶地址。DCD :以四字节对齐分配内存,也就是下个地址是 0x0800 0004 ,存放的是 Reset_Handler中断函数入口地址。
从代码上看,向量表中存放的都是中断服务函数的函数名,所以 C 语言中的函数名对芯片来说实际上就是一个地址。
举个例子,如果发生了异常 SVCall ,则 NVIC 会计算出地址偏移量(注意,是偏移量,不是实际地址)是 11x4=0x2C ,然后从那里取出服务例程的入口地址并跳入。 要注意的是这里有个另类:地址 0x0000 0000 并不是什么入口地址,而是给出了复位后 MSP 的初值(这个指针里存的是栈顶地址)。

复位程序

接下来是定义只读代码段,如图 2.4.1 所示:

 

定义一个段,命名为 .text ,只读的代码段, CODE 区。 复位子程序代码,如图 2.4.2 所示:
复位子程序代码,如图 2.4.2 所示:
利用 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 》第四章 - 指令集里面查询到。

中断服务程序

接下来就是中断服务程序了,如图 2.5.1 所示:

 

可以看到这些中断服务函数都被 [WEAK] 声明为弱定义函数,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不会出错。
这些中断函数分为系统异常中断和外部中断,外部中断根据不同芯片有所变化。 B 指令是跳转到一个标号,这里跳转到一个‘
. ’,表示无限循环。
在启动文件代码中,已经把我们所有中断的中断服务函数写好了,但都是声明为弱定义,所以真正的中断服务函数需要我们在外部实现。
如果我们开启了某个中断,但是忘记写对应的中断服务程序函数又或者把中断服务函数名写错,那么中断发生时,程序就会跳转到启动文件预先写好的弱定义的中断服务程序中,并且在 B 指令作用下跳转到一个‘ .’中,无限循环。
这里的系统异常中断是内核的,外部中断是外设的。

用户堆栈初始化

ALIGN 指令,如图 2.6.1 所示:

 

ALIGN 表示对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示4 字节对齐。要注意的是,这个不是 ARM 的指令,是编译器的。
接下就是启动文件最后一部分代码,用户堆栈初始化代码,如图 2.6.2 所示:

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 是没有源码的,只有库。

系统启动过程

可参考:关于ARM架构和cortexM内核的知识总结_路溪非溪的博客-CSDN博客

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

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

相关文章

SSM面试题

文章目录 一、Spring1.1 配置一个bean的方式?注解/xml1.2 spring 自动装配 bean 有哪些方式?1.3 spring 常用的注入方式有哪些?1.4 Component和Bean的区别?1.5 spring 事务实现方式有哪些?1.6 spring事务的传播机制?1.7 spring 的事务隔离? 二、SpringMVC2.1 SpringlIvc…

阿里云在国内市场占有率怎么样?

阿里云在国内市场占有率怎么样?   阿里云在国内市场占有率分析   随着互联网的飞速发展,越来越多的企业和个人开始利用云计算服务来满足各种业务需求。作为中国领先的云服务提供商,阿里云自成立以来就受到了广泛关注。本文旨在分析阿里云…

cmake入门(2)

cmake 教程2 demo cmake_minimum_required(VERSION 3.10) project(Tutorial)set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True)add_executable(Tutorial tutorial.cxx)基础 cmake_minimum_required cmake的版本要求 project 项目的名字,同时会生…

ad18学习笔记五:统计焊盘数量(board information)

AD18之后,Altium Designer改动比较大。下面将介绍AD19如何统计焊盘(PAD数量)与SMT贴片数量与插件数量 1: PCB 空白处 -> F11 -> Properties 选项卡 -> Board Information -> Pads 2: Pads 包括 通孔焊盘和贴片焊盘 Vias 包括过孔…

22.小波神经网络时间序列预测交通流量(附matlab程序)

1.简述 学习目标:小波神经网络时间序列预测交通流量 WNN(小波神经网络):是在误差反传神经网络拓扑结构的基础上发展而来的网络,与神经网络的结构具有一定的相似.在小波神经网络中,当整体信号…

第十三章 csv模块

1. csv模块介绍 介绍csv 模块前,需要先了解csv 文件(.csv 文件格式),csv 文件中的每行代表电子表格中的一行,并用逗号分隔该行中的单元格。 csv 文件可以使用记事本打开,可以使用Excel 另存为.csv 文件格…

docker内访问tdengine服务

踩坑记 springboot项目使用docker部署。由于tdengine原生连接方式需要安装客户端,第一想法是宿主机装好客户端,然后映射驱动到容器内部,网上找的教程也基本是这种思路。尝试了一天失败了。 错误1:libjemalloc.so.2 file: No such file or d…

docker安装下载tomcat一站式搞定并设置挂载卷

阿丹: 之前在使用nginx部署搭建vue项目的时候没有出docker配置nginx的配置文档(因为之前使用的是腾讯云现成的nginx服务器),今天配置安装一下tomcat和nginx在docker里面的安装。 在docker中安装配置tomcat 操作解读:…

服务器中间件

文章目录 一、tomcat二、 nginx2.1 代理问题2.2 负载均衡问题2.3 资源优化2.4 Nginx处理2.5 Nginx的特点:2.6 Nginx的安装2.7 Nginx的配置文件2.8 Nginx的反向代理2.9 反向代理:2.10 基于Nginx实现反向代理2.11 关于Nginx的location路径映射2.12 负载均衡…

【实战项目】利用mmdetection识别卫星遥感影像上的电线杆塔

前言 这次项目算是对openmmlab AI实战营第二期的一个实验证明,虽然这几天学习的比较粗糙,但也算是入门了mmdetection。 这个工具就像python一样,openmmlab已经将入门门槛做的很低了,但如果想精进、熟练甚至做真正的调参侠&#xf…

小白到运维工程师自学之路 第三十九集 (LVS架构)

一、概述 1、lvs LVS是Linux Virtual Server的缩写,是一种基于Linux内核的高性能、高可用性的 负载均衡软件。它可以将多台服务器组成一个虚拟的服务器集群,通过负载均衡算法将 客户端请求分发到不同的服务器上,从而提高系统的可用性和性能…

【MQTT 5.0】协议 ——发布订阅模式、Qos、keepalive、连接认证、消息结构

一、前言1.1 MQTT 协议概述1.2 MQTT规范 二、MQTT 协议基本概念2.1 发布/订阅模式2.11 MQTT 发布/订阅模式2.12 MQTT 发布/订阅中的消息路由2.13 MQTT 与 HTTP 对比2.14 MQTT 与消息队列 2.2 服务质量:QoS2.21 QoS 0 最多分发一次2.22 QoS1 至少分发一次2.23 QoS 2 …

一款可以支持SNMP协议的网络型温湿度变送器资料

简单网络管理协议(SNMP),由一组网络管理的标准组成,包含一个应用层协议(application layer protocol)、数据库模型(database schema)和一组资料物件。该协议能够支持网络管理系统&am…

generator和promise和async的异同

一、generator(生成器)是ES6标准引入的新数据类型,他和promise一样都是异步事件的解决方案 //generator函数生成斐波那契// generator(生成器)是ES6标准引入的新数据类型,async就是 Generator 函数的语法糖//本质:用来处理异步事件的对象/包含异步操作的容器functio…

Rust语言从入门到入坑——(4)Rust语法(中)

文章目录 0 引入1、函数1.1、函数参数1.2、函数体1.3、函数返回值 2、条件语句3、循环3.1 、while3.2 、for3.3 、loop循环 4、总结 0 引入 在这里我们需要介绍Rust语法,一共分三部分,第二部分主要是一些如函数,编程中的循环等语法的介绍&am…

小白到运维工程师自学之路 第三十九集 (HAproxy 负载均衡)

一、概述 HAProxy是一款高性能的负载均衡软件,可以将来自客户端的请求分发到多个服务器上,以提高系统的可用性和性能。HAProxy支持多种负载均衡算法,包括轮询、加权轮询、最少连接数等。同时,HAProxy还支持会话保持、健康检查、SS…

redis和mysql

文章目录 一、redis1.1 redis的数据结构都有哪些?1.2 持久化方式有哪些?1.3 怎么保证缓存和数据库数据的一致性?1.4 redis缓存是什么意思? 二、数据库2.1 基本数据类型2.2 MySQL 的内连接、左连接、右连接有什么区别?2.3 MySQL 问题排查都有…

第二章(第三节):导数的应用

1.洛必达法则 1.用途 能够使用洛必达法则解决常见的未定式问题。2.极限下的未定式 如果当 x→a 或 x→∞ 时,函数f(x)和g(x)均趋于零或者无穷,那么极限:可以存在,也可能不存在。通常这种极限为:0/0型或∞/∞型未定式。3.示例 1.lim x→0; (1-cosx) / x 2 ^2

机器人开发--Fast DDS

机器人开发--Fast DDS 1 介绍1.1 DDS概述1.2 Fast DDS 介绍域与域通信跨网络通信 2 内容要素与组件介绍IDL (Interface Definition Language)eProsima Fast DDS-Gen? 3 安装步骤3.1 安装选择(linux源码cmakec)3.2 模块…

shardingsphere第四课shardingsphere-proxy的使用

一、为什么要有服务端分库分表? 配合 ORM 框架使用更友好 当使用 ShardingSphere-JDBC 时,需要在代码中直接编写分库分表的逻辑,如果使用 ORM 框架,会产生冲突。ShardingSphere-Proxy 作为服务端中间件,可以无缝对接 ORM 框架。 对 DBA 更加友好 ShardingSphere-Pr…