在上一篇文章《假美猴王之三极管与MOS管,谁是悟空,谁又是六耳猕猴》中我们讲到了真假美猴王之三极管与MOS管的区别。今天来探讨一下武功秘籍的修炼,据说秘籍第一页就写道:欲练此功,必先自宫。意思就是说明一个道理,任何人要想成仙成佛,练就一身超神的本领,那么是要付出的代价并做好准备工作的。

而今天我们的要探讨的主角STM32处理器也不例外,别看它运行起来玉树临风,似乎无所不能,启动初期也要先苦练一番本领才行。那么这一部分都做了哪些工作呢?

STM32这款处理器使用的是的arm的Cortex-M3内核,Cortex-M3内核有个特点就是在地址0x00000000存放的是全局的栈地址。紧接着从地址0x00000004执行(因为STM32是32位的处理器,一条指令正好对应4个字节),也就是从这个地址取出中断向量表的地址,跳转执行中断处理函数。

stm32 如何降低功耗(欲练此功必先自宫之STM32汇编启动)(1)

M3内核复位后程序执行过程

在前面文章《敢问路在何方,STM32迈出的第一步,却注定了它非凡的一生》中我们有介绍通过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中编写的代码。

喜欢就在文末点个“关注”或者 “转发”,这样可以让更多的人学习哦!!!

有问题评论一起交流学习进步! 感谢!

更多优质文章请点击我的头像,选择“文章”标签!

,