ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

STM32专题之STM32是怎么启动的?

STM32专题之STM32是怎么启动的? 目录一、启动文件详解1、栈2、heap堆3、对齐4、向量表5、复位程序6、中断服务程序7、用户堆栈初始化二、STM32的启动方式内部Flash三、STM32的启动过程内部Flash一、启动文件详解系统上电复位后第一个执行的程序是启动文件但启动文件是汇编编写的。启动文件主要做了以下工作1、初始化堆栈指针SP2、初始化PC指针3、初始化中断向量表4、配置系统时钟5、调用C库函数_main初始化用户堆栈从而最终调用main函数去到C的世界。下面我将对照启动文件的代码进行一一梳理按照先后顺序1、栈Stack_Size EQU 0x00000400 AREA STACK, NOINIT, READWRITE, ALIGN3 Stack_Mem SPACE Stack_Size __initial_sp开辟栈的大小是0x000004001KB怎么算的4*16*16/1024 1KB名字为STACKNOINIT即不初始化可读可写8字节对齐(2的3次方)。栈的作用是用于局部变量函数调用函数形参等的开销。栈的大小不能超过SRAM的大小。如果编写的程序较大定义的局部变量较多那就需要修改栈的大小。EQU宏定义的伪指令相当于等于类似C语言中的defineAREA告诉汇编器汇编一个新的代码段或者数据段。STACK表示段名可以随意命名NOINIT表示不初始化READWRITE表示可读可写ALIGN3表示按照2的3次方对齐即8字节对齐。SPACE用于分配一定大小的内存空间单位为字节这里指定大小等于Stack_Size。标号_initial_sp紧挨着SPACE语句放置表示栈的结束地址即栈顶地址这是因为栈是高地址向低地址生长的。2、heap堆Heap_Size EQU 0x00000200 AREA HEAP, NOINIT, READWRITE, ALIGN3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit开辟堆的大小是512B名字为HEAP。__heap_base表示堆的起始地址__heap_limit表示堆的结束地址堆是低地址向高地址生长的。堆主要用来动态内存的分配。像 malloc() 函数申请的内存就在堆上面。STM32用的比较少。3、对齐PRESERVE8 THUMBPRESERVE8指定当前文件的堆栈按8字节对齐。THUMB表示后面的指令兼容THUMB指令。4、向量表AREA RESET, DATA, READONLY EXPORT __Vectors EXPORT __Vectors_End EXPORT __Vectors_Size定义一个数据段名字为RESET只读。并声明__Vectors、__Vectors_End、__Vectors_Size这三个标号具有全局属性可供外部的文件调用。EXPORT 声明一个标号可被外部的文件使用使标号具有全局属性。如果是 IAR 编译器则使用的是 GLOBAL 这个指令。当内核响应一个发生的异常后对应的异常服务例程ESR就会执行。通过内核规定的“向量表查表机制”可以找到ESR的入口地址怎么做呢使用一张向量表这张向量表是一个WORD的数组每个下标对应一种异常该下标元素的值就是该ESR的入口地址。这里说明一下向量表的地址是可以设置的通过NVIC中的重定位寄存器来指出向量表的地址复位后该寄存器的值为0。因此在地址 0 即 FLASH 地址 0处必须包含一张向量表用于初始时的异常分配。要注意的是这里有个另类 0 号类型并不是什么入口地址而是给出了复位后 MSP 的初值。代码如下__Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler ; External Interrupts DCD WWDG_IRQHandler ; Window WatchDog DCD PVD_IRQHandler ; PVD through EXTI Line detection DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line DCD FLASH_IRQHandler ; FLASH DCD RCC_IRQHandler ; RCC DCD EXTI0_IRQHandler ; EXTI Line0 DCD EXTI1_IRQHandler ; EXTI Line1 DCD EXTI2_IRQHandler ; EXTI Line2 DCD EXTI3_IRQHandler ; EXTI Line3 DCD EXTI4_IRQHandler ; EXTI Line4 DCD DMA1_Stream0_IRQHandler ; DMA1 Stream 0 DCD DMA1_Stream1_IRQHandler ; DMA1 Stream 1 DCD DMA1_Stream2_IRQHandler ; DMA1 Stream 2 DCD DMA1_Stream3_IRQHandler ; DMA1 Stream 3 DCD DMA1_Stream4_IRQHandler ; DMA1 Stream 4 DCD DMA1_Stream5_IRQHandler ; DMA1 Stream 5 DCD DMA1_Stream6_IRQHandler ; DMA1 Stream 6 DCD ADC_IRQHandler ; ADC1, ADC2 and ADC3s DCD CAN1_TX_IRQHandler ; CAN1 TX DCD CAN1_RX0_IRQHandler ; CAN1 RX0 DCD CAN1_RX1_IRQHandler ; CAN1 RX1 DCD CAN1_SCE_IRQHandler ; CAN1 SCE DCD EXTI9_5_IRQHandler ; External Line[9:5]s DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9 DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10 DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11 DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare DCD TIM2_IRQHandler ; TIM2 DCD TIM3_IRQHandler ; TIM3 DCD TIM4_IRQHandler ; TIM4 DCD I2C1_EV_IRQHandler ; I2C1 Event DCD I2C1_ER_IRQHandler ; I2C1 Error DCD I2C2_EV_IRQHandler ; I2C2 Event DCD I2C2_ER_IRQHandler ; I2C2 Error DCD SPI1_IRQHandler ; SPI1 DCD SPI2_IRQHandler ; SPI2 DCD USART1_IRQHandler ; USART1 DCD USART2_IRQHandler ; USART2 DCD USART3_IRQHandler ; USART3 DCD EXTI15_10_IRQHandler ; External Line[15:10]s DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI Line DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12 DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13 DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14 DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare DCD DMA1_Stream7_IRQHandler ; DMA1 Stream7 DCD FSMC_IRQHandler ; FSMC DCD SDIO_IRQHandler ; SDIO DCD TIM5_IRQHandler ; TIM5 DCD SPI3_IRQHandler ; SPI3 DCD UART4_IRQHandler ; UART4 DCD UART5_IRQHandler ; UART5 DCD TIM6_DAC_IRQHandler ; TIM6 and DAC12 underrun errors DCD TIM7_IRQHandler ; TIM7 DCD DMA2_Stream0_IRQHandler ; DMA2 Stream 0 DCD DMA2_Stream1_IRQHandler ; DMA2 Stream 1 DCD DMA2_Stream2_IRQHandler ; DMA2 Stream 2 DCD DMA2_Stream3_IRQHandler ; DMA2 Stream 3 DCD DMA2_Stream4_IRQHandler ; DMA2 Stream 4 DCD ETH_IRQHandler ; Ethernet DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line DCD CAN2_TX_IRQHandler ; CAN2 TX DCD CAN2_RX0_IRQHandler ; CAN2 RX0 DCD CAN2_RX1_IRQHandler ; CAN2 RX1 DCD CAN2_SCE_IRQHandler ; CAN2 SCE DCD OTG_FS_IRQHandler ; USB OTG FS DCD DMA2_Stream5_IRQHandler ; DMA2 Stream 5 DCD DMA2_Stream6_IRQHandler ; DMA2 Stream 6 DCD DMA2_Stream7_IRQHandler ; DMA2 Stream 7 DCD USART6_IRQHandler ; USART6 DCD I2C3_EV_IRQHandler ; I2C3 event DCD I2C3_ER_IRQHandler ; I2C3 error DCD OTG_HS_EP1_OUT_IRQHandler ; USB OTG HS End Point 1 Out DCD OTG_HS_EP1_IN_IRQHandler ; USB OTG HS End Point 1 In DCD OTG_HS_WKUP_IRQHandler ; USB OTG HS Wakeup through EXTI DCD OTG_HS_IRQHandler ; USB OTG HS DCD DCMI_IRQHandler ; DCMI DCD CRYP_IRQHandler ; CRYP crypto DCD HASH_RNG_IRQHandler ; Hash and Rng DCD FPU_IRQHandler ; FPU __Vectors_End __Vectors_Size EQU __Vectors_End - __Vectors__Vectors和__Vectors_End分别是向量表的起始地址和结束地址相减得到向量表的大小。向量表从FLASH的0地址开始放置以4字节为一个单位地址0存放的就是栈顶地址0X04存放的是复位程序的地址以此类推。从代码上看向量表中存放的都是中断服务函数的函数名可我们知道在C语言中函数名就是一个地址。DCD分配一个或多个以字为单位的内存以四字节对齐并初始化他们。在向量表中DCD分配了一堆内存并且以ESR的入口地址初始化他们。5、复位程序AREA |.text|, CODE, READONLY定义一个名称为.text的代码段只读。Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit IMPORT __main LDR R0, SystemInit BLX R0 LDR R0, __main BX R0 ENDP复位子程序是上电后执行的第一个程序调用SystemInit函数初始化系统时钟然后调用C库函数__mian最终调用mian函数去到C的世界。WEAK表示弱定义如果外部文件优先定义了该标号则先引用该标号如果外部没有定义也不会出错。这里也说明复位子程序也可以由用户或者其他文件重新实现。IMPORT表示该标号来自于外部文件与C语言中的EXTERN类似。这里表示SystemInit和__main来自于外部文件。SystemInit() 是一个标准的库函数在 system_stm32f4xx.c 这个库文件总定义。主要作用是配置系统时钟这里调用这个函数之后 F407 的系统时钟配被配置为 168M__main 是一个标准的 C 库函数主要作用是初始化用户堆栈最终调用 main 函数去到 C 的世界。这就是为什么我们写的程序都有一个 main 函数的原因。如果我们在这里不调用 __main那么程序最终就不会调用我们 C 文件里面的 main如果是调皮的用户就可以修改主函数的名称然后在这里面 IMPORT 你写的主函数名称即可。此处不太重要后面有时间再验证吧LDR、 BLX、 BX 是 CM4 内核的指令具体作用见下表6、中断服务程序启动文件里已经帮我们写好了所有中断的中断服务函数不过这里函数都是空的真正的中断服务函数程序需要我们在外部的C文件里面重新实现这里只是提前占个位置而已。如果我们使用某个外设的时候开启了某个中断但是我们忘了编写配套的中断服务函数或者函数名写错那么当中断来临的时候程序就会跳转到启动文件这里预先写好的空的中断服务函数中并在这个空函数里面无限循环即程序死在这里了。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 CAN1_TX_IRQHandler [WEAK] EXPORT CAN1_RX0_IRQHandler [WEAK] EXPORT CAN1_RX1_IRQHandler [WEAK] EXPORT CAN1_SCE_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 USART3_IRQHandler [WEAK] EXPORT EXTI15_10_IRQHandler [WEAK] EXPORT RTC_Alarm_IRQHandler [WEAK] EXPORT OTG_FS_WKUP_IRQHandler [WEAK] EXPORT TIM8_BRK_TIM12_IRQHandler [WEAK] EXPORT TIM8_UP_TIM13_IRQHandler [WEAK] EXPORT TIM8_TRG_COM_TIM14_IRQHandler [WEAK] EXPORT TIM8_CC_IRQHandler [WEAK] EXPORT DMA1_Stream7_IRQHandler [WEAK] EXPORT FSMC_IRQHandler [WEAK] EXPORT SDIO_IRQHandler [WEAK] EXPORT TIM5_IRQHandler [WEAK] EXPORT SPI3_IRQHandler [WEAK] EXPORT UART4_IRQHandler [WEAK] EXPORT UART5_IRQHandler [WEAK] EXPORT TIM6_DAC_IRQHandler [WEAK] EXPORT TIM7_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 ETH_IRQHandler [WEAK] EXPORT ETH_WKUP_IRQHandler [WEAK] EXPORT CAN2_TX_IRQHandler [WEAK] EXPORT CAN2_RX0_IRQHandler [WEAK] EXPORT CAN2_RX1_IRQHandler [WEAK] EXPORT CAN2_SCE_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 OTG_HS_EP1_OUT_IRQHandler [WEAK] EXPORT OTG_HS_EP1_IN_IRQHandler [WEAK] EXPORT OTG_HS_WKUP_IRQHandler [WEAK] EXPORT OTG_HS_IRQHandler [WEAK] EXPORT DCMI_IRQHandler [WEAK] EXPORT CRYP_IRQHandler [WEAK] EXPORT HASH_RNG_IRQHandler [WEAK] EXPORT FPU_IRQHandler [WEAK] WWDG_IRQHandler PVD_IRQHandler TAMP_STAMP_IRQHandler RTC_WKUP_IRQHandler FLASH_IRQHandler RCC_IRQHandler EXTI0_IRQHandler EXTI1_IRQHandler EXTI2_IRQHandler EXTI3_IRQHandler EXTI4_IRQHandler DMA1_Stream0_IRQHandler DMA1_Stream1_IRQHandler DMA1_Stream2_IRQHandler DMA1_Stream3_IRQHandler DMA1_Stream4_IRQHandler DMA1_Stream5_IRQHandler DMA1_Stream6_IRQHandler ADC_IRQHandler CAN1_TX_IRQHandler CAN1_RX0_IRQHandler CAN1_RX1_IRQHandler CAN1_SCE_IRQHandler EXTI9_5_IRQHandler TIM1_BRK_TIM9_IRQHandler TIM1_UP_TIM10_IRQHandler TIM1_TRG_COM_TIM11_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 OTG_FS_WKUP_IRQHandler TIM8_BRK_TIM12_IRQHandler TIM8_UP_TIM13_IRQHandler TIM8_TRG_COM_TIM14_IRQHandler TIM8_CC_IRQHandler DMA1_Stream7_IRQHandler FSMC_IRQHandler SDIO_IRQHandler TIM5_IRQHandler SPI3_IRQHandler UART4_IRQHandler UART5_IRQHandler TIM6_DAC_IRQHandler TIM7_IRQHandler DMA2_Stream0_IRQHandler DMA2_Stream1_IRQHandler DMA2_Stream2_IRQHandler DMA2_Stream3_IRQHandler DMA2_Stream4_IRQHandler ETH_IRQHandler ETH_WKUP_IRQHandler CAN2_TX_IRQHandler CAN2_RX0_IRQHandler CAN2_RX1_IRQHandler CAN2_SCE_IRQHandler OTG_FS_IRQHandler DMA2_Stream5_IRQHandler DMA2_Stream6_IRQHandler DMA2_Stream7_IRQHandler USART6_IRQHandler I2C3_EV_IRQHandler I2C3_ER_IRQHandler OTG_HS_EP1_OUT_IRQHandler OTG_HS_EP1_IN_IRQHandler OTG_HS_WKUP_IRQHandler OTG_HS_IRQHandler DCMI_IRQHandler CRYP_IRQHandler HASH_RNG_IRQHandler FPU_IRQHandler B . ENDPB跳转到一个标号。这里跳转到一个.,表示无限循环。7、用户堆栈初始化ALIGNALIGN对指令或者数据存放的地址进行对齐后面会跟一个立即数。缺省表示 4 字节对齐。IF :DEF:__MICROLIB EXPORT __initial_sp EXPORT __heap_base EXPORT __heap_limit ELSE IMPORT __use_two_region_memory EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, Heap_Mem LDR R1, (Stack_Mem Stack_Size) LDR R2, (Heap_Mem Heap_Size) LDR R3, Stack_Mem BX LR ALIGN ENDIF END首先判断是否定义了__MICROLIB如果定义了这个宏则赋予标号 __initial_sp栈顶地址、__heap_base堆起始地址、 __heap_limit堆结束地址全局属性可供外部文件调用。有关这个宏我们在 KEIL 里面配置具体见图使用微库 。然后堆栈的初始化就由 C 库函数 _main 来完成。如果没有定义 __MICROLIB则插入标号 __use_two_region_memory这个函数需要用户自己实现具体要实现成什么样可在 KEIL 的帮助文档里面查询到具体见图 use_two_region_memory函数 。然后声明标号 __user_initial_stackheap 具有全局属性可供外部文件调用并实现这个标号的内容IF,ELSE,ENDIF汇编的条件分支语句跟 C 语言的 if ,else 类似END文件结束。二、STM32的启动方式内部FlashCM-3 内核在离开复位状态后的工作过程如下即1.从地址0x0000 0000处取出栈指针MSP的值也就是栈顶指针2.从地址0x0000 0004处取出程序指针PC的值该值指向程序复位后应执行的第一条指令上述过程由内核自动设置运行环境并执行主体程序因此它被称为自举过程。虽然内核可以固定访问地址0x0000 0000和0x0000 0004但是这两个地址可以被重映射到其它内存空间。以STM32F407为例根据芯片引出的Boot0和Boot1引脚的电平情况这两个地址可以被映射到内部Flash、内部SRAM和系统存储器中映射表见下图所以说通过改变Boot引脚实际上内核在离开复位状态后会从映射的地址取值给MSP和PC然后执行指令。内部Flash的启动方式就很好理解了首先芯片上电采样Boot引脚Boot0为0Boot1不关心就将0x0000 0000映射到0x0800 00000x0000 0004映射到0x0800 0004上因此芯片离开复位状态后读取内部Flash 0x0800 0000地址空间存储的内容赋值给栈指针MSP作为栈顶指针再读取0x0800 0004地址空间存储的内容赋值给程序指针PC作为将要执行的第一条指令所在的地址。具备这两个条件后内核就可以开始从PC指向的地址读取指令执行了。三、STM32的启动过程内部Flash启动文件的开头定义了一个大小1KB的栈空间且栈顶的地址使用标号__initial_sp在后面还定义了一个Reset Hander的复位子程序这个子程序就是芯片离开复位状态后启动的第一个执行的代码。在汇编语法中程序的名字和标号本身就是它所在的地址因此我们主要就是把__initial_sp和Reset Hander赋值到0x0800 0000和0x0800 0004的地址空间存储这样内核自举的时候就可以获得栈顶地址和第一条要执行的指令了。在启动代码的中间位置使用了汇编的关键字DCD将__initial_sp和Reset Hander定义在了最前面的地址空间。虽然放在了前面但是并没有指定绝对地址各种内容的绝对地址是由链接器根据分散加载文件 (*.sct) 分配的。默认.sct文件如下; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* LR_IROM1 0x08000000 0x00100000 { ; load region size_region ER_IROM1 0x08000000 0x00100000 { ; load address execution address *.o (RESET, First) *(InRoot$$Sections) .ANY (RO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (RW ZI) } }分散加载文件把加载区和执行区的首地址都设置为 0x08000000正好是内部 FLASH 的首地址因此汇编文件中定义的栈顶及首条指令地址会被存储到 0x08000000 和 0x08000004 的地址空间。我们可以查看反汇编代码及 map 文件信息来了解各个地址空间存储的内容从反汇编代码可了解到这个工程的 0x08000000 地址存储的值为 0x20000400 0x08000004 地址存储的值为 0x0800019d查看 map 文件这两个值正好是栈顶地址 __initial_sp 以及首条指令Reset_Handler 的地址。由此可知 BOOT0 为低电平时内核复位后从 0x08000000 读取到栈顶地址为 0x20000400了解到子程序的栈空间范围再从 0x08000004 读取到第一条指令的存储地址为 0x0800019d于是跳转到该地址执行代码即从 ResetHandler 开始运行运行 SystemInit、 __main(包含分散加载代码)最后跳转到 C 语言的 main 函数。
返回列表