STM32启动代码分析.docx
《STM32启动代码分析.docx》由会员分享,可在线阅读,更多相关《STM32启动代码分析.docx(10页珍藏版)》请在冰豆网上搜索。
STM32启动代码分析
STM32启动代码概述
一般嵌入式开发流程就是先建立一个工程,再编写源文件,然后进行编译,把所有的*.s文件和*.c文件编译成一个*.o文件,再对目标文件进行链接和定位,编译成功后会生成一个*.hex文件和调试文件,接下来要进行调试,如果成功的话,就可以将它固化到flash里面去。
启动代码是用来初始化电路以及用来为高级语言写的软件作好运行前准备的一小段汇编语言,是任何处理器上电复位时的程序运行入口点。
比如,刚上电的过程中,PC机会对系统的一个运行频率进行锁定在一个固定的值,这个设计频率的过程就是在汇编源代码中进行的,也就是在启动代码中进行的。
与此同时,设置完后,程序开始运行,注意,程序是在内存中运行的。
这个时候,就需要把一些源文件从flash里面copy到内存中,又要对它们进行初始化读写,这又有频率的设置。
这些都是初始化。
初始化完成后,我们又要设置一些堆栈,要跳到C语言的main函数里面运行。
这就需要堆栈。
对普通的ARMCPU有这样一个要求:
在绝对地址为零的地方要放置一个异常向量表,但并不是所有的ARMCPU都留有这个一个空间,这就需要用到映射的功能。
我们可以将其它地方的一些空间映射到绝对地址里面。
当发生异常时,ARM核来读取异常中断表的时候,它会使用映射之后的那个表,这个就可以接着往下执行,否则在绝对地址零的地方找不到任何信息,程序就会死掉。
这些运行的环境全部建立好后,程序就会跳转到我们的main函数里面。
总之,启动代码,就是对最小系统的初始化。
包括晶振,CPU频率等。
启动代码的最小系统是:
异常向量表的初始化–存储区分配–初始化堆栈–高级语言入口函数调用–main()函数。
程序的启动过程:
以下面这个例子为例,编译完后,DEBUG后,我们可以看到,光标指向绝对地址为零的地方,这里存放的就是一个异常向量表。
它对应在startup.s里的源文件如下:
单步运行后,马上跳转到初始化CPU的频率。
即初始化锁相环,将其锁在一个固定的频率。
具体代码如下:
;SetupPLL
IF PLL_SETUP<>0
LDR R0,=PLL_BASE
MOV R1,#0xAA
MOV R2,#0x55
; ConfigureandEnablePLL
MOV R3,#PLLCFG_Val
STR R3,[R0,#PLLCFG_OFS]
MOV R3,#PLLCON_PLLE
STR R3,[R0,#PLLCON_OFS]
STR R1,[R0,#PLLFEED_OFS]
STR R2,[R0,#PLLFEED_OFS]
; WaituntilPLLLocked
PLL_Loop LDR R3,[R0,#PLLSTAT_OFS]
ANDS R3,R3,#PLLSTAT_PLOCK
BEQ PLL_Loop
; SwitchtoPLLClock
MOV R3,#(PLLCON_PLLE:
OR:
PLLCON_PLLC)
STR R3,[R0,#PLLCON_OFS]
STR R1,[R0,#PLLFEED_OFS]
STR R2,[R0,#PLLFEED_OFS]
ENDIF ;PLL_SETUP
然后再初始化每一种模式的堆栈,再进行单步运行的时候,下面我们可以看到,它自动跳转到main()函数:
;EntertheCcode
IMPORT __main
LDR R0,=__main
BX R0
IF :
DEF:
__MICROLIB
EXPORT __heap_base
EXPORT __heap_limit
ELSE
这个时候,程序会运行各种scatterload函数,将我们的堆栈、全局变量等内容拷贝到内存中去。
拷贝完后,就正式跳转到我们的main()函数中来执行了。
这就是启动代码执行的全过程,呵呵,平时我们看到以为只是执行main()函数就行了,是不是没有想到在执行main()函数后还有这么多学问呢?
STM32启动代码分析[转]
启动代码文件名是STM32F10X.S,它的作用先总结下,然后再分析。
启动代码作用一般是:
1)堆和栈的初始化;2)向量表定义;3)地址重映射及中断向量表的转移;4)设置系统时钟频率;5)中断寄存器的初始化;6)进入C应用程序。
(1)按启动代码的次序,先看堆和栈的初始化:
Stack_Size EQU 0x00000200 ;定义Stack_Size为0x00000200
AREA STACK,NOINIT,READWRITE,ALIGN=3 ;定义栈,可初始为0,8字节对齐
Stack_Mem SPACE Stack_Size ;分配0x200个连续字节,并初始化为0
__initial_sp ;汇编代码地址标号
Heap_Size EQU 0x00000000
AREA HEAP,NOINIT,READWRITE,ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8 ;指定当前文件堆栈8字节对齐
THUMB ;告诉汇编器下面是32为的Thumb指令,如果需要汇编器将插入位以保证对齐
(2)中断向量表定义
AREA RESET,DATA,READONLY;定义复位向量段,只读
EXPORT __Vectors ;定义一个可以在其他文件中使用的全局标号。
此处表示中断地址
__Vectors DCD __initial_sp ; 给__initial_sp分配4字节32位的地址0x0
DCD Reset_Handler ;给标号ResetHandler分配地址为0x00000004
DCD NMI_Handler ;给标号NMIHandler分配地址0x00000008
DCD HardFault_Handler ;HardFaultHandler
DCD MemManage_Handler ;MPUFaultHandler
DCD BusFault_Handler ;BusFaultHandler
DCD UsageFault_Handler ;UsageFaultHandler
DCD 0 ; 这种形式就是保留地址,不给任何标号分配
DCD 0 ;Reserved
DCD 0 ;Reserved
DCD 0 ;Reserved
DCD SVC_Handler ;SVCallHandler
DCD DebugMon_Handler ;DebugMonitorHandler
DCD 0 ;Reserved
DCD PendSV_Handler ;PendSVHandler
DCD SysTick_Handler ;SysTickHandler
;ExternalInterrupts
DCD WWDG_IRQHandler ;WindowWatchdog
DCD PVD_IRQHandler ;PVDthroughEXTILinedetect
DCD TAMPER_IRQHandler ;Tamper
DCD RTC_IRQHandler ;RTC
DCD FLASH_IRQHandler ;Flash
DCD RCC_IRQHandler ;RCC
DCD EXTI0_IRQHandler ;EXTILine0
DCD EXTI1_IRQHandler ;EXTILine1
DCD EXTI2_IRQHandler ;EXTILine2
DCD EXTI3_IRQHandler ;EXTILine3
DCD EXTI4_IRQHandler ;EXTILine4
DCD DMAChannel1_IRQHandler ;DMAChannel1
DCD DMAChannel2_IRQHandler ;DMAChannel2
DCD DMAChannel3_IRQHandler ;DMAChannel3
DCD DMAChannel4_IRQHandler ;DMAChannel4
DCD DMAChannel5_IRQHandler ;DMAChannel5
DCD DMAChannel6_IRQHandler ;DMAChannel6
DCD DMAChannel7_IRQHandler ;DMAChannel7
DCD ADC_IRQHandler ;ADC
DCD USB_HP_CAN_TX_IRQHandler ;USBHighPriorityorCANTX
DCD USB_LP_CAN_RX0_IRQHandler;USBLow PriorityorCANRX0
DCD CAN_RX1_IRQHandler ;CANRX1
DCD CAN_SCE_IRQHandler ;CANSCE
DCD EXTI9_5_IRQHandler ;EXTILine9..5
DCD TIM1_BRK_IRQHandler ;TIM1Break
DCD TIM1_UP_IRQHandler ;TIM1Update
DCD TIM1_TRG_COM_IRQHandler ;TIM1TriggerandCommutation
DCD TIM1_CC_IRQHandler ;TIM1CaptureCompare
DCD TIM2_IRQHandler ;TIM2
DCD TIM3_IRQHandler ;TIM3
DCD TIM4_IRQHandler ;TIM4
DCD I2C1_EV_IRQHandler ;I2C1Event
DCD I2C1_ER_IRQHandler ;I2C1Error
DCD I2C2_EV_IRQHandler ;I2C2Event
DCD I2C2_ER_IRQHandler ;I2C2Error
DCD SPI1_IRQHandler ;SPI1
DCD SPI2_IRQHandler ;SPI2
DCD USART1_IRQHandler ;USART1
DCD USART2_IRQHandler ;USART2
DCD USART3_IRQHandler ;USART3
DCD EXTI15_10_IRQHandler ;EXTILine15..10
DCD RTCAlarm_IRQHandler ;RTCAlarmthroughEXTILine
DCD USBWakeUp_IRQHandler ;USBWakeupfromsuspend
(3)中断向量表的转移
AREA |.text|,CODE,READONLY;代码段定义
;ResetHandler
Reset_Handler PROC ;标记一个函数的开始
EXPORT Reset_Handler [WEAK];[WEAK]选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为0,若该标号为B或BL指令引用,则将B或BL指令置为NOP操作。
EXPORT提示编译器该标号可以为外部文件引用。
IMPORT __main;通知编译器要使用的标号在其他文件
LDR R0,=__main;使用“=”表示LDR目前是伪指令不是标准指令。
这里是把__main的地址给RO。
BX R0;BX是ARM指令集和THUMB指令集之间程序的跳转
ENDP
;DummyExceptionHandlers(infiniteloopswhichcanbemodified)
NMI_Handler m ;"m"其实就是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_HandlerPROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_HandlerPROC
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 DMAChannel1_IRQHandler [WEAK]
EXPORT DMAChannel2_IRQHandler [WEAK]
EXPORT DMAChannel3_IRQHandler [WEAK]
EXPORT DMAChannel4_IRQHandler [WEAK]
EXPORT DMAChannel5_IRQHandler [WEAK]
EXPORT DMAChannel6_IRQHandler [WEAK]
EXPORT DMAChannel7_IRQHandler [WEAK]
EXPORT ADC_IRQHan