STM32启动流程详解(超全,startup_stm32xx.s分析)

单片机上电后执行的第一段代码

822e5e8dcad64b5e9abf94a9a16f0e58.png

        1.初始化堆栈指针 SP=_initial_sp

        2.初始化 PC 指针=Reset_Handler

        3.初始化中断向量表

        4.配置系统时钟

        5.调用 C 库函数_main 初始化用户堆栈,然后进入 main 函数。

        在正式讲解之前,我们需要了解STM32的启动模式。

STM32的启动模式

181e8d540bb34605a9f31fb2d4f23def.png

        手册可以在Keil中跳转查看

STM32的三种启动模式

        首先要讲一下STM32的启动模式,因为启动模式决定了向量表的位置,STM32有三种启动模式:

        1. 主闪存存储器(Main Flash memory)启动

        从STM32内置的Flash启动(0x0800 0000-0x0807 FFFF),一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。以0x08000000 对应的内存为例,则该块内存既可以通过0x00000000 操作也可以通过0x08000000 操作,且都是操作的同一块内存。

        2. 系统存储器(System memory)启动

        从系统存储器启动(0x1FFFF000 - 0x1FFF F7FF),这种模式启动的程序功能是由厂家设置的。一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的ISP程序中,提供了串口下载程序的固件,可以通过这个ISP程序将用户程序下载到系统的Flash中。以0x1FFFFFF0对应的内存为例,则该块内存既可以通过0x00000000 操作也可以通过0x1FFFFFF0操作,且都是操作的同一块内存。

        3. 片上SRAM(Embedded SRAM)启动

        从内置SRAM启动(0x2000 0000-0x3FFFFFFF),既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。SRAM 只能通过0x20000000进行操作,与上述两者不同。从SRAM 启动时,需要在应用程序初始化代码中重新设置向量表的位置。

        用户可以通过设置BOOT0和BOOT1的引脚电平状态,来选择复位后的启动模式。

        如下图所示:

5cdb65af3d244fd38cebd60304b5b272.png

 

 

 

20d718ad30d94b3793839f6c78702e69.png

总结 

        启动模式只决定程序烧录的位置,加载完程序之后会有一个重映射(映射到0x00000000地址位置);真正产生复位信号的时候,CPU还是从开始位置执行。

        值得注意的是STM32上电复位以后,代码区都是从0x00000000开始的,三种启动模式只是将各自存储空间的地址映射到0x00000000中。

STM32的启动文件分析

        因为单片机上电启动过程主要是由汇编完成的,因此STM32的启动的大部分内容都是在启动文件里。我用CubeMX生成的的启动文件是startup_stm32f103xb.s,不管使用标准库还是使用HAL库,启动文件都是差不多的。

1. Stack栈

        栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部SRAM 的大小。当程序较大时,需要修改栈的大小,不然可能会出现的HardFault的错误。

06efc80c404844169484668fc0874736.png

第32行:表示开辟栈的大小为 0X400(1KB),EQU是伪指令,相当于C 中的 define。

第34行:开辟一段可读可写数据空间,ARER 伪指令表示下面将开始定义一个代码段或者数据段。此处是定义数据段。ARER 后面的关键字表示这个段的属性。段名为STACK,可以任意命名;NOINIT 表示不初始化;READWRITE 表示可读可写,ALIGN=3,表示按照 8 字节对齐。

第35行:SPACE 用于分配大小等于 Stack_Size连续内存空间,单位为字节。

第37行: __initial_sp表示栈顶地址。栈是由高向低生长的。

2. Heap堆

        堆主要用来动态内存的分配,像malloc()函数申请的内存就在堆中。

5c6195ffd67f4a758ab5e6a3ec0c83da.png

        开辟堆的大小为 0X200(512 字节),名字为 HEAP,NOINIT 即不初始化,可读可写,8字节对齐。__heap_base 表示对的起始地址,__heap_limit 表示堆的结束地址。

572e655a446e4003b97139f12b1ae4e8.png

3. 向量表

        向量表是一个WORD( 32 )数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为 0。因此,在地址 0 (即 FLASH 地址 0)处必须包含一张向量表,用于初始时的异常分配。

        值得注意的是这里有个另类: 0 号类型并不是什么入口地址,而是给出了复位后 MSP 的初值,后面会具体讲解。

62c41cc496974e99a8fee3a8bebeec71.png

82f155ef9d124c68905ed17929629c40.png

第55行:定义一块代码段,段名字是RESET,READONLY 表示只读。

第56-58行:使用EXPORT将3个标识符申明为可被外部引用,声明 __Vectors、__Vectors_End 和__Vectors_Size 具有全局属性。

第60行:__Vectors 表示向量表起始地址,DCD 表示分配 1 个 4 字节的空间。每行 DCD 都会生成一个 4 字节的二进制代码,中断向量表 存放的实际上是中断服务程序的入口地址。当异常(也即是中断事件)发生时,CPU 的中断系统会将相应的入口地址赋值给 PC 程序计数器,之后就开始执行中断服务程序。在60行之后,依次定义了中断服务程序的入口地址。

第121行:__Vectors_End 为向量表结束地址。

第123行:__Vectors_Size则是向量表的大小,向量表的大小是通过__Vectors 和__Vectors_End 相减得到的。

4. 复位程序

        复位程序是系统上电后执行的第一个程序,复位程序也是中断程序,只是这个程序比较特殊,因此单独提出来讲解。

e4dc62936c9949e186d21f6a3aca21fc.png

第128行:定义了一个服务程序,PROC表示程序的开始。

第129行:使用EXPORT将Reset_Handler申明为可被外部引用,后面WEAK表示弱定义,如果外部文件定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位程序可以由用户在其他文件重新实现,这种写法在HAL库中是很常见的。

第130-131行:表示该标号来自外部文件,SystemInit()是一个库函数,在system_stm32f1xx.c中定义的,__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,这个是由编译器完成的,该函数最终会调用我们自己写的main函数,从而进入C世界中。

第132行:这是一条汇编指令,表示从存储器中加载SystemInit到一个寄存器R0的地址中。

第133行:汇编指令,表示跳转到寄存器R0的地址,并根据寄存器的 LSE 确定处理器的状态,还要把跳转前的下条指令地址保存到 LR。

第134行:和132行是一个意思,表示从存储器中加载__main到一个寄存器R0的地址中。

第135行:和133稍微不同,这里跳转到至指定寄存器的地址后,不会返回。

第136行:和PROC是对应的,表示程序的结束。

5. 中断服务程序

        我们平时要使用哪个中断,就需要编写相应的中断服务程序,只是启动文件把这些函数留出来了,但是内容都是空的,真正的中断复服务程序需要我们在外部的 C 文件里面重新实现,这里只是提前占了一个位置罢了。

        这部分没啥好说的,和服务程序类似的,只需要注意‘B .’语句,B表示跳转,这里跳转到一个‘.’,即表示无线循环。

c6f1c67f76a94e83a258fa4508c14d5e.png

b4836b2c21d3483281fe227506248674.png

 6. 堆栈初始化

        堆栈初始化是由一个IF条件来实现的,MICROLIB的定义与否决定了堆栈的初始化方式。

        这个定义是在Options->Target中设置的

3339e34492734b5b8a2561f2cd27d61f.png

        这部分也没啥讲的,需要注意的是,ALIGN表示对指令或者数据存放的地址进行对齐,缺省表示4字节对齐。

e7c11f8a3d0b40019860c8d4cca34d1e.png

        

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

透视数据:数据可视化工具的多重场景应用

数据可视化工具已经成为了许多领域中的重要利器,它们在各种场景下发挥着重要作用。下面我就以可视化从业者的角度简单谈谈数据可视化工具在不同场景下的应用: 企业数据分析与决策支持 在企业层面,数据可视化工具被广泛应用于数据分析和决策…

27jd网卡丢失IP地址问题追踪

一、问题描述及复现步骤 问题描述 启用network服务,关闭NetworkManager服务后,(通过 ip a 查看) em1网卡丢失IP地址 网络相关组件信息 glib-networking-2.58.0-7.ky10.x86_64 network-scripts-10.01-6.ky10.x86_64 dracut-…

MapReduce综合应用案例 — 电信数据清洗

文章目录 第1关:数据清洗 第1关:数据清洗 测试说明 平台会对你编写的代码进行测试: 评测之前先在命令行启动hadoop:start-all.sh; 点击测评后MySQL所需的数据库和表会自动创建好。 PhoneLog:封装对象 L…

Android定制ROM简介

Android定制ROM简介 这篇文章是为对自定义ROM、AOSP等词汇不太熟悉的技术爱好者和好奇的人写的。我希望通过向您介绍这个世界来开始博客写作。 在我们将注意力转向定制ROM之前,让我们先了解一些基础知识。 什么是操作系统? 维基百科对此的定义简洁而…

探讨二维半导体的概念、应用前景及其与传统半导体的差异

当探讨二维半导体时,我们置身于科技革新的前沿。这种材料以其纳米级薄度和独特电学性质区别于传统半导体,引发了科学界的广泛兴趣。本文将深入探讨二维半导体的概念、应用前景及其与传统半导体的差异。 什么是二维半导体? 二维半导体是由单…

计算机网络 网络层下 | IPv6 路由选择协议,P多播,虚拟专用网络VPN,MPLS多协议标签

文章目录 5 IPv65.1 组成5.2 IPv6地址5.3 从IPv4向IPv6过渡5.3.1 双协议栈5.3.2 隧道技术 6 因特网的路由选择协议6.1 内部网关协议RIP6.2 内部网关协议 OSPF基本特点 6.3 外部网关协议 BGP6.3.1 路由选择 6.4 路由器组成6.4.1 基本了解6.4.2 结构 7 IP多播7.1 硬件多播7.2 IP多…

0062-Java运算符

文章目录 1.运算符介绍2.算术运算符2.1 介绍2.2 细节说明 3.关系运算符(比较运算符)3.1 介绍3.2 细节说明 4.逻辑运算符4.1 介绍4.2 逻辑运算规则4.3 && 和 & 基本规则4.4 && 和 & 使用区别4.5 || 和 | 基本规则4.6 || 和 | 使用区别 5. ! 取反 基本规…

Logback简介与配置详解

在开发和维护Spring Boot应用程序时,一个强大而灵活的日志框架是至关重要的。Spring Boot默认集成了Logback,一个高性能的Java日志框架。本文将介绍如何配置Logback以满足你的日志记录需求。 Logback简介 官方网址:https://logback.qos.ch/ …

Kafka核心参数(带完善)

客户端 api Kafka提供了以下两套客户端API HighLevel(重点)LowLevel HighLevel API封装了kafka的运行细节,使用起来比较简单,是企业开发过程中最常用的客户端API。 而LowLevel API则需要客户端自己管理Kafka的运行细节,Partition&#x…

RocketMQ系统性学习-SpringCloud Alibaba集成RocketMQ以及消费收发实战

文章目录 Spring Cloud Alibaba 集成 RocketMQ 最佳实践集成依赖DashBoard消息收发实战 Spring Cloud Alibaba 集成 RocketMQ 最佳实践 SpringBoot 相对于 SSM 来说已经很大程度上简化了开发,但是使用 SpringBoot 集成一些第三方的框架,还是需要花费一些…

Node.js 工作线程与子进程:应该使用哪一个

Node.js 工作线程与子进程:应该使用哪一个 并行处理在计算密集型应用程序中起着至关重要的作用。例如,考虑一个确定给定数字是否为素数的应用程序。如果我们熟悉素数,我们就会知道必须从 1 遍历到该数的平方根才能确定它是否是素数&#xff…

搭建知识付费平台?明理信息科技为你提供全程解决方案

明理信息科技saas知识付费平台 在当今数字化时代,知识付费已经成为一种趋势,越来越多的人愿意为有价值的知识付费。然而,公共知识付费平台虽然内容丰富,但难以满足个人或企业个性化的需求和品牌打造。同时,开发和维护…

Python Pandas Excel/csv文件的保存与读取(第14讲)

Python Pandas Excel/csv文件的读取于保存(第14讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

在 Kubernetes 上部署 Python 3.7、Chrome 和 Chromedriver(版本 114.0.5735.90)的完整指南

一、构建基础镜像 docker build -f /u01/isi/DockerFile . -t thinking_code.com/xhh/crawler_base_image:v1.0.2docker push thinking_code.com/xhh/crawler_base_image:v1.0.2 二、K8s运行Pod 三、DockerFile文件 # 基于镜像基础 FROM python:3.7# 设置代码文件夹工作目录…

【论文解读】Comparing VVC, HEVC and AV1 using Objective and Subjective Assessments

时间:2020 级别:IEEE 机构: IEEE 组织 摘要: 对3种最新的视频编码标准HEVC (High Efficiency video Coding)测试模型HM (High Efficiency video Coding)、amedia video 1 (AV1)和Versatile video Coding测试模型 (VTM)进行了客观和…

关于“Python”的核心知识点整理大全25

目录 10.3.4 else 代码块、 10.3.5 处理 FileNotFoundError 异常 alice.py 在这个示例中,try代码块引发FileNotFoundError异常,因此Python找出与该错误匹配的 except代码块,并运行其中的代码。最终的结果是显示一条友好的错误消息&#x…

抖音直播间websocket礼物和弹幕消息推送可能出现重复的情况,解决办法

在抖音直播间里,通过websocket收到的礼物消息数据格式如下: {common: {method: WebcastGiftMessage,msgId: 7283420150152942632,roomId: 7283413007005207308,createTime: 1695803662805,isShowMsg: True,describe: 莎***:送给主播 1个入团卡,priority…

速学数据结构 | 二叉树堆的实现详解篇

🎬 鸽芷咕:个人主页 🔥 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活! 📋 前言 🌈hello! 各位宝子们大家好啊,二叉树的概念大家都了解了那么我们…

自动驾驶学习笔记(十九)——Planning模块

#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo 社区开发者圆桌会》免费报名—>传送门 文章目录 前言 Planning作用 Planning内容 Plannin…

uni-app ucharts中饼图与圆环图区别

项目情况: uni-app的用于移动端H5项目,包使用uni_modules目录存放。 图表引用ucharts中的echarts配置的组件方式 区别1 饼图与圆环图在echarts使用的配置都是pie类型。但是配置raduis使用: radius: [40%, 70%] 区别2 组件type指明&#xf…
最新文章