今天来探讨一下武功秘籍的修炼,据说秘籍第一页就写道:欲练此功,必先自宫。意思就是说明一个道理,任何人要想成仙成佛,练就一身超神的本领,那么是要付出的代价并做好准备工作的。
而今天我们的要探讨的主角STM32处理器也不例外,别看它运行起来玉树临风,似乎无所不能,启动初期也要先苦练一番本领才行。那么这一部分都做了哪些工作呢?
STM32这款处理器使用的是的ARM的Cortex-M3内核,Cortex-M3内核有个特点就是在地址0x00000000存放的是全局的栈地址。紧接着从地址0x00000004执行(因为STM32是32位的处理器,一条指令正好对应4个字节),也就是从这个地址取出中断向量表的地址,跳转执行中断处理函数。
在前面文章中我们有介绍通过BOOT0和BOOT1引脚高低电平的不同组合,对应者三种不同的启动方式,同样也就对应着三种地址的映射: Flash system memory就是将0x00000000映射到了0x08000000;System memory就是将0x00000000映射到了0x1FFF0000;SRAM就是将0x00000000映射到了0x20000000。选择了哪种启动方式就会把0地址映射到对应物理地址。同样该区的物理地址最前面存放的就是栈地址和中断向量表。
DATA
__vector_table
DCD sfe(CSTACK)
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
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 RTC_IRQHandler ; RTC through EXTI Line
DCD FLASH_IRQHandler ; FLASH
DCD RCC_CRS_IRQHandler ; RCC_CRS
DCD EXTI0_1_IRQHandler ; EXTI Line 0 and 1
DCD EXTI2_3_IRQHandler ; EXTI Line 2 and 3
DCD EXTI4_15_IRQHandler ; EXTI Line 4 to 15
DCD TSC_IRQHandler ; TSC
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_3_IRQHandler ; DMA1 Channel 2 and Channel 3
DCD DMA1_Channel4_5_6_7_IRQHandler ; DMA1 Channel 4, Channel 5, Channel 6 and Channel 7
DCD ADC1_COMP_IRQHandler ; ADC1, COMP1 and COMP2
DCD LPTIM1_IRQHandler ; LPTIM1
DCD 0 ; Reserved
DCD TIM2_IRQHandler ; TIM2
DCD 0 ; Reserved
DCD TIM6_DAC_IRQHandler ; TIM6 and DAC
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD TIM21_IRQHandler ; TIM21
DCD 0 ; Reserved
DCD TIM22_IRQHandler ; TIM22
DCD I2C1_IRQHandler ; I2C1
DCD I2C2_IRQHandler ; I2C2
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD RNG_LPUART1_IRQHandler ; RNG and LPUART1
DCD LCD_IRQHandler ; LCD
DCD USB_IRQHandler ; USB
而中断处理函数的第一个就是复位中断处理函数Reset_Handler:
THUMB
PUBWEAK Reset_Handler
SECTION .text:CODE:NOROOT:REORDER(2)
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0
PUBWEAK NMI_Handler
SECTION .text:CODE:NOROOT:REORDER(1)
我们看下默认的复位中断处理函数里边都做了什么事情,首先将SystemInit函数的首地址存放到R0寄存器里边,然后跳转到R0寄存器的地址,即去执行IAR本身库文件中的SystemInit函数进行相关初始化。然后将__iar_program_start函数的首地址存放到R0寄存器里边,然后跳转到R0寄存器的地址,即去执行__iar_program_start函数。
那么__iar_program_start这个函数在哪里呢?没错,你在整个汇编文件中都没有找到!这个文件在IAR的安装目录里边,具体的路径是C:\Program Files (x86)\IAR Systems\Embedded Workbench 7.2\arm\src\lib\thumb,我是将IAR装在C:\Program Files (x86)目录下的,大家根据自己情况查找。在thumb目录下有个cstartup_M.s文件,文件代码如下:
MODULE ?cstartup
PUBLIC __iar_program_start
EXTERN __cmain
EXTERN __vector_table
EXTWEAK __iar_init_core
EXTWEAK __iar_init_vfp
SECTION .text:CODE:REORDER(1)
THUMB
__iar_program_start:
FUNCALL __iar_program_start, __iar_init_core
BL __iar_init_core
FUNCALL __iar_program_start, __iar_init_vfp
BL __iar_init_vfp
FUNCALL __iar_program_start, __cmain
BL __cmain
REQUIRE __vector_table
END
__iar_program_start中分别调用了__iar_init_core,__iar_init_vfp和__cmain三个函数。前面两个函数式弱函数,在工程中没有定义,大家可以根据实际情况重写这两个函数。最后一个函数__cmain也在同目录下的cmain.s中定义:
THUMB
__cmain:
?main:
; Initialize segments.
; __segment_init and __low_level_init are assumed to use the same
; instruction set and to be reachable by BL from the ICODE segment
; (it is safest to link them in segment ICODE).
FUNCALL __cmain, __low_level_init
bl __low_level_init
cmp r0,#0
beq ?l1
FUNCALL __cmain, __iar_data_init3
bl __iar_data_init3
?l1:
REQUIRE ?l3
SECTION .text:CODE:NOROOT(2)
PUBLIC _main
PUBLIC _call_main
THUMB
__iar_init$done: ; Copy initialization is done
?l3:
_call_main:
MOVS r0,#0 ; No parameters
FUNCALL __cmain, __iar_argc_argv
BL __iar_argc_argv ; Maybe setup command line
FUNCALL __cmain, main
BL main
_main:
FUNCALL __cmain, exit
BL exit
END
其实最后就是进行了一个底层的初始化,然后就永久的跳转到main.c文件中的main函数了。至此,汇编部分代码结束,开始执行大家在main中编写的代码。