STM32_启动流程详解

news2024/11/16 1:21:04

目录标题

  • 前言
  • 启动流程概述
    • 复位中断函数详解
      • SystemInit函数详解
      • __main函数详解
  • 附录
    • stm32单片机的存储器映像
    • 中断向量表的映射

前言

最近在学习IAP远程OTA升级单片机固件程序,发现自己对单片机的启动流程还不是那么了解,就总结整理一下吧。


启动流程概述

  • 1.内核初始化;
    • 1.内核复位和NVIC寄存器部分清零;
    • 2.内核设置堆栈:内核从向量表0地址读出堆栈地址,并设置主堆栈指针(SP_main)
    • 3.设置PC和LR寄存器
      • a. LR设置未初始复位值0xFFFF FFFF
      • b. 单片机的内部硬件机制自动将PC指针定位到中断向量表的复位中断向量处,把复位中断函数Reset_Handler的地址赋值给PC指针,然后跳转执行Reset_Handler。
  • 2.强制PC指针指向中断向量表的复位中断向量执行复位中断函数;
  • 3.在复位中断函数中调用 SystemInit 函数,初始化时钟配置中断向量表
  • 4.调用 __main 函数完成全局/静态变量的初始化和重定位工作,初始化堆栈和库函数
  • 5.跳转到main函数中执行

复位中断函数详解

上面内核初始化的最后一步,是把复位中断函数Reset_Handler的地址赋值给PC指针,然后跳转执行复位中断处理函数,我们来看一下在复位中断里内核都做了哪些操作。
我们随便打开一个标准库工程的启动文件,都能找到下面这段代码:

; Reset handler                     //程序注释(汇编中;表示注释)
Reset_Handler    PROC               //定义了一个子程序:Reset_Handler
                 EXPORT  Reset_Handler             [WEAK]   //EXPORT  表明此函数可供启动模块调用
     IMPORT  __main               //IMPORT 表明函数定义在外部,链接时需要去寻找
     IMPORT  SystemInit            
                 LDR     R0, =SystemInit    //将SystemInit地址加载到R0寄存器
                 BLX     R0                 //跳转到R0执行SystemInit程序
                 LDR     R0, =__main        //将__main地址加载到R0寄存器
                 BX      R0                 //跳转到R0执行_main程序
                 ENDP                       //表明程序结束

根据代码我们可以看出,复位中断主要是调用了SystemInit__main 这两个函数,下面我们再来详细介绍下这两个函数都做了什么工作。

SystemInit函数详解

利用跳转功能,我们可以看到SystemInit()函数的代码部分:

/**
  * @brief   设置微控制器系统
  *         初始化嵌入式Flash接口、PLL,并且更新SystemCoreClock 变量
  * @note   此功能仅能在复位后使用.
  * @param  None
  * @retval None
  */
void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifdef STM32F10X_CL
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      
#else
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
    
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif 

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 
}

根据上面的代码,我们可以看出,SystemInit()主要做了两件事:

  • 1、初始化时钟:SYSCLK,HCLK,PCLK2 PCLK1 预分频器等
  • 2、配置中断向量表:中断向量表的定位是在 Flash 还是 SRAM,以及是否需要偏移

注意:

  • 可以通过system_stm32f1xx文件中的宏定义修改系统时钟频率(通过设置锁相环的相关系数),中断向量表的地址(位于SRAM还是Flsah,是否偏移,偏移地址多少等参数)
  • 函数内含VTOR寄存器(即中断向量偏移)设置:SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; 产品IAP由BootLoader跳转到app程序时,需设置中断向量偏移。

__main函数详解

__main 其实不是我们定义的,当编译器编译时,只要遇到这个标号就会定义这个函数,该函数的主要功能是:负责初始化栈、堆,配置系统境,并在最后跳转到用户自定义的main函数,从此来到C的世界。
所以在不同的IDE该函数的名称也有所不同,但所实现的功能大同小异:

  • 完成全局变量/静态变量/常量的初始化和重定位工作
    • a. 跳转进入__scatterload_rt2函数:通过设置四个寄存器来配置待copy内容(静态变量、全局变量、常量)的的加载域和运行域,设置待copy内容的大小,为后续__scatterload_cpy()函数服务。
    • b. 跳转进入__scatterload_cpy函数,完成静态变量、全局变量、常量的从flash到SRAM的重定位。
    • c. 跳转进入__scatterload_zeroinit函数,完成未初始化的全局变量的初始化。
  • 初始化堆栈(这里指程序栈)
    • 跳转进入__user_steup_stackheap函数:调用 __user_libspac__user_libspace 为C库保持了静态数据。这是一个96字节,0初始化的数据块,该块由C库创建。在C库初始化期间可以用来当做临时栈。再调用 __user_initial_stackheap 用户的初始化堆栈函数,实现用户的堆栈的配置,调用 _fp_init 和 __rt_fp_status_addr (C库函数) 两个函数调用实现浮点运算的支持。
    • 如果在”魔法棒“—”Target“中编译勾选了”Use Micro_lib“,程序则采用单区存放堆栈的方式;否则,采用双区存储的方式,分别初始化堆区、栈区。
  • 程序跳转,进入main()函数,执行用户代码

最后,总结下STM32从Flash的启动流程:
1、初始化堆栈指针。 单片机复位后从0x0800 0000处读取栈顶地址并保存。
2、初始化PC指针。 从0x0800 0004读取中断向量表的起始地址(复位中断入口地址),接着跳转到复位程序
3、初始向量表,然后设置时钟,设置堆栈。
4、最后跳转到C空间的main函数,即进入用户程序。
在这里插入图片描述

附录

stm32单片机的存储器映像

类型起始地址存储器用途
ROM0x0800 0000程序存储器Flash存储C语言编译后的程序代码
0x1FFF F000系统存储器存储BootLoader,用于串口下载
0x1FFF F800选项字节存储一些独立于程序代码的配置参数
RAM0x2000 0000运行内存SRAM存储运行过程中的临时变量
0x4000 0000外设寄存器存储各个外设的配置参数
0xE000 0000内核外设寄存器存储内核各个外设的配置参数

中断向量表的映射

BOOT启动方式主要有三种,主闪存存储器启动、系统存储器启动、内置SRAM启动,BOOT1和BOOT0在芯片复位时的电平状态决定了芯片复位后从哪个区域开始执行程序。

注:启动模式只决定程序烧录的位置,加载完程序之后会有一个重映射(映射到0x00000000地址位置)。所以说STM32上电复位以后,代码区都是从0x00000000开始的,三种启动模式只是将各自存储空间的地址映射到0x00000000中。

对应的BOOT引脚状态及启动地址如下图:

BOOT1BOOT0启动模式启动地址说明
X0主闪存存储器Flash0x0800 0000中断向量表定位于FLASH区,主闪存被选为启动区域,最常用,用户代码。同时复位后PC指针位于0x2000000处
01系统存储器0x1FFF F000系统存储器被选为启动区域,程序功能由厂家设置。中断向量表定位于内置Bootloader区,此时可通过串口下载程序的二进制文件到flash区
11内置SRAM0x2000 0000内置SRAM被选为启动区域,中断向量表定位于SRAM区,同时复位后PC指针位于0x2000000处

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

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

相关文章

如何进行Feign的自定义配置

Feign可以支持很多的自定义配置&#xff0c;下列是部分配置信息 类型作用说明Logger.Level修改日志级别配置键&#xff1a;loggerLevel&#xff0c;包含四种不同的级别&#xff1a;NONE、BASIC、HEADERS、FULLClass<Retryer>失败重试机制配置键&#xff1a;retryer&#…

C语言程序_速通_基础_笔记1_备战大一期末考_12.16

1.固定格式 #include<stdio.h>int main(){return 0;} 2.printf 正常输出&#xff1a;printf("XXXXX"); 如果没有任何换行符什么的&#xff0c;输进去多少个printf&#xff0c;就直接连在一起。 \n&#xff0c;换行 \t 空格 3.int float double char flo…

牛客第一期

1.创建动态数组 #include <iostream> using namespace std;int main() {int n; cin>>n; int *pnew int [n]; int i0; for(i0;i<n;i) {*(pi)ni; } int j0; for(j0;j<n;j) {printf("%d ",*(pj)); } } #include<bits/stdc.h> using namespace s…

文件上传自动化测试方案(超详细)

一、概述 【测试地址】&#xff1a;https://pan.baidu.com 【测试工具】&#xff1a;selenium、requests 【脚本语言】&#xff1a;Python 【运行环境】&#xff1a;Windows 百度网盘作为文件存储及分享的平台&#xff0c;核心功能大部分是对文件的操作&#xff0c;如果要…

2024GoCN线下活动全面启动,赠送深圳MeetUp门票

2024年GoCN社区将全面启动一系列令人期待的线下活动---不仅将在北京、上海、深圳、武汉、成都、杭州、广州、西安等地举办 meetup&#xff0c;还将在北京和上海举办 GopherChina 大会。 2024议题征集通道已开启&#xff0c;欢迎各位有实战经验、独特观点的Gopher前来分享~ 2024…

vue中iframe标签跨域通信——父子页面之间传值(解决子页面收不到父页面的值或子页面收到的数据为undefined的问题)

背景 因为本系统需要支持第三方系统页面的嵌入&#xff0c;于是尝试使用iframe标签&#xff0c;进行跨域通信&#xff0c;父子页面相互传值。初始化时&#xff0c;父页面发送数据给子页面&#xff0c;需要在子页面加载完成后发送&#xff0c;不然接收不到数据。父页面直接给子页…

湖北点赋网络:明星为何钟爱美式咖啡?品味与生活的完美融合

美式咖啡作为一种传统的咖啡饮品&#xff0c;备受明星们的青睐。无论是在电影拍摄现场&#xff0c;还是舞台后台&#xff0c;明星们经常手持着一杯美式咖啡&#xff0c;这不仅仅是为了满足他们的口味偏好&#xff0c;更是一种享受和生活方式的体现。那么&#xff0c;为何明星们…

磁力计LIS2MDL开发(2)----电子罗盘

磁力计LIS2MDL开发.2--电子罗盘 概述视频教学样品申请源码下载环境磁场建模消除硬铁误差软铁干扰主程序 概述 本文将介绍如何使用 LIS2MDL 传感器来读取数据来转化为指南针。 地磁场强度范围约为 23,000 至 66,000 nT &#xff0c;并且可以建模为磁偶极子&#xff0c;其场线起…

Spring AOP 和 Spring Boot 统一功能处理

文章目录 Spring AOP 是什么什么是 AOPAOP 组成切面&#xff08;Aspect&#xff09;连接点&#xff08;Join Point&#xff09;切点&#xff08;Pointcut&#xff09;通知&#xff08;Advice&#xff09; 实现 Spring AOP添加 Spring AOP 框架支持execution表达式定义切面、切点…

Pycharm第三方库导入失败避坑!

最近遇到了明明安装了 python 第三方库&#xff0c;但是在 pycharm 当中却导入不成功的问题。 使用Pycharm手动安装三方库和自动安装三方库都失败&#xff0c;以及Pycharm终端使用pip命令安装也未解决。网上找各种方法尝试都没成功&#xff0c;原来是一不小心就跳进了虚拟环境…

【经验分享】openGauss 5.0.0全密态数据库应用小试

引子&#xff1a; 去年了解openGauss数据库安全特性的时候了解到全密态等只查询特性&#xff0c;实际上openGauss在早期的1.0.0版本就引入了全密态等值查询特性&#xff0c;下面尝试对openGauss 5.0.0版本全密态的使用进行记录&#xff0c;供参考。 全密态数据库&#xff1a;…

造型精致的冰精灵充电头,充电效率高安全可靠,居家出行皆可用

随着大家对手机的依赖度越来越高&#xff0c;快速充电已经成为必不可少的需求。快充当然少不了支持快充的充电器&#xff0c;现在市面上的快充头很多&#xff0c;安全性和便携性是我们选择时的重点关注方向&#xff0c;我目前用的是战飞ZEFi冰精灵&#xff0c;这款产品有着独特…

Python学习路线 - Python语言基础入门 - 函数进阶

Python学习路线 - Python语言基础入门 - 函数进阶 函数的多返回值多个返回值 函数的多种参数使用形式函数参数种类位置参数关键字参数缺省参数不定长参数位置传递 - 不定长关键字传递 - 不定长 函数作为参数传递lambda匿名函数 函数的多返回值 问: 如果一个函数如些两个return…

[Spring ~松耦合的设计神器]`SPI`

Java SPI&#xff08;Service Provider Interface&#xff09;是一种Java的服务提供者接口机制。它允许在运行时动态加载实现服务接口的类。 文章目录 基本概念最简单的实例使用 jar 包通过 spi动态实现接口功能 基本概念 SPI 机制的基本思想是&#xff0c;定义一个服务接口&a…

Git 使用教程(超级详细)

目录 一&#xff1a;Git二&#xff1a;SVN与Git的的区别三、安装Git四&#xff1a;常规操作五&#xff1a;远程仓库六&#xff1a;创建与合并分支七&#xff1a;bug分支八&#xff1a;多人协作九&#xff1a;git可视化工具 Git Git 是一种分布式版本控制系统&#xff0c;用于…

Echarts 热力图与折线图的结合

热力图与折线图结合使用(文末含源码) 这种需求并不多见&#xff0c;遇到后第一时间翻看了Echars官方文档&#xff0c;并没有发现类似的例子。于是自己动手合并了双轴&#xff0c;后发现折线图会被遮盖。经过排查发现了一个关键参数&#xff1a;visualMap的配置。这个配置在热力…

Vue脚手架 Vue CLI安装

目录 0.为什么要安装Vue CLI脚手架 1.配置方法 1.全局安装 (一次) 2.查看Vue版本&#xff08;一次&#xff09; 报错&#xff1a;出现禁止运行脚本 3.创建项目架子&#xff08;可多次&#xff09; 报错npm err! 问题&#xff1a;已知npm换过国内源&#xff0c;且进度条…

了解AOP

1 AOP概述 思考&#xff1a;在一个教务系统中&#xff0c;以下哪些是主要业务逻辑&#xff0c;哪些是次要业务逻辑&#xff1f; 1.1 关于AOP AOP&#xff08;面向方面/切面编程&#xff09;是对OOP&#xff08;面向对象编程&#xff09;的补充&#xff0c;提供另一种关于程序…

FC 可视化功能菜单主代码

[FC][BestVisible][Config].asm ;文件头配置 NES_16KB_PRG_SIZE 8 NES_8KB_CHR_SIZE 16 PRG_BANK_MASK NES_16KB_PRG_SIZE * 2 - 1 ;bank号掩码 PRG_BANK_E000 NES_16KB_PRG_SIZE * 2 - 1 PRG_BANK_C000 …

《Kotlin核心编程》笔记:集合、序列与内联函数

集合的高阶函数API map 操作 val list listOf(1, 2, 3, 4, 5, 6) val newList list.map { it * 2 }当然&#xff0c;在 Java 8 中&#xff0c;现在也能像Kotlin那样去操作集合了。 上面的方法实际上就是一个高阶函数&#xff0c;它接收的参数实际上就是一个函数&#xff0…