2012/9/2
1. 在分析启动代码之前,先把STM32的存储结构说一下:
上面这张图来自CORTEX M3权威指南
STM32F103RTB6 有20k的SRAM,起始地址为0x20000000,128k的闪存flash,起始地址为0x08000000(处于code区域).
从主闪存存储器启动时,主闪存存储器被映射到启动空间(0x00000000),但仍然能够在它原有的地址(0x08000000)访问它,即闪存存储器的内容可以在两个地址区域访问,0x00000000或0x0800 0000。
通过boot0和boot1的配置可以选着三种启动方式:
当我们下载程序时,选着的是系统存储器启动,当我们一上电开始跑程序的时候,应选择闪存flash启动.
1. 再明确一下一些代码对齐的规则:
1.1 字对齐: 4字节对齐,ARM指令时32位一跳的; 半字对齐: 2字节对齐.thumb指令是16位一跳的。而CORTEX-M3 是thumb-2指令集,16位32位都有
1.2 注意align的不同:
AREA STACK, NOINIT, READWRITE, ALIGN=3
此处的align表示接下来的堆栈段是2^3=8字节对齐的
而
单纯.align或ALIGN表示字对齐(32位)
3.我想我们还要明确比较重要的一点,既然我们的程序是从flash闪存上开始执行的,在STM32中文手册上由这么一句话:,CPU从地址0x0000 0000获取堆栈顶的地址,并从启动存储器的0x00000004指示的地址开始执行代码。而0x00000004开始的地址放的是什么呢?我可以告诉你从该处开始放的是cortex-m3规定的异常向量表。而且我们还可以确定,0x0000 0004出放的必然是复位异常向量。
4.下面好好来分析启动代码,我从keil得到的文件是STM32F10X.s,跟着我的注释走吧
//栈大小
Stack_Size EQU 0x00000200
//定义堆栈段,8字节对齐,注意,是可读写!!而我们的闪存flash是要擦除后才能写的,这就有问题了,说明我们的堆栈段不可能是安排在Flash闪存上的,那安排在哪呢?当然是内部20k的SRAM上啦!
AREA STACK, NOINIT, READWRITE, ALIGN=3
//stack_Mem为堆栈的底部,SPACE表示存储Stack_size个 分配单元
Stack_Mem SPACE Stack_Size
//__initial_sp很显然表示的是栈顶地址
__initial_sp
//下面是在SRAM上分配堆空间,大小为0
Heap_Size EQU 0x00000000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
//__heap_base和Heap_Mem为堆起始地址
__heap_base
Heap_Mem SPACE Heap_Size
//__heap_limit为堆结束的地址
__heap_limit
//8字节对齐,在keil中必须要这一条
PRESERVE8
//使用的是thumb指令集,但是cortex-m3是thumb-2版本,16位和32位的指令都有
THUMB
//只读代码段
AREA RESET, DATA, READONLY
//导出__Vectors中断向量表起始地址,相当于在此声明__Vectors为一个全局变量,别的文件时可以访问到的
EXPORT __Vectors
//前面说过0x0地址必须放堆栈顶地址
__Vectors DCD __initial_sp ; Top ofStack
//接下来是一些异常向量表,当发生该异常时,PC将会跳到这里执行
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 detect
DCD TAMPER_IRQHandler ; Tamper
DCD RTC_IRQHandler ; RTC
DCD FLASH_IRQHandler ; Flash
DCD RCC_IRQHandler ; RCC
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 DMAChannel1_IRQHandler ; DMA Channel 1
DCD DMAChannel2_IRQHandler ; DMA Channel 2
DCD DMAChannel3_IRQHandler ; DMA Channel 3
DCD DMAChannel4_IRQHandler ; DMA Channel 4
DCD DMAChannel5_IRQHandler ; DMA Channel 5
DCD DMAChannel6_IRQHandler ; DMA Channel 6
DCD DMAChannel7_IRQHandler ; DMA Channel 7
DCD ADC_IRQHandler ; ADC
DCD USB_HP_CAN_TX_IRQHandler ; USB High Priority or CAN TX
DCD USB_LP_CAN_RX0_IRQHandler ; USB Low Priority or CAN RX0
DCD CAN_RX1_IRQHandler ; CAN RX1
DCD CAN_SCE_IRQHandler ; CAN 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
//声明代码段|.text|是约定的代码段名称
AREA |.text|, CODE,READONLY
//中断异常向量的函数指针
Reset_Handler PROC
//到处该符号,[WEAK]表示如果外部有同名符号,外部该同名符号是优先使用的
EXPORT Reset_Handler [WEAK]
//__main标号起始不是我们c语言中的main函数,而是C/C++标准实时库函数里的一个初始化子程序__main的入口地址,该程序的一个主要作用是初始化堆栈(对于我们的程序来说是跳转到后面的_user_initial_stackheap标号出进行初始化堆栈),并初始化映象文件,最后跳转到C程序的main函数。我们就理解为__main最后就跳到了main吧
IMPORT __main
LDR R0, =__main
BX R0
ENDP
//下面就是其他向量的函数地址了,这里只是简单的死循环,不做任何处理,然而很有意思的是,他们都EXPORT…[WEAK]了,所以,当我们要设置该异常的中断向量函数时,以我们设置的同名标号为准,这就是我们经常在keil中写的各个外部中断的handler了,然而,当没有设置这个异常的中断函数就引发了该中断,程序就会死在中断函数入口了。
; Dummy Exception Handlers (infinite loopswhich 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 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_IRQHandler [WEAK]
EXPORT USB_HP_CAN_TX_IRQHandler [WEAK]
EXPORT USB_LP_CAN_RX0_IRQHandler [WEAK]
EXPORT CAN_RX1_IRQHandler [WEAK]
EXPORT CAN_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]
WWDG_IRQHandler
PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMAChannel1_IRQHandler
DMAChannel2_IRQHandler
DMAChannel3_IRQHandler
DMAChannel4_IRQHandler
DMAChannel5_IRQHandler
DMAChannel6_IRQHandler
DMAChannel7_IRQHandler
ADC_IRQHandler
USB_HP_CAN_TX_IRQHandler
USB_LP_CAN_RX0_IRQHandler
CAN_RX1_IRQHandler
CAN_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
B .
ENDP
// 字对齐(32位)
ALIGN
; User Initial Stack & Heap
//这里我们没有定义__MICROLIB,走下面分支
IF :DEF:__MICROLIB
//导出栈顶,堆头和堆尾地址
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
//一个小函数,我们前面提到在__main中会调用他来初始化堆栈
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
//调回__mian中调用__user_initial_stackheap的地方去
BX LR
ALIGN
ENDIF
//汇编结束
END