STM32的启动流程分析 和 一些底层控制的原理
阅读引言: 阅读本文之后, 你将对单片机, 甚至是嵌入式系统, 或者是传统的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
相关文章:

STM32的启动流程分析 和 一些底层控制的原理
阅读引言: 阅读本文之后, 你将对单片机, 甚至是嵌入式系统, 或者是传统的PC机系统的启动流程有一个大致的了解, 本文更加偏向于单片机的启动流程分析。 目录 一、基础知识 1.STM32系列的微控制器(mcu&…...
C#面:几种注释类型
三种常见的注释类型:单行注释、多行注释和 XML 注释。 单行注释: 以双斜线 // 开头,用于在一行中注释单个语句或代码块。单行注释会被编译器忽略,不会对程序的执行产生任何影响。 例如: // 这是一个单行注释 int a…...

#onenet网络请求http(GET,POST)
参考博文: POST: https://blog.csdn.net/qq_43350239/article/details/104361153 POST请求(用串口助手测试): POST /devices/1105985351/datapoints HTTP/1.1 api-key:AdbrV5kCRsKsRCfjboYOCVcF9FY Host:api.heclouds.com Con…...
零基础学习JS--基础篇--索引集合类
数组是由名称和索引引用的值构成的有序列表。 JavaScript 中没有明确的数组数据类型。但是,你可以使用预定义的 Array 对象及其方法来处理应用程序中的数组。Array 对象具有以各种方式操作数组的方法,例如连接、反转和排序。它有一个用于确定数组长度的…...
【硬件工程师面经整理25_AD】
文章目录 1 AD设计电路全流程2 ad和cadence区别-逻辑上的区别 1 AD设计电路全流程 软件AD or 模拟数字? 软件AD:AD设计电路全流程包括以下步骤:选择AD库和添加、画原理图、PCB布局、PCB布线、PCB打样、PCB加工 模拟数字: 需求分…...

神经网络的矢量化,训练与激活函数
我们现在再回到我们的神经元部分,来看我们如何用python进行正向传递。 单层的正向传递: 我们回到我们的线性回归的函数。我们每个神经元通过上述的方法,就可以得到我们的激发值,从而可以继续进行下一层。 我们用这个方法就可以得…...

3.7号freeRtoS
1. 串口通信 配置串口为异步通信 设置波特率,数据位,校验位,停止位,数据的方向 同步通信 在同步通信中,数据的传输是在发送端和接收端之间通过一个共享的时钟信号进行同步的。这意味着发送端和接收端的时钟需要保持…...

瑞芯微 | I2S-音频基础 -1
最近调试音频驱动,顺便整理学习了一下i2s、alsa相关知识,整理成了几篇文章,后续会陆续更新。 喜欢嵌入式、Li怒晓得老铁可以关注一口君账号。 1. 音频常用术语 名称含义ADC(Analog to Digit Conversion)模拟信号转换…...

Linux配置.bashrc文件导致各种命令(vim、sudo)失效。
Linux配置.bashrc文件导致各种命令(vim、sudo)失效。 起因是 nvcc-V一直报错:-bash:nvcc: command not found 踩坑记录:上网一查说是没有配置cuda的环境变量。于是去修改了bashrc文件,在最下面…...

Visual Studio 2022 Version 17.9 新功能
Visual Studio 2022 v17.9 为广大 C 开发者引入了一系列好用的新功能和改进优化。 内存布局 现在,你可以使用【内存布局,Memory Layout】功能以可视化的方式来查看对象,结构体及联合体的内存布局信息,这可比以前需要手动查看内存…...
ArrayList 和 LinkedList 的区别
ArrayList ArrayList 是基于动态数组实现的, 它使用一块连续的内存空间来存储元素,因此访问元素的速度非常快(时间复杂度为 O(1)), 但是,在插入或删除元素时,如果位置不在数组末尾࿰…...
VGG16-CF-VGG11实验报告
说明:VGG16和CF-VGG11是论文《A 3D Fluorescence Classification and Component Prediction Method Based on VGG Convolutional Neural Network and PARAFAC Analysis Method》使用的两种主要模型。其对应代码仓库提供了实验使用的数据集、平行因子分析结果和CNN模型。论文和…...

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用
基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍TM1638键盘…...

uniapp使用华为云OBS进行上传
前言:无论是使用华为云还是阿里云,使用其产品的时候必须阅读文档 1、以华为云为例,刚接触此功能肯定是无从下手的情况,那么我们需要思考,我们使用该产品所用到的文档是什么 2、我们要使用obs 文件上传,肯…...

用一个 Python 脚本实现依次运行其他多个带 argparse 命令行参数的 .py 文件
🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 问题描述:在 Windows 环境中,您希望通过一个 Python 脚本来实现特定的自动化任务,该任务需要依次运行其他多个带 argparse 命令行参数的 .py 文件。您希望找到一种简…...
力扣热题100_普通数组_189_轮转数组
文章目录 题目链接解题思路解题代码 题目链接 189. 轮转数组 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] …...

讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
1.需求 最近linux下的Qt项目中要连接oracle数据库,用户需要我们访问他们的oracle数据库,查询数据 2.遇到的问题 qt连接oracle数据库需要oracle的驱动库libqsqloci.so插件,需要编译下,之前没有编译过,看了网上的…...

SpringCloud Ribbon 负载均衡服务调用
一、前言 接下来是开展一系列的 SpringCloud 的学习之旅,从传统的模块之间调用,一步步的升级为 SpringCloud 模块之间的调用,此篇文章为第三篇,即介绍 Ribbon 负载均衡服务调用 二、概述 2.1 Ribbon 是什么 Spring Cloud Ribbon…...

物联网在智慧城市建设中的关键作用:连接、感知、智能响应
一、引言 随着信息技术的飞速发展,物联网(IoT)技术已经渗透到我们生活的方方面面,特别是在智慧城市建设中发挥着至关重要的作用。智慧城市是指通过运用先进的信息和通信技术,实现城市基础设施、公共服务、交通管理、环…...

安卓7原生相机切到视频崩溃
目录 1、查看日志 2、分析日志、提取重点 3、寻找解决方法 author daisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 daisy.skye_嵌入式,Linux,Qt-CSDN博客daisy.skye擅长嵌入式,Linux,Qt,等方面的知识https://blog.csdn.net/qq_40715266?typeblog 1、查看日志 由于安…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...