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. helm3培训文档

    helm介绍1.传统服务部署到k8s集群的流程拉取代码--> 打包编译-->构建镜像-->准备一堆相关部署的yaml文件(如:deployment.service.ingress等) ...

  2. layui的验证码倒计时按钮

    HTML部分 <div class="layui-form-item"> <label class="layui-icon layui-icon-ver ...

  3. WindowsPE文件格式入门05.PE加载器LoadPE

    https://bpsend.net/thread-316-1-1.html LoadPE - pe 加载器 壳的前身 如果想访问一个程序运行起来的内存,一种方法就是跨进程读写内存,但是跨进程读写内存 ...

  4. 爬取西刺代理的IP与端口(一)

    0x01 简陋代码是,获取(.*?)的字符串 #coding:utf-8 from requests import * import re headers = { "accept" ...

  5. Chocolate

    提供一种比较清新的做法. 思路:贪心. 思考:如果在巧克力上横向切一刀,那么纵向要切的刀数就会加一.如果在巧克力上纵向切一刀,那么横向要切的刀数就会加一. 结论:要先切代价大的,再切代价小的. 做法: ...

  6. Vue3中Mock数据的简单方案

    因为Vue3项目开发中需要用到Mock数据,所以这里记录一种快速Mock数据的方法. 一.安装 首先,你需要安装 axios 和 axios-mock-adapter. npm install axi ...

  7. 构建基于Serverless架构的向量检索MCP Server

    构建基于Serverless架构的向量检索MCP Server 随着LLM与Agent的快速发展,向量检索成为构建高效语义搜索和智能推荐系统的关键技术之一.OpenSearch Service 作为一 ...

  8. 函数使用十三:BAPI_REQUISITION_CREATE

    *&---------------------------------------------------------------------**& Report  ZBAPI_REQ ...

  9. Java 并发工具类核心使用场景深度解析

    在 Java 并发编程中,java.util.concurrent(JUC)包提供的工具类是解决多线程协作.资源控制及任务调度的关键.本文聚焦同步协调.资源控制.线程协作.并行计算四大核心场景,系统解 ...

  10. ChatClient vs ChatModel:开发者必须知道的4大区别!

    在 Spring AI/Spring AI Alibaba 框架中,ChatModel 和 ChatClient 都可以实现大模型的文本生成功能,例如聊天机器人,但二者是两种不同层级的 API 封装, ...