阅读引言: 阅读本文之后, 你将对单片机, 甚至是嵌入式系统, 或者是传统的PC机系统的启动流程有一个大致的了解, 本文更加偏向于单片机的启动流程分析。
目录
一、基础知识
1.STM32系列的微控制器(mcu)使用的计算机体系结构
2.嵌入式系统的存储器
3.CPU是如何控制外设的
二、点亮LED的几种办法
1.使用汇编指令读写指定的地址
2.使用C语言读写指定的地址
3.其它方法
三、STM32的启动流程
1.引导加载程序(Boot Loard)
2.STM32G431RBT6的启动流程分析
3.stm32启动代码详解
一、基础知识
1.STM32系列的微控制器(mcu)使用的计算机体系结构
STM32微控制器使用的是哈佛结构。哈佛结构是指指令存储器和数据存储器分开的计算机存储器结构。在STM32微控制器中,指令存储器通常是闪存,而数据存储器通常是SRAM。这种结构可以提高系统的运行效率,并且可以同时访问指令和数据,提高系统的响应速度。
2.嵌入式系统的存储器
存储装置是计算机不可或缺的组件,用于存储程序代码和数据,它赋予计算机记忆功能。存储装置通常分为主存和外部存储器。主存是电路板上的半导体存储器,而外部存储器包括硬盘、光盘、U盘、闪存卡等。 嵌入式系统的存储器系统与通用计算机系统的设计方法有所不同,嵌入式微处理器芯片上集成了一定数量的存储器,成为构成嵌入式系统硬件的主要组成部分。 存储器性能的一些指标包括易失性、只读性、位容量、速度、功耗、可靠性和价格等因素。嵌入式存储器根据掉电信息的保留情况分为ROM和RAM两类, 大致画了一个思维导图, 总结了一下。
引出这些的目的就是需要知道stm32的程序, 也就是指令是存放在flash中的, 而程序在运行期间的数据是存放在SRAM中的, 关于ROM和RAM大家可以把ROM想象成电脑的硬盘, RAM比喻成内存, 运行程序的地方。
3.CPU是如何控制外设的
- CPU的组成
CPU由以下几部分组成:
1. 控制单元(Control Unit):负责指令的解码和执行,协调各个部件的工作,控制数据的流动。
2. 运算单元(Arithmetic Logic Unit,ALU):负责执行算术和逻辑运算,例如加法、减法、乘法、与、或等操作。
3. 寄存器(Register):用来存放指令、数据和中间结果,包括程序计数器(Program Counter),指令寄存器(Instruction Register),累加器(Accumulator)等。
4. 时钟(Clock):用来同步各个部件的工作,控制指令和数据的传输和处理速度。
5. 数据总线(Data Bus):用于传输数据,连接CPU和其他设备或存储器。
6. 地址总线(Address Bus):用于传输地址信号,指示数据在存储器中的位置。
7. 控制总线(Control Bus):用于传输控制信号,控制各个部件的工作。
以上是CPU的主要组成部分,它们协同工作,使得计算机能够执行各种指令和处理数据。
其实在嵌入式系统中我们熟知的ARM只是为cpu的架构设计提供方案的公司, 或说是一种技术,准确来说我们见到的ST公司生产的芯片都是基于ARM的内核, 然后自己添加了一些硬件控制器在一块芯片里面, 专业术语叫做SOC。
好了, 言归正传, 只是铺垫得差不多了, 该说说cpu是如何控制外设的了, 比如, cpu控制LED灯的亮灭。这里得提到一个知识点, 指令。
什么是指令, 其定义是CPU能够识别并执行的机器码, 也就是一些0101的二进制数据, 我们写的代码, 给到交叉编译器编译之后得到的.hex或者.bin文件本质就是机器码, 现在知道为什么cpu能够执行我们写的程序了把, 是编译器的功能, 选择和cpu适配的编译器, 编译出的指令cpu就能识别并执行。、
cpu执行指令一般分为三个步骤、取值、译码、执行。这些都是由抓门的cpu指令的, 比如通过该cpu的取指的指令给到cpu执行,cpu通过地址总线去内存的指定位置取出指令给到cpu内部的硬件电路去执行。
编不下去, 直接给大伙看两个汇编指令, LDR, STR, 一个是cpu去指定的地址加载数据到cpu内部, 一个是cpu往指定的地址写数据, 那么, cpu控制LED灯点亮, 大家是不是已经想到答案了, cpu之所以能够控制硬件, 就是其能够通过总线去读写存储器的指定位置。
二、点亮LED的几种办法
1.使用汇编指令读写指定的地址
去芯片厂家提供的地址映射表可以查到对用外设的地址, LTR, STR, MOV,这些指令往控制LED的寄存器的指定的那一个位写值, 即可控制LED
LDR R1, [R2]
STR R1, [R2]
2.使用C语言读写指定的地址
假设你知道了控制LED的gpio的数据寄存器的地址, 哪就可以这样干。
#define LED_DATA (0X48000000)void led_on(void)
{*(volatile unsigned int *)0x4800000 = *(volatile unsigned int *)0x4800000 | 1 << i
}
* 二.C语言访问存储器* 1.读存储器* data = *ADDR* 2.写存储器* *ADDR = data*/
3.其它方法
各个芯片厂家提供的SDK, 其本质就是我上面说的那个, 将地址转换位C语言中的指针, 封装成宏, 结构体等等, 提供接口, 提升开发效率。
三、STM32的启动流程
1.引导加载程序(Boot Loard)
是芯片厂家固化在IROM(内部存储器)的一段代码, 可以和windows中的BIOS对比学, 作用是引导cpu执行程序的, 初始化软硬件, 接着该段程序会去读boot引脚的电平状态, 确定程序的入口地址在哪
2.STM32G431RBT6的启动流程分析
上面我们说, cpu之说一能控制硬件, 是因为其有读写存储器的能力, stm32是一个32位的单片机, 那是不是其能够寻址的范围就是0-4G, 这些空间内的存储器都是cpu能够访问到的,芯片厂家会对ARM架构的cpu给出的地址映射范围加上自己的外设。所谓的存储器映射其实就是给存储器分配地址的过程。
图解:
跳到flash之后, 开头的前两条指令一般都是固定的, 第一条指令, 赋值cpu内部MSP寄存器的值, 指定主栈的地址, 第二条指令是Reset_Handler这个函数的入口地址, 将这个函数的地址值给CPU内部的PC寄存器, 这样cpu就会去Reset_Handler函数对用的地址空间内取指执行了。
3.stm32启动代码详解
给大家写写注释!
;*******************************************************************************
;* @File Name : startup_stm32g431xx.s
;* @Author : MCD Application Team
;* @Brief : Vector table for MDK-ARM toolchain
;*******************************************************************************
;* Description : STM32G431xx Mainstream devices vector table for
;* MDK-ARM toolchain.
;* This module performs:
;* - Set the initial SP
;* - Set the initial PC == Reset_Handler
;* - Set the vector table entries with the exceptions ISR address
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the Cortex-M4 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.
;********************************************************************************
;* @attention
;*
;* Copyright (c) 2019 STMicroelectronics.
;* All rights reserved.
;*
;* This software is licensed under terms that can be found in the LICENSE file
;* in the root directory of this software component.
;* If no LICENSE file comes with this software, it is provided AS-IS.
;
;*******************************************************************************
;* <<< Use Configuration Wizard in Context Menu >>>
;
; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>@EQU指令,类似C语言的宏
Stack_Size EQU 0x400 @定义了一个内存区域,名字STACK,后面是一些属性,没有初始化、可读可写、按照2^3对齐,提高访问效率AREA STACK, NOINIT, READWRITE, ALIGN=3@用于分配一个连续的地址空间
Stack_Mem SPACE Stack_Size
__initial_sp @该空间的其实地址, 也就是栈的起始地址; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>@和上面栈的定义类似, 如果没有用到malloc relloc calloc类似的函数,可以删除
Heap_Size EQU 0x200AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit@伪指令, 告诉编译器按照8字节对齐PRESERVE8@使用的是thumb指令THUMB; Vector Table Mapped to Address 0 at Reset@开辟一段空间, 名字讲座reset,存放指令的,只能读AREA RESET, DATA, READONLY@export,导出的意思,让别的.c和.s文件能够使用这个符号EXPORT __Vectors EXPORT __Vectors_EndEXPORT __Vectors_Size@一个标号,用来占位置的
@DCD,声明一个4个字节的变量
@看第一条和第二条指令, 就是msp的值和即将要执行的代码
__Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler; External InterruptsDCD WWDG_IRQHandler ; Window WatchDogDCD PVD_PVM_IRQHandler ; PVD/PVM1/PVM2/PVM3/PVM4 through EXTI Line detectionDCD RTC_TAMP_LSECSS_IRQHandler ; RTC, TAMP and RCC LSE_CSS through the EXTI lineDCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI lineDCD FLASH_IRQHandler ; FLASHDCD RCC_IRQHandler ; RCCDCD EXTI0_IRQHandler ; EXTI Line0DCD EXTI1_IRQHandler ; EXTI Line1DCD EXTI2_IRQHandler ; EXTI Line2DCD EXTI3_IRQHandler ; EXTI Line3DCD EXTI4_IRQHandler ; EXTI Line4DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6DCD 0 ; ReservedDCD ADC1_2_IRQHandler ; ADC1 and ADC2DCD USB_HP_IRQHandler ; USB Device High PriorityDCD USB_LP_IRQHandler ; USB Device Low PriorityDCD FDCAN1_IT0_IRQHandler ; FDCAN1 interrupt line 0DCD FDCAN1_IT1_IRQHandler ; FDCAN1 interrupt line 1DCD EXTI9_5_IRQHandler ; External Line[9:5]sDCD TIM1_BRK_TIM15_IRQHandler ; TIM1 Break, Transition error, Index error and TIM15DCD TIM1_UP_TIM16_IRQHandler ; TIM1 Update and TIM16DCD TIM1_TRG_COM_TIM17_IRQHandler ; TIM1 Trigger, Commutation, Direction change, Index and TIM17DCD TIM1_CC_IRQHandler ; TIM1 Capture CompareDCD TIM2_IRQHandler ; TIM2DCD TIM3_IRQHandler ; TIM3DCD TIM4_IRQHandler ; TIM4DCD I2C1_EV_IRQHandler ; I2C1 EventDCD I2C1_ER_IRQHandler ; I2C1 ErrorDCD I2C2_EV_IRQHandler ; I2C2 EventDCD I2C2_ER_IRQHandler ; I2C2 ErrorDCD SPI1_IRQHandler ; SPI1DCD SPI2_IRQHandler ; SPI2DCD USART1_IRQHandler ; USART1DCD USART2_IRQHandler ; USART2DCD USART3_IRQHandler ; USART3DCD EXTI15_10_IRQHandler ; External Line[15:10]DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI LineDCD USBWakeUp_IRQHandler ; USB Wakeup through EXTI lineDCD TIM8_BRK_IRQHandler ; TIM8 Break, Transition error and Index error InterruptDCD TIM8_UP_IRQHandler ; TIM8 Update InterruptDCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger, Commutation, Direction change and Index InterruptDCD TIM8_CC_IRQHandler ; TIM8 Capture Compare InterruptDCD 0 ; ReservedDCD 0 ; ReservedDCD LPTIM1_IRQHandler ; LP TIM1 interruptDCD 0 ; ReservedDCD SPI3_IRQHandler ; SPI3DCD UART4_IRQHandler ; UART4DCD 0 ; ReservedDCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&3 underrun errorsDCD TIM7_IRQHandler ; TIM7DCD DMA2_Channel1_IRQHandler ; DMA2 Channel 1DCD DMA2_Channel2_IRQHandler ; DMA2 Channel 2DCD DMA2_Channel3_IRQHandler ; DMA2 Channel 3DCD DMA2_Channel4_IRQHandler ; DMA2 Channel 4DCD DMA2_Channel5_IRQHandler ; DMA2 Channel 5DCD 0 ; ReservedDCD 0 ; ReservedDCD UCPD1_IRQHandler ; UCPD1DCD COMP1_2_3_IRQHandler ; COMP1, COMP2 and COMP3DCD COMP4_IRQHandler ; COMP4DCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD CRS_IRQHandler ; CRS InterruptDCD SAI1_IRQHandler ; Serial Audio Interface 1 global interruptDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD FPU_IRQHandler ; FPUDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD RNG_IRQHandler ; RNG global interruptDCD LPUART1_IRQHandler ; LP UART 1 interruptDCD I2C3_EV_IRQHandler ; I2C3 EventDCD I2C3_ER_IRQHandler ; I2C3 ErrorDCD DMAMUX_OVR_IRQHandler ; DMAMUX overrun global interruptDCD 0 ; ReservedDCD 0 ; ReservedDCD DMA2_Channel6_IRQHandler ; DMA2 Channel 6DCD 0 ; ReservedDCD 0 ; ReservedDCD CORDIC_IRQHandler ; CORDICDCD FMAC_IRQHandler ; FMAC__Vectors_End@计算出向量表的大小
__Vectors_Size EQU __Vectors_End - __VectorsAREA |.text|, CODE, READONLY@复位函数的入口
Reset_Handler PROC @proc, 可以看作是C语言中的{ , endp可以看作是}EXPORT Reset_Handler [WEAK]IMPORT SystemInit @导入别的文件中的这个符号IMPORT __main @导入编译器生成的__main函数, 具体作用后面说@将SystemInit函数的地址加载到R0寄存器LDR R0, =SystemInit@跳转到R0中的地址出执行, 保存返回地址, 以便执行完SystemInit函数后返回到下一条指令执行BLX R0@加载__main的地址到R0寄存器LDR R0, =__main@跳转到R0寄存器中的地址执行,不返回BX R0ENDP; Dummy Exception Handlers (infinite loops which can be modified)@下面就是各个异常的默认处理, 都是 [WEAK]虚符号, 当出现强符号的时候会覆盖掉它
NMI_Handler PROCEXPORT NMI_Handler [WEAK]B .ENDP
HardFault_Handler\PROCEXPORT HardFault_Handler [WEAK]B .ENDP
MemManage_Handler\PROCEXPORT MemManage_Handler [WEAK]B .ENDP
BusFault_Handler\PROCEXPORT BusFault_Handler [WEAK]B .ENDP
UsageFault_Handler\PROCEXPORT UsageFault_Handler [WEAK]B .ENDP
SVC_Handler PROCEXPORT SVC_Handler [WEAK]B .ENDP
DebugMon_Handler\PROCEXPORT DebugMon_Handler [WEAK]B .ENDP
PendSV_Handler PROCEXPORT PendSV_Handler [WEAK]B .ENDP
SysTick_Handler PROCEXPORT SysTick_Handler [WEAK]B .ENDPDefault_Handler PROCEXPORT WWDG_IRQHandler [WEAK]EXPORT PVD_PVM_IRQHandler [WEAK]EXPORT RTC_TAMP_LSECSS_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_Channel1_IRQHandler [WEAK]EXPORT DMA1_Channel2_IRQHandler [WEAK]EXPORT DMA1_Channel3_IRQHandler [WEAK]EXPORT DMA1_Channel4_IRQHandler [WEAK]EXPORT DMA1_Channel5_IRQHandler [WEAK]EXPORT DMA1_Channel6_IRQHandler [WEAK]EXPORT ADC1_2_IRQHandler [WEAK]EXPORT USB_HP_IRQHandler [WEAK]EXPORT USB_LP_IRQHandler [WEAK]EXPORT FDCAN1_IT0_IRQHandler [WEAK]EXPORT FDCAN1_IT1_IRQHandler [WEAK]EXPORT EXTI9_5_IRQHandler [WEAK]EXPORT TIM1_BRK_TIM15_IRQHandler [WEAK]EXPORT TIM1_UP_TIM16_IRQHandler [WEAK]EXPORT TIM1_TRG_COM_TIM17_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 USART3_IRQHandler [WEAK]EXPORT EXTI15_10_IRQHandler [WEAK]EXPORT RTC_Alarm_IRQHandler [WEAK]EXPORT USBWakeUp_IRQHandler [WEAK]EXPORT TIM8_BRK_IRQHandler [WEAK]EXPORT TIM8_UP_IRQHandler [WEAK]EXPORT TIM8_TRG_COM_IRQHandler [WEAK]EXPORT TIM8_CC_IRQHandler [WEAK]EXPORT LPTIM1_IRQHandler [WEAK]EXPORT SPI3_IRQHandler [WEAK]EXPORT UART4_IRQHandler [WEAK]EXPORT TIM6_DAC_IRQHandler [WEAK]EXPORT TIM7_IRQHandler [WEAK]EXPORT DMA2_Channel1_IRQHandler [WEAK]EXPORT DMA2_Channel2_IRQHandler [WEAK]EXPORT DMA2_Channel3_IRQHandler [WEAK]EXPORT DMA2_Channel4_IRQHandler [WEAK]EXPORT DMA2_Channel5_IRQHandler [WEAK]EXPORT UCPD1_IRQHandler [WEAK]EXPORT COMP1_2_3_IRQHandler [WEAK]EXPORT COMP4_IRQHandler [WEAK]EXPORT CRS_IRQHandler [WEAK]EXPORT SAI1_IRQHandler [WEAK]EXPORT FPU_IRQHandler [WEAK]EXPORT RNG_IRQHandler [WEAK]EXPORT LPUART1_IRQHandler [WEAK]EXPORT I2C3_EV_IRQHandler [WEAK]EXPORT I2C3_ER_IRQHandler [WEAK]EXPORT DMAMUX_OVR_IRQHandler [WEAK]EXPORT DMA2_Channel6_IRQHandler [WEAK]EXPORT CORDIC_IRQHandler [WEAK]EXPORT FMAC_IRQHandler [WEAK]WWDG_IRQHandler
PVD_PVM_IRQHandler
RTC_TAMP_LSECSS_IRQHandler
RTC_WKUP_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
ADC1_2_IRQHandler
USB_HP_IRQHandler
USB_LP_IRQHandler
FDCAN1_IT0_IRQHandler
FDCAN1_IT1_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_TIM15_IRQHandler
TIM1_UP_TIM16_IRQHandler
TIM1_TRG_COM_TIM17_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTC_Alarm_IRQHandler
USBWakeUp_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
LPTIM1_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
TIM6_DAC_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_IRQHandler
DMA2_Channel5_IRQHandler
UCPD1_IRQHandler
COMP1_2_3_IRQHandler
COMP4_IRQHandler
CRS_IRQHandler
SAI1_IRQHandler
FPU_IRQHandler
RNG_IRQHandler
LPUART1_IRQHandler
I2C3_EV_IRQHandler
I2C3_ER_IRQHandler
DMAMUX_OVR_IRQHandler
DMA2_Channel6_IRQHandler
CORDIC_IRQHandler
FMAC_IRQHandlerB .ENDPALIGN;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************IF :DEF:__MICROLIBEXPORT __initial_spEXPORT __heap_baseEXPORT __heap_limitELSEIMPORT __use_two_region_memoryEXPORT __user_initial_stackheap__user_initial_stackheapLDR R0, = Heap_MemLDR R1, =(Stack_Mem + Stack_Size)LDR R2, = (Heap_Mem + Heap_Size)LDR R3, = Stack_MemBX LRALIGNENDIFEND
- 编译器的__main函数干了那些事
在 STM32 的启动代码中,`__main` 是一个由编译器生成的函数,它是程序的入口点。`__main` 函数在启动代码中被调用,在处理器初始化和启动期间执行一些必要的任务,然后进入主程序。
`__main` 函数通常会执行以下几个重要的任务:
1. 设置堆栈指针(Stack Pointer,SP):在启动代码中,首先会设置堆栈指针,即将 SP 寄存器设置为存放堆栈顶端地址的值。通过这个设置,程序可以正确使用堆栈来保存临时变量、函数调用的返回地址等。
2. 初始化数据段:启动代码将会负责把程序的初始化数据段(.data 段)从 ROM 复制到 RAM。这包括全局变量和静态变量的初始值。这样,程序在执行时就可以使用这些变量了。 3. 清零未初始化数据段:启动代码也会负责把程序的未初始化数据段(.bss 段)清零,以确保所有未初始化的全局变量和静态变量都被初始化为 0 值。
4. 初始化系统时钟和外设:启动代码可能会进行一些初始化工作,如配置系统时钟和外设的寄存器。这些初始化工作可能包括配置时钟源、设置时钟分频器、配置中断向量表等。
5. 调用 C/C++ 运行时库的初始化函数:如果使用了 C/C++ 运行时库,启动代码会在主程序执行之前调用一些运行时库的初始化函数,以确保所需的运行环境被正确设置。
6. 调用主程序(`main` 函数):启动代码结束之前,会最后调用主程序函数 `main`,开始执行主要的应用逻辑。 总的来说,`__main` 函数在 STM32 启动代码中负责执行一系列的初始化工作,以确保系统正确地启动并进入主程序。这些初始化工作包括设置堆栈指针、初始化数据段、清零未初始化数据段、初始化系统时钟和外设等。
然后,`__main` 函数会调用主程序函数 `main`,将控制权转交给主程序,开始执行应用逻辑。
够了够了, 再多就撑爆了, 希望大家有收获, 不懂异常向量表的作用的可以去翻翻我老久之前写的文章http://t.csdnimg.cn/Ac0ws