0 启动流程

  1. 复位
  2. 通过boot引脚选择启动模式
  3. 从地址0x00000000读取 __initial_sp 初始栈顶值到MSP
  4. 从地址0x00000004读取 Reset_Handler 地址到PC
  • 32位系统, 字长4字节
  1. 指向复位向量的内容
  2. 执行SystemInit函数, 初始化系统时钟
  3. 执行__main, 软件设置SP指针, 初始化栈空间, 清除bss段, 最后跳转到mian函数

1 启动模式

BOOT0 BOOT1 启动模式
0 X 从FLASH启动0x0800 0000
1 0 从系统存储器启动(bootROM)
1 1 从RAM启动0x2000 0000
  • pc指针在硬件复位后会自动指向0x0000 0000, 而Flash实际起始地址:0x0800 0000, RAM实际起始地址: 0x2000 0000, 硬件会自动将0x0000 0000映射到对应的地址

2 启动流程

2.1 Reset_Handler 复位向量

2.1.1 Reset_Handler 源代码:

; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

2.1.2 代码分析:

导入 SystemInit 和 __main 两个符号,并LDR数据到R0寄存器,跳转执行(BLX/BX)

2.2 SystemInit

2.2.1 SystemInit源代码:

2.2.1.1HAL库中的SystemInit

void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif #if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */ /* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
}

2.2.1.2 STD库中的SystemInit

2.2.2 代码分析:

  1. HAL库中的SystemInit: 根据宏配置初始化FPU, 外部SRAM, 中断向量表(根据用户设置的地址)
  2. STD库中的SystemInit: 根据宏配置初始化FPU, 外部SRAM, 初始化PLL, 设置系统时钟, 根据宏设置中断向量表地址(只能配置FLASH_BASE和SRAM_BASE)

2.3 __main

2.3.1 代码分析:

1. _初始化RW段和ZI段
- RW:程序中已经初始化的变量所占空间
- ZI:未初始化的static和全局变量以及堆栈所占的空间
2. 调用__rt_entry()函数
1. __user_initial_stackheap 是 标准C库初始化时自动调用的函数,主要用于向C库传递堆(Heap)和栈(Stack)的地址信息。它的执行时机会在复位处理函数跳转到 __main 后,在 C库初始化阶段自动触发
2. 初始化堆栈
3. 初始化库函数
4. 最后跳转到main函数

3 进入mian函数

4 启动文件分析

4.1 __initial_sp

栈顶地址,复位时 CPU 会把这个值加载到堆栈指针 SP,告诉程序临时数据存哪里。

Stack_Size      EQU     0x800              ; 定义栈大小为 2KB (2048 bytes)

AREA    STACK, NOINIT, READWRITE, ALIGN=3  ; 定义一个名为 STACK 的内存区域
Stack_Mem SPACE Stack_Size ; 分配连续 2KB 的栈内存空间
__initial_sp ; 声明栈顶符号

4.2 __heap_limit

Heap_Size      EQU     0x400          ; 定义堆大小为 1KB (1024 bytes)
AREA HEAP, NOINIT, READWRITE, ALIGN=3 ; 定义可读写、8字节对齐的HEAP段
__heap_base ; 堆起始地址符号(链接器识别)
Heap_Mem SPACE Heap_Size ; 分配连续 1KB 堆内存空间
__heap_limit ; 堆结束地址符号(Heap_Mem + Heap_Size)

4.3 处理器模式设置

PRESERVE8             ; 要求堆栈8字节对齐(兼容Cortex-M系列)
THUMB ; 指定使用Thumb指令集

4.4 中断向量表

AREA    RESET, DATA, READONLY  ; 定义只读数据段
EXPORT __Vectors ; 导出向量表起始地址(用于链接脚本)
EXPORT __Vectors_End
EXPORT __Vectors_Size__Vectors
DCD __initial_sp ; 地址0: 主栈顶地址(硬件自动加载到MSP)
DCD Reset_Handler ; 地址4: 复位处理函数(程序入口)
; ▼ 内核异常向量 ▼
DCD NMI_Handler ; NMI
DCD HardFault_Handler ; 硬件错误
DCD MemManage_Handler ; 内存管理错误
DCD BusFault_Handler ; 总线错误
DCD UsageFault_Handler ; 用法错误
DCD 0 ; 保留
DCD 0
DCD 0
DCD 0
DCD SVC_Handler ; 系统调用
DCD DebugMon_Handler ; 调试监控
DCD 0
DCD PendSV_Handler ; 可挂起系统调用
DCD SysTick_Handler ; 系统节拍定时器
; ▼ 外设中断向量 ▼
DCD WWDG_IRQHandler ; 窗口看门狗
DCD PVD_IRQHandler ; 电源电压检测
DCD TAMP_STAMP_IRQHandler ; 入侵检测和时间戳 ;
...(后续DCD均为具体外设的中断入口)
__Vectors_End ; 向量表结束标识
__Vectors_Size EQU __Vectors_End - __Vectors ; 计算向量表大小
4.5 Reset_Handler
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

4.6 异常处理函数

NMI_Handler     PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP Default_Handler PROC EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
EXPORT RTC_WKUP_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Stream0_IRQHandler [WEAK]
EXPORT DMA1_Stream1_IRQHandler [WEAK]
EXPORT DMA1_Stream2_IRQHandler [WEAK]
EXPORT DMA1_Stream3_IRQHandler [WEAK]
EXPORT DMA1_Stream4_IRQHandler [WEAK]
EXPORT DMA1_Stream5_IRQHandler [WEAK]
EXPORT DMA1_Stream6_IRQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_TIM9_IRQHandler [WEAK]
EXPORT TIM1_UP_TIM10_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_TIM11_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT OTG_FS_WKUP_IRQHandler [WEAK]
EXPORT DMA1_Stream7_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT DMA2_Stream0_IRQHandler [WEAK]
EXPORT DMA2_Stream1_IRQHandler [WEAK]
EXPORT DMA2_Stream2_IRQHandler [WEAK]
EXPORT DMA2_Stream3_IRQHandler [WEAK]
EXPORT DMA2_Stream4_IRQHandler [WEAK]
EXPORT OTG_FS_IRQHandler [WEAK]
EXPORT DMA2_Stream5_IRQHandler [WEAK]
EXPORT DMA2_Stream6_IRQHandler [WEAK]
EXPORT DMA2_Stream7_IRQHandler [WEAK]
EXPORT USART6_IRQHandler [WEAK]
EXPORT I2C3_EV_IRQHandler [WEAK]
EXPORT I2C3_ER_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
EXPORT SPI4_IRQHandler [WEAK]
EXPORT SPI5_IRQHandler [WEAK]

4.7 __user_initial_stackheap

                IF      :DEF:__MICROLIB  ; 当使用微库时的路径
; ▼ 直接导出符号供微库使用 ▼
EXPORT __initial_sp ; 导出初始栈顶地址(MICROLIB需要)
EXPORT __heap_base ; 导出堆起始地址
EXPORT __heap_limit ; 导出堆结束地址
ELSE ; 使用标准C库时的路径
; ▼ 导出动态堆栈初始化函数 ▼
IMPORT __use_two_region_memory ; 声明C库内存模型
EXPORT __user_initial_stackheap ; 导出初始化函数
__user_initial_stackheap ; 堆栈初始化函数(由C库调用)
LDR R0, =Heap_Mem ; R0 = 堆起始地址(用于malloc)
LDR R1, =(Stack_Mem + Stack_Size) ; R1 = 栈顶地址
LDR R2, = (Heap_Mem + Heap_Size) ; R2 = 堆结束地址
LDR R3, =Stack_Mem ; R3 = 栈底地址(监测栈溢出)
BX LR ; 返回调用者(C库)
ENDIF END ; 文件结束

参考文档

[深入剖析STM32]STM32 启动流程详解

stm32的启动文件详解 Reset_Handler做了什么工作 疑问--初始化pc指针的操作在哪里 ---硬件设置SP 和 PC的值_reset handler-CSDN博客

STM32启动代码分析及其汇编学习-ARM - 蓝天上的云℡ - 博客园

STM32_从SystemInit、__main到main() 已修正 - 蓝天上的云℡ - 博客园

STM32启动过程详解-CSDN博客

关于ARM CM3的启动文件分析 - strongwong - 博客园

15. 启动文件详解 — [野火]STM32库开发实战指南——基于野火霸天虎开发板 文档

典型arm32位单片机启动流程(从上电到main.c)的更多相关文章

  1. STM8单片机启动流程彻底探究--基于IAR开发环境

    初学STM8会发现,STM8官方的固件库并没有提供一个.s文件的启动代码,那么她是如何启动然后跳转到main函数执行的呢 首先,我们根据ARM的只是可以推测,STM8也是通过复位向量来启动的,假设流程 ...

  2. WPF启动流程-自己手写Main函数

    WPF一般默认提供一个MainWindow窗体,并在App.Xaml中使用StartupUri标记启动该窗体.以下通过手写实现WPF的启动. 首先先介绍一下VS默认提供的App.Xaml的结构,如下图 ...

  3. 转载-Qualcomm MSM8953启动流程:PBL-SBL1-(bootloader)LK-Android

    文章转载链接: https://blog.csdn.net/RadianceBlau/article/details/73229005 对于嵌入式工程师了解芯片启动过程是十分有必要的,在分析.调试各种 ...

  4. 【ARM】S3C6410芯片的启动流程

    S3C6410芯片的启动流程 (1) 上电后首先运行iRom(BL0)内的代码,主要完成时钟和看门狗等外围器件的初始化.(2) 拷贝SD卡或者NnadFlash中的前4k(BL1)代码到片内ram(垫 ...

  5. arm处理器启动流程分析

    2440: 启动方式:nor , nand 地址布局: 启动流程: 开发板在上电后,会从0x0地址处运行. 如果从nor flash启动,则代码要放在nor 的0地址处: 如果从nand flash启 ...

  6. [project X] tiny210(s5pv210)上电启动流程(BL0-BL2)

    建议参考文档: S5PV210-iROM-ApplicationNote-Preliminary-20091126 S5PV210_UM_REV1.1 项目介绍参考 [project X] tiny2 ...

  7. [project X] tiny210(s5pv210)上电启动流程(BL0-BL2)(转)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/ooonebook/article/det ...

  8. 嵌入式Linux驱动学习之路(五)u-boot启动流程分析

    这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...

  9. 转:OK6410内存及启动流程

    一.内存 只是从大体上介绍,并没有涉及寄存器的操作 6410的系统资源为:256MB DDR .2GB NANDFlash 如下图所示: ROM是只读存储器,RAM是随机存储器. 区别: 1.ROM( ...

  10. 转:AM335x启动流程(BootRom->MLO->Uboot)

    http://blog.chinaunix.net/uid-28458801-id-3486399.html 参考文件: 1,AM335x ARM Cortex-A8 Microprocessors ...

随机推荐

  1. 使用Node.js打造自己的Git版本控制系统

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...

  2. RPC的通信流程

    核心原理:能否画张图解释下RPC的通信流程? RPC的全称是Remote Procedure Call,即远程过程调用.RPC帮助我们屏蔽网络编程细节,实现调用远程方法跟调用本地(同一个项目中的方法) ...

  3. Django踩坑之ExtendsNode: extends 'base/base.html'> must be the first tag in the template.

    模板继承报错: extends 'base/base.html'> must be the first tag in the template base.html如下: <!-- {% l ...

  4. Microsoft 赋能企业应用开发者:无缝集成AI,无需转变编程语言

    在当前的技术浪潮中,Microsoft 针对企业级应用开发领域提出了一项新的见解,打破了关于AI.机器学习(ML)与Java之间的固有神话.作为Java或.NET企业级应用的开发者,您无需转换阵地或深 ...

  5. Kubernetes如何通过StatefulSet支持有状态应用?

    Kubernetes如何通过StatefulSet支持有状态应用? 为什么Deployment不能编排所有类型应用? Deployment认为一个应用中所有的Pod是完全一样的,所以他们之间没有顺序, ...

  6. ChatMoney化身恋爱大师来帮助你收获完美爱情!

    本文由 ChatMoney团队出品 介绍说明 在纷繁复杂的情感世界里,寻找真爱的道路常常充满迷茫和困惑.但现在,有了 AI 智能体恋爱大师,为您的爱情之旅点亮明灯. AI 智能体恋爱大师并非传统意义上 ...

  7. DTALK直播预约 | 金融行业嘉宾分享:金融机构数据治理实践路径

    金融业天然就是一个经营数据的行业,一直对数字技术保持高度重视,是数字化转型最早和数字技术应用最广的行业之一.在金融和技术融合过程中,数据治理是关键.当前金融数据治理已由过去局部数据管理,发展到统筹数据 ...

  8. ArcObject SDK 015 出图

    1.核心出图代码 出图主要是靠IExport接口,继承该接口的类如下图所示. 出不同格式的图,实例化不同的类即可.例如导出jpg格式的图片的代码如下. private void Export(stri ...

  9. XXL-JOB v3.1.1 | 分布式任务调度平台(Dify工作流调度增强)

    Release Notes 1.[调整]AI任务(difyWorkflowJobHandler)优化:针对 "baseUrl.apiKey" 等Dify配置信息,从执行器侧文件类配 ...

  10. 9.Java SDK源码分析系列笔记-LinkedHashMap

    目录 1. 是什么 2. 使用 3. 实现 3.1. uml 3.2. 构造方法 3.3. put 3.3.1. 创建LinkedHashMap增强的节点--Entry[既是Node数组的节点又是双向 ...