一、启动文件介绍
启动文件位于标准库的下面目录中:STM32F10x_StdPeriph_Lib_V3.6.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm
标准库下载方法,可以参考这篇文章
启动文件有很多个,不同型号的单片机用的启动文件不一样,每个启动文件的详细说明见下图:
启动文件由汇编语言编写,是系统上电复位后第一个执行的程序,主要做了一下工作:
初始化SP(Stack Pointer,栈指针);
初始化PC(Programmer Counter,程序计数器)== Reset_Handler;【程序计数器内核中的一个特殊寄存器,它用于存储即将执行的下一条指令的地址】
初始化中断向量表;
配置系统时钟;
调用C库函数_main初始化用户堆栈,最终调用main函数;
启动代码中涉及到ARM汇编指令和Cortex内核的指令;
ARM汇编指令可以在Keil MDK—>Help—>Uvison Help中搜索到(太官方,可读性比较差):
二、启动文件详解
汇编语言的注释符号为 ;
Stack Configuration 栈配置
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
EQU汇编指令用来给数字常量、寄存器相对值、程序计数器相对值命名;【Stack_Size EQU 0x00000400------>即把数字常量0x00000400(4 * 16^2 = 1024 = 1KB)命名为Stack_Size】;
AREA汇编指令用来汇编一个新的代码段或数据段。AREA定义的作用范围会持续到下一个AREA伪指令出现,或者直到源文件结束。
;使用方法
AREA segment_name, attributes
;segment_name 段的名称,用户可以自定义。例如:STACK、TEXT、CODE、DATA等;
;attributes 描述端属性的关键字,可以有多个属性,以逗号分隔;
;常见属性:
;(1)CODE或DATA
; CODE 表示该段含可执行代码;
; DATA 表示该段含初始化的数据;
;(2)READONLY或READWRITE
; READONLY 表示该段的内容只能读取,通常用于代码段;
; READWRITE 表示该段的内容可以被修改,通常用于数据段;
;(3)NOINIT
; 表示该段中的数据不会在程序启动时被初始化;
;(4)ALIGN=n
; 表示段内数据的对其方式。n的值决定了对齐到2的多少次幂。例如ALIGN=3表示8(2^3)字节对齐;
;(5)CONST
; 表示该段包含常量数据;
【AREA STACK, NOINIT, READWRITE, ALIGN=3------>即定义一个代码段,段名为STACK,不初始化,可读可写,8字节对齐】;
SPACE汇编指令用于在内存中预留一块指定大小的空间,并将其初始化为零;
;预留一段内存并初始化为0,大小为Stack_Size(1024)字节,标签Stack_Mem指向这段内存的开头,标签__initial_sp指向这段内存的结尾;
Stack_Mem SPACE Stack_Size
__initial_sp
注意:栈的生长方向是从栈底(高地址)向栈顶生长(低地址)
Heap Configuration 堆配置
Heap_Size EQU 0x00000200 ;数字常量0x00000200命名为Heap_Size
AREA HEAP, NOINIT, READWRITE, ALIGN=3 ;汇编一个代码段,命名为HEAP,不初始化,可读可写,8字节对齐
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit ;预留一段内存并初始化为0,大小为Heap_Size(0x00000200,512)字节,标签Heap_Mem指向这段内存的起始地址,标签__heap_limit指向这段内存的结束地址
PRESERVE8:指定当前文件的堆栈按照8字节对齐;
THUMB:表示后面指令兼容THUMB指令。THUMB是arm以前的指令集,每个指令的大小为16位;现在Cortex-M系列都是用THUMB-2指令集,THUMB-2是32位的,兼容16位和32位的指令,即是THUMB的超集;
Vector Table Mapped to Address 0 at Reset 复位时的向量表映射
当内核响应了一个发生的异常后,对应的异常服务例程(ESR,Exception Service Routine)就会执行;
内核通过查询向量表来找到ESR的入口地址;
向量表就是一个WORD(32位)数组,每个下标对应一种异常,下标元素的值则是该ESR的入口地址。
向量表的地址存储在NVIC的一个重定位寄存器中。复位后,该寄存器的值为0,因此,地址0处必须包含一张向量表,用于初始时的异常分配。
EXPORT:声明一个标签可以被外部文件使用;
DCD:分配一个或多个内存字,按四字节边界对齐,并定义内存的初始运行时内容。
AREA RESET, DATA, READONLY ;定义一个数据段,命名为RESET,只读;
EXPORT __Vectors ;声明标签__Vectors,使其可以被外部文件使用
EXPORT __Vectors_End ;声明标签__Vectors_End,使其可以被外部文件使用
EXPORT __Vectors_Size ;声明标签__Vectors_Size,使其可以被外部文件使用
__Vectors DCD __initial_sp ; Top of Stack--->分配一个字(4字节)的内存,内存中存储__initial_sp的值;标签__Vectors指向该内存的起始地址;
DCD Reset_Handler ; Reset Handler--->分配一个字(4字节)的内存,内存中存储Reset_Handler(复位处理程序)的地址;
DCD NMI_Handler ; NMI Handler--->分配一个字(4字节)的内存,内存中存储NMI_Handler(NMI,None-Maskable Interrupt,不可屏蔽中断处理程序)的地址;
DCD HardFault_Handler ; Hard Fault Handler--->分配一个字(4字节)的内存,内存中存储HardFault_Handler(硬错误处理程序,指非常严重的错误)的地址;
DCD MemManage_Handler ; MPU Fault Handler--->分配一个字(4字节)的内存,内存中存储MemManage_Handler(内存管理处理程序)的地址;
DCD BusFault_Handler ; Bus Fault Handler--->分配一个字(4字节)的内存,内存中存储BusFault_Handler(总线错误处理程序)的地址;
DCD UsageFault_Handler ; Usage Fault Handler--->分配一个字(4字节)的内存,内存中存储UsageFault_Handler(用法错误处理程序)的地址;
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler--->分配一个字(4字节)的内存,内存中存储SVC_Handler(Supervisor Call管理程序调用处理程序)的地址;
DCD DebugMon_Handler ; Debug Monitor Handler--->分配一个字(4字节)的内存,内存中存储DebugMon_Handler(Debug Monitor调试监视器处理程序)的地址;
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler--->分配一个字(4字节)的内存,内存中存储PendSV_Handler(Pendable Service Call,可挂起系统服务处理程序)的地址;
DCD SysTick_Handler ; SysTick Handler--->分配一个字(4字节)的内存,内存中存储SysTick_Handler(系统定时器处理程序)的地址;
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect--->分配一个字(4字节)的内存,内存中存储PVD_IRQHandler(电源电压检测中断请求处理程序)的地址;
DCD TAMPER_IRQHandler ; Tamper--->分配一个字(4字节)的内存,内存中存储TAMPER_IRQHandler(入侵中断请求处理程序)的地址;
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC--->分配一个字(4字节)的内存,内存中存储RCC_IRQHandler(复位时钟控制中断请求处理程序)的地址;
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 & ADC2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD TIM1_BRK_IRQHandler ; TIM1 Break
DCD TIM1_UP_IRQHandler ; TIM1 Update
DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation
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 ; EXTI Line 15..10
DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line
DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
DCD TIM8_BRK_IRQHandler ; TIM8 Break
DCD TIM8_UP_IRQHandler ; TIM8 Update
DCD TIM8_TRG_COM_IRQHandler ; TIM8 Trigger and Commutation
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD ADC3_IRQHandler ; ADC3
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_IRQHandler ; TIM6
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1
DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2
DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3
DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5
__Vectors_End ;标签__Vectors_End指向向量表的末尾地址
__Vectors_Size EQU __Vectors_End - __Vectors ;标签__Vectors_Size表示向量表的大小,末尾地址-初始地址
复位程序
lable PROC…ENDP:函数的定义。
WEAK:弱定义。如果外部文件中有定义则使用外部定义,如果没有则使用该定义。
IMPORT:表示该定义来自外部文件,类似C语言中的extern.
LDR:从内存加载数据到寄存器;
BLX:内核指令,跳转到目标地址执行代码,并把下一条指令的地址保存到链接寄存器LR中,这样目标地址代码执行结束后还可以返回原始地址并继续执行;
BX:内核指令, 只是简单地将程序计数器(PC)设置为目标地址,不会保存任何返回地址。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
中断处理程序定义
启动文件中已经写好了所有的中断处理程序,但这些处理程序中都是空的,真正的中断处理程序需要我们在外部的C文件中重新实现。
如果我们在使用某个外设的时候,开启了某个中断,但没编写配套的中断处理程序或中断处理程序的名字写错。那么当中断来临的时候,程序就会跳转到启动文件预先写好的中断处理程序中,并在这个空程序中无限循环。
B . 指令会让程序不断跳转回当前指令,形成一个无法退出的循环。
ALIGN:伪指令确保接下来的数据或代码对齐。正确的内存对齐可以提高访问速度,尤其是在处理多字节数据(如32位整数、浮点数等)时,未对齐的访问可能会导致性能下降甚至硬件异常。后面会跟一个参数,参数必须是2的幂次方(如2、4、8、16等),表示对齐的字节数。如果没有跟参数,则默认4字节对齐
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 TAMPER_IRQHandler [WEAK]
EXPORT RTC_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 DMA1_Channel7_IRQHandler [WEAK]
EXPORT ADC1_2_IRQHandler [WEAK]
EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK]
EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_IRQHandler [WEAK]
EXPORT TIM1_UP_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_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 RTCAlarm_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 ADC3_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_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Channel1_IRQHandler [WEAK]
EXPORT DMA2_Channel2_IRQHandler [WEAK]
EXPORT DMA2_Channel3_IRQHandler [WEAK]
EXPORT DMA2_Channel4_5_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_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
DMA1_Channel7_IRQHandler
ADC1_2_IRQHandler
USB_HP_CAN1_TX_IRQHandler
USB_LP_CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_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
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
TIM8_BRK_IRQHandler
TIM8_UP_IRQHandler
TIM8_TRG_COM_IRQHandler
TIM8_CC_IRQHandler
ADC3_IRQHandler
FSMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_IRQHandler
TIM7_IRQHandler
DMA2_Channel1_IRQHandler
DMA2_Channel2_IRQHandler
DMA2_Channel3_IRQHandler
DMA2_Channel4_5_IRQHandler
B .
ENDP
ALIGN
用户堆栈初始化
IF,ELSE,ENDIF:汇编的条件分支语句,与C语言的if, else类似;
END:文件结束;
**该部分汇编程序首选判断了是否定义了宏__MIRCOLIB,如果定义了这个宏,则把标签__init_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局化,可供外部文件调用,之后堆栈的初始化就由C库函数_main来完成。我们一般使用这种方式。 **__MICROLIB这个宏我们在Keil的魔术棒中可以配置,具体配置方法如下图:
如果没有定义 __MICROLIB,则插入标号 __use_two_region_memory,这个函数需要用户自己实现,具体要如何实现,可在 KEIL 的帮助文档里面查询到,具体见图 use_two_region_memory函数。
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
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_61470035/article/details/144255912
|