STM32F4启动代码分析
用到的汇编知识介绍
;
在汇编中,分号(;)用于表示注释。当汇编器遇到分号时,它会将分号后的内容视为注释,不会对其进行编译或解析。
指令介绍如下
可以利用keil直接查看汇编指令
代码解读
配置栈(Stack)的大小和相关信息
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
这段代码的作用是将栈的大小设置为0x0000 0400(1kb),栈名为STACK,不初始化,可读可写,8(2^3)字节对齐,__initial_sp表示栈顶地址,就是栈结束的地址。
通过MAP文件可知(在目标工程栏–>>双击工程名,就会在kail文件显示框出现map文件),STACK的地址为 0x20002150, Stack_Mem大小为1024字节,__initial_sp的地址为0x20002550(0x20002150+0x400),因为栈是由高地址向低地址生长。
Stack_Size EQU 0x00000400
在该代码中,‘Stack_Size’用于指定栈的大小,即为栈分配的内存空间大小,此处将栈的大小设置为1024字节。栈用于存储函数调用期间的局部变量、函数参数以及其他与函数执行相关的信息。可以根据应用程序的需求,通过设置’Stack_Size’的值来调整栈的大小。
AREA STACK, NOINIT, READWRITE, ALIGN=3
在汇编语言中,’AREA’指令用于在程序中创建一个新的存储区域,并指定该区域的属性和特征。
以下是’AREA’指令中的参数解释:
- ‘STACK’:存储区域的名称,此处名称为”STACK“。
- ’NOINIT‘:存储区域中的数据不需要进行初始化。
- ’READWRITE‘:存储区域可以被读取和写入。
- ’ALIGN=3’:存储区域的对齐方式,为2的ALIGN次方。在此处,对齐值为3,表示存储区域的地址需要按照2^3=8字节的边界对齐。
这行指令的作用是定义一个名为’STACK’的存储区域,该区域不需要进行初始化,可以被读取和写入,并且其地址需要按照8字节的边界对齐。
Stack_Mem SPACE Stack_Size
在汇编语言中,‘SPACE’是一个伪指令,用于分配一段指定大小的连续内存空间(给STACK分配)。
’Stack_Mem‘是一个符号,用来表示一个内存区域的起始地址和标签。’Stack_Size‘是一个常数或符号,表示要分配的内存空间的大小。
这行代码的目的是给STACK分配一块大小为’Stack_Size’ 字节的内存空间,并将其起始地址赋值给 ’Stack_Mem’符号,以便在后续的程序中使用这个内存空间。
__initial_sp
‘__initial_sp’是一个符号,用于表示初始栈指针的值。
在嵌入式系统中,栈是用来管理程序执行过程中的函数调用、局部变量和其他相关数据的一种数据结构。栈指针是一个寄存器或内存地址,它指向当前栈顶的位置。在程序执行过程中,栈指针会根据函数调用和返回的操作动态地进行移动,但初始栈指针的值是在系统启动时预先确定的。当函数被调用时,栈指针会向下移动,为局部变量和其他数据结构分配空间。当函数返回时,栈指针会向上移动,释放之前分配的空间。
配置堆(Heap)的大小和相关信息
堆是计算机内存中一种用于动态分配内存的数据结构。它用于存储程序运行时需要动态分配的内存对象,例如在运行时创建的对象、数据结构、数组等。堆内存的分配和释放是在程序运行时动态进行的,与静态内存(如全局变量和静态变量)或栈内存(用于函数调用和局部变量)不同。
堆内存的特点包括:
- 动态分配:堆内存的大小可以根据需要在运行时进行动态分配和释放,而不是在编译时确定。
- 灵活性:堆内存可以用于存储各种大小和类型的对象,可以根据实际需求进行动态调整。
- 持久性:堆内存中的对象可以在函数调用之间保持存在,不会随着函数的退出而销毁。
- 相对较慢的访问速度:与栈内存相比,堆内存的访问速度较慢,因为需要通过指针进行间接访问。
例如使用malloc()等相关函数在堆上分配内存,然后使用free()函数释放已分配的内存。
Heap_Size EQU 0x00000000 ;我们不使用MDK自带的malloc和free函数,所以设置这里为0.
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
  这段代码的作用是定义一个名为’HEAP’的区域,用于存储堆的数据。通过MAP文件可知,堆的大小为0
Heap_Size EQU 0x00000000 ;我们不使用MDK自带的malloc和free函数,所以设置这里为0.
利用EQU伪指令设置Heap_Size的大小为0,即堆(Heap)的大小被设置为0字节,没有为堆分配任何内存空间。
AREA HEAP, NOINIT, READWRITE, ALIGN=3
利用AREA指令定义一个名为’HEAP’的存储区域,该区域不需要初始化,可以被读取和写入,并且其起始地址需要按照8字节的边界对齐。
__heap_base
该标识符用于表示堆的起始地址
Heap_Mem SPACE Heap_Size
利用SPACE指令在堆中为变量‘Heap_Mem’分配了‘Heap_Size’大小的连续内存空间。
__heap_limit
 ’__heap_limit‘是一个汇编语言中的符号,用于表示堆的结束地址。
对齐方式与兼容指令集
PRESERVE8
'PRESERVE8’是一个指令或指示符,用于在汇编语言中指定对齐要求。指定当前文件的栈按照8字节对齐
THUMB
THUMB指定后面所使用的指令集为THUMB
中断向量表定义
; Vector Table Mapped to Address 0 at Reset
存储在地址0处的向量表(vector Table),用于处理器复位时的初始化。(地址0并不是真正的地址0,假设STM32从flash启动,则此中断向量表起始地址为0x8000000)
AREA RESET, DATA, READONLY
定义了名为’RESET’的代码段,其中存储的数据是只读的。
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
EXPORT伪指令,用于在程序中声明全局的变量,该变量可以在其他文件中引用。EXPORT可用GLOBAL代替。变量在程序中区分大小写。
- ‘EXPORT __Vectors’:导出变量‘__Vectors’
- ‘EXPORT __Vectors_End’:导出变量‘__Vectors_End’,表示向量表的结束位置。
- ’EXPORT __Vectors_Size‘:导出变量’__Vectors_Size‘,表示向量表的大小。
__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 DAC1&2 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 DCD __initial_sp ; Top of Stack
’DCD‘是一个伪指令,用于分配一片连续的字存储单元并用指定的数据初始化。此处是开辟一个4字节对齐的空间,该空间存着栈的起始地址。
向量表的第一个表项是栈顶地址,该处物理地址值即为__Vectors 变量所表示的值,该地址中存储’__initial_sp‘所表示的地址值,大小为一个字(32bit)。
DCD Reset_Handler ; Reset Handler
。。。。。。。
DCD FPU_IRQHandler ; FPU
这些代码将复位处理函数Reset_Handler 等的地址存于中断向量表中,每个中断都有一个对应的处理程序。
DCD 0 ; Reserved
这种行代码定义一个字存储0,在定义该字之前会插入3个字节的填充,以实现4字节对齐。
__Vectors_End
这行代码定义了向量表的结束。
总的来说,向量表这段代码定义了一个包含处理器中断和异常处理程序地址的向量表。在处理器发生中断或异常时,它会根据向量表中的条目跳转到相应的处理程序。
__Vectors_Size EQU __Vectors_End - __Vectors
’__Vectors_Size‘是一个符号常量,用于表示向量表的大小。其值是通过’ __Vectors_End - __Vectors‘得到,即向量表的结束位置减去向量表的起始位置。
__Vectors_Size为0x188,是因为有98个元素,一个DCD分配一个或多个字的内存,这里分配的一个字即4个字节。
AREA |.text|, CODE, READONLY
这行代码是将下面的代码片段分配到名为‘.text’的区域,并指定该区域的属性为‘CODE’和‘READONLY’。
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
;IMPORT SystemInit ;寄存器代码,不需要在这里调用SystemInit函数,故屏蔽掉,库函数版本代码,可以留下
;不过需要在外部实现SystemInit函数,否则会报错.
IMPORT __main
LDR R0, =0xE000ED88 ; 使能浮点运算 CP10,CP11
LDR R1,[R0]
ORR R1,R1,#(0xF << 20)
STR R1,[R0]
;LDR R0, =SystemInit ;寄存器代码,未用到,屏蔽
;BLX R0 ;寄存器代码,未用到,屏蔽
LDR R0, =__main
BX R0
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
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 .
ENDP
ALIGN
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
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
复位处理程序
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
;IMPORT SystemInit ;寄存器代码,不需要在这里调用SystemInit函数,故屏蔽掉,库函数版本代码,可以留下
;不过需要在外部实现SystemInit函数,否则会报错.
IMPORT __main
LDR R0, =0xE000ED88 ; 使能浮点运算 CP10,CP11
LDR R1,[R0]
ORR R1,R1,#(0xF << 20)
STR R1,[R0]
;LDR R0, =SystemInit ;寄存器代码,未用到,屏蔽
;BLX R0 ;寄存器代码,未用到,屏蔽
LDR R0, =__main
BX R0
ENDP
第一行代码
Reset_Handler PROC
这行代码是用来定义一个Reset_Handler函数的开始,PROC和ENDP配套使用。
EXPORT Reset_Handler [WEAK]
这一行是用来导出’Reset_Handler’符号,并将其标记为弱符号。
[WEAK]表示Reset_Handler是一个弱符号。弱符号与强符号相对应,其特点是如果存在多个同名的弱符号,链接器将选择其中一个,并在没有强符号的情况下使用它。如果存在强符号,则优先选择强符号。
IMPORT __main
这行代码表示该符号来自外部文件
使能浮点运算 CP10,CP11
LDR R0, =0xE000ED88
LDR指令的作用是将一个立即数加载到寄存器中。
这里将地址0xE000ED88赋给寄存器R0
LDR R1,[R0]
这行代码的作用是将寄存器R0中存储的地址作为内存地址,从该地址读取数据,赋值给R1。
ORR R1,R1,#(0xF << 20)
ORR(”OR“ with register)指令,具体用法如下:
ORR Rd, Rn, Operand2
- ‘Rd’是目标寄存器,存储运算结果
- ‘Rn’是第一个操作数寄存器,用于与第二个操作数进行逻辑或运算。
- Operand2是第二个操作数,可以是一个寄存器、一个立即数或一个移位后的寄存器。
- ORR指令执行的操作是将’Rn’寄存器的值与‘Operand2’进行逻辑或运算,并将结果存储到‘Rd’寄存器中。
这行代码是将R1寄存器的20至23位设置为1,其余位保持不变。
STR R1,[R0]
这条指令是将寄存器R1的值存储到寄存器R0的值所对应的内存地址处,即存到0xE000ED88处。
LDR R0, =__main
这行代码将__main的地址加载到寄存器R0中。
BX R0
’BX R0‘是一个跳转指令,它用于无条件跳转到寄存器RO中存储的地址。当执行该指令时,程序将跳转到寄存器R0中存储的地址,并且程序的执行将从该地址处继续。在这个特定的代码片段中,BX R0指令用于跳转到__main函数的入口地址,开始执行程序的主体逻辑。通过跳转到__main函数,程序将继续执行主函数中的指令和代码逻辑。
ENDP
ENDP表示该代码片的结束。
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
只分析以下语句
B .
‘B’指令是一个无条件分支指令,用于无条件跳转到一个指定的标签或地址。
‘.’代表当前指令的地址。
这个指令的作用是创建一个无限循环,使程序一直在这个位置循环执行,直到被中断或重置。
这种无限循环的用法常见于嵌入式系统的启动代码或异常处理程序中,目的是保持处理器处于一个稳定状态或等待外部事件的发生。
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
。。。。
FPU_IRQHandler
B .
ENDP
这部分代码中,所有的中断函数都被定义为弱符号(weak symbol),且每个中断函数都只有函数名。这意味着它们的默认实现是空函数或者无操作。这样做的目的是为了提供一个默认的中断处理行为,以防止程序在中断触发时崩溃。
当某个中断触发时,处理器会跳转到对应中断处理函数的地址执行,但由于这些中断处理函数是弱符号并没有具体的实现,所以处理器会继续执行下面的代码,直到遇到无限循环的’B .'指令,程序就会停留在这个无限循环中,等待下一次的中断触发。
当在其他地方实现了中断函数,并被定义为强符号,则中断触发的时候,执行强符号对应的中断处理函数。
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
在 IF :DEF:__MICROLIB 条件判断下,如果定义了 __MICROLIB 宏,表示使用微库,那么会导出 __initial_sp、__heap_base 和 __heap_limit 符号。这些符号通常用于微库运行时环境的初始化和配置。
如果没有定义 __MICROLIB 宏,表示不使用微库,那么会导入 __use_two_region_memory 符号,并导出 __user_initial_stackheap 符号。这部分代码通常用于配置堆栈和堆内存的起始地址。
在 __user_initial_stackheap 函数中,将堆内存的起始地址 Heap_Mem、堆栈的起始地址 Stack_Mem 和堆栈的大小 Stack_Size 加载到寄存器 R0、R1、R2 和 R3 中。然后通过 BX LR 指令返回,将控制权交还给调用者。
最后,使用 ALIGN 指令进行对齐,并结束代码块。
上电后单片机运行流程
单片机上电后首先是启用ST公司固化的代码,检测BOOT0与BOOT1引脚电平,确定启动模式。
在确定启动模式后,对于Flash,CPU从地址0x0000 0000获取栈顶的地址,并从0x0000 0004指示的地址开始执行代码,即执行Reset_Handler。