硬件:stm32f103cbt6
软件:STM32F10x_StdPeriph_Lib_V3.5.0


1 预备知识

基于标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)的IAP升级相关资料可以参考 IAP ST官方资料汇总

STM32升级的三种方式:IAP,ICP,ISP;具体有什么区别可以自行Google

本文需要实现STM32的Bootloader(后面Bootloader/IAP不加以区分),文件传输基于ymodem协议通过串口进行传输,这里参考了ST官方的DEMO —— STM32F10xxx in-application programming using the USART AN2557,在此基础上做了部分修改,增加了延时启动的功能,最终可以实现想要的效果。

在这里插入图片描述
整体架构分为两个部分;BootloaderApplication,具体如下图所示;

在这里插入图片描述

由上图可知,STM32内置的Flash被分成了两个部分,分别用来保存BootloaderApplication程序,这里有两个有两个FLASH起始地址0x80000000x8003000

为什么是0x8000000这个地址呢?而不是其他地址呢?
这是由M3内核硬件上的设计就已经这么做了,人为设计好了,可以参考M3内核权威指南;

0x8003000这个地址则是由我们自己来规定的,这个地址的范围必须在0x80000000x8020000之间,所以一般根据Bootloader程序的最终大小,在这范围之间取一个比较合理的值即可。如下图所示;

在这里插入图片描述

注意:本文使用的stm32f103cb,属于中等大小Flash,128K = 0x20000,所以地址范围是0x8000000~0x8020000;

2 Bootloader

2.1 启动流程

这里的Bootloader即为IAP程序,它具备以下几个功能;

  • 支持文件传输;本文基于ymodem协议通过串口通讯接收或发送的bin文件;当然也可以通过I2CSPIUSBWiFi蓝牙等等进行文件传输;
  • 对内置Flash进行读写,擦除和编程;
  • 启动Application程序;

前面分析STM32启动文件的时候,我们可以知道,正常一个系统的启动流程,可以参考《STM32 标准库V3.5启动文件startup_stm32f10xxx.s分析》
在这里插入图片描述

由该图可以知道程序正常启动流程;以下表格一四个向量是必须的,从图中也可以了解到;

地址异常编号
0x0800_0000-MSP 的初始值
0x0800_00041复位向量(PC 初始值)
0x0800_00082NMI 服务例程的入口地址
0x0800_000C3硬 fault 服务例程的入口地址

2.2 校验跳转地址是否有效

在主函数中可以看到如下程序;甚是不解和迷茫;沉思一会儿才恍然大悟;

    /* Test if user code is programmed starting from address "ApplicationAddress" */
   if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
   { 
      /* Jump to user application */
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      Jump_To_Application();
    }

本文中ApplicationAddress = 0x8000000
那么*(__IO uint32_t*)ApplicationAddress)则是这个地址中所保存的值,由表格一可以知道,程序起始地址的第一个向量地址保存的栈顶的,因此,地址0x800_00000x800_3000中保存的值都是指向栈顶,如下图所示;
在这里插入图片描述

栈是在RAM上分配,因此RAM的有效范围要做一个检测,栈顶地址和0x2FFE0000做与运算可以推算出,要校验的RAM范围是0x2000_0000—0x2001_FFFF,所以RAM大小是128K,官方DEMO默认使用HD高密度系列,所以是128K,本文是CBT6,20K的RAM,则需要改成0x2FFFB000

    /* Test if user code is programmed starting from address "ApplicationAddress" */
   if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFFB000 ) == 0x20000000)
   { 
      /* Jump to user application */
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      Jump_To_Application();
    }

计算方式:
20K = 20*1024= 0x5000
0x2FFF_FFFF - (0x5000 - 1) = 0x2FFF_B000

2.3 Keil工程IAP的相关设置

2.3.1 修改Flash地址

在这里插入图片描述
设置程序起始地址0x800000和大小0x3000
在这里插入图片描述

设置Debug工具烧写时Flash的起始地址0x800000和大小0x3000

在这里插入图片描述

2.3.2 使用自己的链接脚本

该项为选配,与上述配置二选一即可,如果仍然想使用自己的链接脚本,在Option-->Linker下将Use Memort Layout from Target Dialog选项勾选去掉,然后选择自己的链接脚本,如下图进行配置;
在这里插入图片描述

参考ARMCC的链接脚本编写方法,可以自己编写的srt文件,参考ARM分散加载技术;

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00003000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00003000  {  ; load address = execution address
    *.o (RESET, +First)
    *(InRoot$$Sections)
    .ANY (+RO)
    .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00005000  {  ; RW data
    .ANY (+RW +ZI)
  }
}

如果用的gcc工具链,则要编写gcc的链接脚本ld文件;

2.3.3 下载固件

配置完成之后进行Build,然后通过SWD的方式先下载固件,进行实验;

在这里插入图片描述

3 Application

3.1 启动流程

用户的Application需要在IAP启动完成后,才能正常执行;具体启动过程,比正常应用的启动多了一个IAP启动的过程,并最终通过IAP引导进入Application;具体如下图所示;
白色部分为IAP;
灰色部分为Application;

在这里插入图片描述

图中的0x8000004+N+M 就等于0x8003004,所以Application的启动地址需要进行修改,另外还有其他需要修改的地方,下面会详细指出。

3.2 IAP中的引导部分

参考IAP中的引导程序;

#define ApplicationAddress    0x8003000
      /* Jump to user application */
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      Jump_To_Application();

可以发现的是SP的值为0x8003000,指向栈顶,而0x8003004则为ResetHander的地址,系统会进行复位,然后开始Application正常启动流程;

3.3 关于 VTOR

VTOR是向量表偏移量寄存器,它将用来告诉CPU,从Flash的哪个地方去取向量地址,第一个要取的是MSP的值,然后就是复位向量地址ResetHandler,如果这里设置错误,那么程序是无法正常启动的。下面是标准库中与其相关的代码片段;

__IO uint32_t VTOR;  /*!< Offset: 0x08  Vector Table Offset Register  */

/*!< FLASH base address in the alias region */
#define FLASH_BASE            ((uint32_t)0x08000000)

/*!< Vector Table base offset field. This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET  0x0 

/* Vector Table Relocation in Internal FLASH. */
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; 

所以Application中还需要修改VECT_TAB_OFFSET的值为0x3000;

参考M3权威指南:

如果需要动态地更改向量表,则对于任何器件来说,向量表的起始处都必须包含以下向量:

  • 主堆栈指针(MSP)的初始值
  • 复位向量
  • NMI
  • 硬 fault 服务例程
    后两者也是必需的,因为有可能在引导过程中发生这两种异常。可以在 SRAM 中开出一块空间用于存储向量表。在引导期间先填写好各向量,然后在引导完成后,就可以启用内存中的新向量表,从而实现向量可动态调整的能力。

3.4 Keil工程设置

3.4.1 Flash地址设置

IAP工程设置类似,这里的Application是另一个Keil工程,同样的需要对Flash进行设置,如下图所示;

在这里插入图片描述
这里Application工程的Flash地址偏移了0x3000,正是之前Bootloader所占用的Flash空间大小,这里和VTOR的设置也必须保持一致;

在这里插入图片描述

3.4.2 hex2bin

配置工程最终生成hex文件,如下图所示;

在这里插入图片描述

最终我们需要使用的是bin文件,所以,这里需要使用将hex文件转成bin文件,本文使用hex2bin的工具;配置工程运行之后执行转换文件的脚本;

在这里插入图片描述
create_bin.bat

echo %cd%
set ROOT_PATH=.\..\..
set SRC=%ROOT_PATH%\..\Project\STM32F10x_StdPeriph_Template\MDK-ARM\STM3210B-EVAL\*.hex
set TOOL=%ROOT_PATH%\..\Bin\hex2bin.exe

if exist %SRC% (%TOOL% %SRC%)
exit

copy_firmware.bat

echo %cd%

set ROOT_PATH=.\..\..
set HEX_FILE=%ROOT_PATH%\..\Project\STM32F10x_StdPeriph_Template\MDK-ARM\STM3210B-EVAL\*.hex
set BIN_FILE=%ROOT_PATH%\..\Project\STM32F10x_StdPeriph_Template\MDK-ARM\STM3210B-EVAL\*.bin
set DST_DIR=%ROOT_PATH%\..\Firmware\
if exist %HEX_FILE% (XCOPY /Y /S /F %HEX_FILE% %DST_DIR%)
if exist %BIN_FILE% (XCOPY /Y /S /F %BIN_FILE% %DST_DIR%)
exit

最终在Build之后,可以在Firmware中找到STM321-APP.bin,这个文件就是要用来IAP程序进行串口下载的程序。

3.4.3 用户程序串口下载测试

SecureCRT软件支持ymodem协议,可以安装该软件,打开串口连接,设置ymodem的协议;
打开菜单选项:OptionsSession Options,配置如下;

在这里插入图片描述
为了便于测试,在STM32F1-APP进行串口发送以下信息,便于观察APP是否正常启动;

usart_printf(" \r\n STM32F1-APP start running*******************");

打开连接的串口,并根据终端提示进行操作,最终下载固件STM32F1-APP成功,并成功运行;

在这里插入图片描述

4 附件

stm32f10x-bootloader

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐