分享

STM32单片机Bootloader设计(上)

 乐创客搬运工 2021-07-02
文 / Edward


   STM32的启动文件

STM32作为一款单片机,它的启动方式很简单,即当Boot配置了从内部Flash启动模式之后,一上电程序就会从0x8000000地址处开始执行文件,因此我们在使用Keil设置程序起始地址的时候,需要将这个Flash地址设置成0x8000000,只有将这个地址设置成0x8000000,生成的hex文件才可以被正常烧录到此地址,单片机上电之后才可以正常启动。而如果使用J-Flash工具烧写Hex文件时,这个地址会自动根据Hex文件解析出来。然而如果当你烧写二进制Bin文件时,还需要手动将单片机的起始地址制定出来,关于Hex文件和Bin文件的异同点,这个又是可以长篇大论一番了,我们下次特别写文章来讲。

图1 Keil设置起始地址和空间

STM32启动文件

;********************* (C) COPYRIGHT 2017 STMicroelectronics ********************;* File Name : startup_stm32l151xb.s;* Author : MCD Application Team;* Description : STM32L151XB Devices vector for MDK-ARM toolchain.;* This module performs:;* - Set the initial SP;* - Set the initial PC == Reset_Handler;* - Set the vector table entries with the exceptions ISR ;* address.;* - Configure the system clock;* - Branches to __main in the C library (which eventually;* calls main()).;* After Reset the Cortex-M3 processor is in Thread mode,;* priority is Privileged, and the Stack is set to Main.;********************************************************************************;*;* Copyright (c) 2017 STMicroelectronics. All rights reserved.;*;* This software component is licensed by ST under BSD 3-Clause license,;* the "License"; You may not use this file except in compliance with the;* License. You may obtain a copy of the License at:;* opensource.org/licenses/BSD-3-Clause;*;*******************************************************************************;* <<< Use Configuration Wizard in Context Menu >>>;; Amount of memory (in bytes) allocated for Stack; Tailor this value to your application needs; <h> Stack Configuration; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>; </h>
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3Stack_Mem SPACE Stack_Size__initial_sp

; <h> Heap Configuration; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>; </h>
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3__heap_baseHeap_Mem SPACE Heap_Size__heap_limit
PRESERVE8 THUMB

; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors EXPORT __Vectors_End EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack 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_STAMP_IRQHandler ; Tamper and Time Stamp DCD RTC_WKUP_IRQHandler ; RTC Wakeup 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 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_IRQHandler ; ADC1 DCD USB_HP_IRQHandler ; USB High Priority DCD USB_LP_IRQHandler ; USB Low Priority DCD DAC_IRQHandler ; DAC DCD COMP_IRQHandler ; COMP through EXTI Line DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 DCD 0 ; Reserved DCD TIM9_IRQHandler ; TIM9 DCD TIM10_IRQHandler ; TIM10 DCD TIM11_IRQHandler ; TIM11 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 RTC_Alarm_IRQHandler ; RTC Alarm through EXTI Line DCD USB_FS_WKUP_IRQHandler ; USB FS Wakeup from suspend DCD TIM6_IRQHandler ; TIM6 DCD TIM7_IRQHandler ; TIM7 __Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset handler routineReset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDPHardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] B . ENDPMemManage_Handler\ PROC EXPORT MemManage_Handler [WEAK] B . ENDPBusFault_Handler\ PROC EXPORT BusFault_Handler [WEAK] B . ENDPUsageFault_Handler\ PROC EXPORT UsageFault_Handler [WEAK] B . ENDPSVC_Handler PROC EXPORT SVC_Handler [WEAK] B . ENDPDebugMon_Handler\ PROC EXPORT DebugMon_Handler [WEAK] B . ENDPPendSV_Handler PROC EXPORT PendSV_Handler [WEAK] B . ENDPSysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK] EXPORT PVD_IRQHandler [WEAK] EXPORT TAMPER_STAMP_IRQHandler [WEAK] EXPORT RTC_WKUP_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_IRQHandler [WEAK] EXPORT USB_HP_IRQHandler [WEAK] EXPORT USB_LP_IRQHandler [WEAK] EXPORT DAC_IRQHandler [WEAK] EXPORT COMP_IRQHandler [WEAK] EXPORT EXTI9_5_IRQHandler [WEAK] EXPORT TIM9_IRQHandler [WEAK] EXPORT TIM10_IRQHandler [WEAK] EXPORT TIM11_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 RTC_Alarm_IRQHandler [WEAK] EXPORT USB_FS_WKUP_IRQHandler [WEAK] EXPORT TIM6_IRQHandler [WEAK] EXPORT TIM7_IRQHandler [WEAK]
WWDG_IRQHandlerPVD_IRQHandlerTAMPER_STAMP_IRQHandlerRTC_WKUP_IRQHandlerFLASH_IRQHandlerRCC_IRQHandlerEXTI0_IRQHandlerEXTI1_IRQHandlerEXTI2_IRQHandlerEXTI3_IRQHandlerEXTI4_IRQHandlerDMA1_Channel1_IRQHandlerDMA1_Channel2_IRQHandlerDMA1_Channel3_IRQHandlerDMA1_Channel4_IRQHandlerDMA1_Channel5_IRQHandlerDMA1_Channel6_IRQHandlerDMA1_Channel7_IRQHandlerADC1_IRQHandlerUSB_HP_IRQHandlerUSB_LP_IRQHandlerDAC_IRQHandlerCOMP_IRQHandlerEXTI9_5_IRQHandlerTIM9_IRQHandlerTIM10_IRQHandlerTIM11_IRQHandlerTIM2_IRQHandlerTIM3_IRQHandlerTIM4_IRQHandlerI2C1_EV_IRQHandlerI2C1_ER_IRQHandlerI2C2_EV_IRQHandlerI2C2_ER_IRQHandlerSPI1_IRQHandlerSPI2_IRQHandlerUSART1_IRQHandlerUSART2_IRQHandlerUSART3_IRQHandlerEXTI15_10_IRQHandlerRTC_Alarm_IRQHandlerUSB_FS_WKUP_IRQHandlerTIM6_IRQHandlerTIM7_IRQHandler
B .
ENDP
ALIGN
;*******************************************************************************; User Stack and Heap initialization;******************************************************************************* 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
;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****

首先让我们来看下STM32启动文件,当MCU上电复位之后,整个程序会跳转到以0x8000000为基址,偏移0的地址处,即还是0x8000000。但是STM32的0x8000000地址处存放的并不是整个芯片的第一句指令,而是整个芯片的堆栈初始化程序,如图2所示。

图2 0x8000000偏移0地址处的堆栈初始化程序指针

由于STM32的地址空间都是4字节对齐的,因此这个栈顶指针的存放空间为4字节,所以STM32复位之后跳转的地址应该是0x8000000基址偏移4个字节,即0x8000004。如同3所示。

图3 STM32复位跳转地址

图3中的程序非常浅显易懂,第136和137行,即将程序跳转到SystemInit处,这是个C语言函数,定义在“system_stm32l1xx.c”文件里,它的目的就是对中断向量表起始地址进行指定,也就是图2中的“__Vector”处。当然CM3内核和CM0内核关于SCB(系统控制块)的定义有些许差别,CM0不在本文讨论中,但是CM3和CM4的中断向量表映射机制还是很相似的。

图4 SystemInit函数映射中断向量表

图4中我们可以看到,SCB中关于Vector的地址是通过符号FLASH_BASE和VECT_TAB_OFFSET计算出来的,我们可以找到关于它们的定义,如图5所示。

图5 FLASH_BASE和VECT_TAB_OFFSET的定义

通过图5中的计算,正好可以得出整个中断向量表被映射到了0x8000000地址处。

    STM32的FLASH分配

前面的大段文章内容中,频繁提及了一个关键的数值,即0x8000000,那么这个0x8000000到底是怎么来的呢?这个数值并不是平白无故拍脑袋想出来的。之前我们就说过,ARM体系的存储器结构是其一大特色,而这个0x8000000正是整个STM32内置FLASH的起始地址。我们随便打开一份STM32的数据手册,在存储器章节里面就可以看到STM32全部的存储器定义。如图6所示。

图6 STM32内部FLASH的起始地址

   STM32的Bootloader思路

抛开所有的Bootloader高级功能来说,我们设计STM32 Bootloader的主要目的有两个,第一个为方便程序烧写和更新,第二个目前是从Bootloader程序中跳转(引导)用户的应用程序。这两个目的中,对于Bootloader来说程序跳转尤其重要,因为程序跳转成不成功将会严重影响整个用户程序的运行状态。因而,怎么跳,何时跳,跳到哪里,则是下篇文章的着重讨论部分。

前面一个FLASH烧写,可以根据自己的特殊要求来定制,只要严格安装HEX文件指定的地址和数据的关系,一般不会出错。

本文分析了STM32启动时比较重要的一些定义和函数跳转,下篇将会开始着手设计一个STM32 Bootloader。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多