目录
功能实现:
正常上电或复位后运行用户Bootloader程序,检查变量存储区的标志位。如果标志位为APP_FLAG则跳转到APP程序运行;如果标志位为BOOT_FLAG,则运行用户Bootloader程序,等待接收文件并准备IAP升级后跳转到新的APP程序运行。
APP程序运行时,完成正常功能;当接收到上位机从串口发来升级的指令后,会在FLASH中的变量存储区写下升级的标志位BOOT_FLAG,并且通过软复位返回用户Bootloader程序。Bootloader程序会接收新的程序文件并放到FLASH的APP空间,完成升级后会写下跳转标志位APP_FLAG,同时运行新的APP程序。
-
IAP相关
1. IAP概念:
IAP(In Application Programming)即在应用编程,也就是用户可以使用自己的程序对单片机 Flash的某一区域进行烧写。当产品发布后,可以很方便的使用预留的通信接口(串口、USB、网口、蓝牙等)来完成程序的升级,避免使用ICP/ISP方式烧写程序。实现IAP技术的核心是一段预先烧写在单片机内部的IAP程序。这段程序主要负责与外部的上位机软件进行握手同步,然后通过外设通信接口将来自上位机软件的程序数据接收后写入内部指定的Flash区域,然后再跳转执行新写入的APP程序,从而达到程序更新的目的。
因此,实现IAP功能需要两套程序:
① 用户Bootloader程序。这部分程序一般存储在FLASH的起始位置,用来引导、升级App程序;
②App程序,即实现产品功能的程序。通过用户Bootloader来完成对App程序的更新升级,从而实现IAP功能。
2. IAP与ICP/ISP的区别:
ICP(In-Circuit Programming)技术是通过在线仿真器对单片机进行程序烧写;ISP(In-System Programming)技术则是通过单片机内置的Bootloader程序(区别于IAP中的用户Bootloader程序)引导的烧写技术。无论是ICP技术还是ISP技术,都需要有机械性的操作如连接下载线,设置跳线帽等。若产品的电路板已经封装在外壳中,或者安装在狭小空间等难以触及的地方,要通过ICP或ISP技术对程序进行更新非常困难。使用IAP则可以避免以上问题,而且若使用远距离或无线的数据传输方案,甚至可以实现远程编程和无线编程,这是ICP或ISP技术无法做到的。
-
STM32F4的启动模式
图1 STM32F4启动模式
如图1所示,当BOOT0设置为0时,启动模式为用户闪存存储器启动。此时主闪存存储器被映射到启动空间(0x00000000),但仍然能够在原有的地址(0x80000000)访问,即闪存存储器可以在0x00000000和0x80000000这两个地址区域访问。
当BOOT0设置为1,BOOT1设置为0时,启动模式为从系统存储器启动。此时系统存储器被映射到启动空间(0x00000000),但仍然能够在他原有的地址(0x1FFFF000)访问。该模式用于串口下载。
当BOOT0设置为1,BOOT1设置为1时,只能在0x20000000开始的地址区访问SRAM。
-
FLASH相关
在开始编程之前,需要了解不同型号单片机的Flash容量和闪存模块组织,从而确定Flash分配。由于Flash在擦除时是以扇区(Sector)为单位进行,所以要在写程序之前确定好Flash的扇区分配,保证用户Bootloader程序、变量存储区和APP程序不能存在于一个扇区,防止更新程序或数据时误擦除其他数据,造成死机(踩坑点1)。
1. STM32F4 FLASH简介
STM32F411xC/E 产品的闪存模块组织如图2所示:
图2 STM32F4闪存模块组织
STM32F4的闪存模块由主存储器(Main memory)、系统存储器(System memory)、OPT区域和选项字节(Option bytes)等4部分组成。
主存储器(Main memory)部分用来存放代码和数据常数(如const类型的数据), 共有8个扇区。扇区0~3容量为16KB,扇区4容量为64KB,扇区5~7容量为128K,不同容量的STM32F411拥有的扇区数不同。此次使用的芯片是STM32F411CE,Flash大小为512KB,共有7个扇区,从图2可以看出主存储器的起始地址是 0x08000000。BOOT0接GND时,从0x08000000开始运行代码。
系统存储器(System memory)主要用来存放STM32F4的内置Bootloader代码。此代码在出厂时固化在STM32F4芯片内部,用来给主存储器下载代码。当 BOOT0接3.3V,BOOT1 接GND时,从该存储器启动(即进入串口下载模式)。
OTP区域,即一次性可编程区域,共528字节,被分成两个部分。前512字节(32字节为1块,共16块),可以用来存储一些用户数据(一次性的,写完一次永远不可以擦除),后面16字节用于锁定对应块。
选项字节(Option bytes)用于配置读保护、 BOR级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。
闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。
2. STM32的内部闪存组织架构和其启动过程
STM32的内部闪存地址起始于0x08000000,一般情况下,程序文件就从此地址开始写入。STM32内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是0x8000004。当中断来临,STM32的内部硬件机制会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。
图3 STM32正常运行流程图
STM32常规的程序运行流程如图3所示:
- STM32复位后,会从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序,如图3中标号①所示。
- 复位中断服务程序执行的最终结果是跳转至C程序的main函数,如图3中标号②所示。而main函数应该是一个死循环,是一个永不返回的函数。
- 在main函数执行的过程中,发生了一个中断请求,此时STM32的硬件机制会将PC指针强制指回中断向量表处,如图3中标号③所示。
- 根据中断源进入相应的中断服务程序,如图3中标号⑤所示。
- 中断服务程序执行完毕后,程序再度返回至main函数中执行,如图3中标号⑥所示。
图4 加入IAP之后程序运行流程图
在STM32微控制器上实现IAP方案,除了常规的串口等通讯接口接收数据以及闪存数据写入等常规操作外,还需注意STM32的启动过程和中断响应方式。
加入IAP程序后,程序运行流程图如图4所示:
- STM32复位后,从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序,随后跳转至IAP程序的main函数,如图4中标号①、②所示。这个过程和图1相应部分是一致的。
- 执行完IAP过程后(STM32内部多出了新写入的程序,图4中以灰色底纹方格表示,地址始于0x8000004+N+M)跳转至新写入程序的复位中断向量表,如图4中标号④所示。取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,其过程如图4的标号③所示。新程序的main函数应该也具有永不返回的特性。同时应该注意在STM32的内部存储空间在不同的位置上出现了2个中断向量表。
- 在新程序main函数执行的过程中,一个中断请求来临,PC指针仍会回转至地址为0x8000004中断向量表处,而不是新程序的中断向量表,这是由STM32的硬件机制决定的,如图4中标号⑤所示。
- 根据中断源跳转至对应的中断服务,如图4中标号⑥所示。注意此时是跳转至了新程序的中断服务程序中。
- 中断服务执行完毕后,返回main函数。如图4中标号⑧所示。
总结:
通过以上两个过程的分析, IAP 程序必须满足两个要求:
- 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;
- 必须将新程序的中断向量表做相应的移动,移动的偏移量为 x。
3. 应用IAP时的FLASH分配
此次设计程序时将Flash分为3部分:
- 0x08000000 ~ 0x08010000-1: 64K(Sector0~Sector3),用于存放用户Bootloader程序;
- 0x08010000 ~ 0x08020000-1: 64K(Sector4),作为变量存储区,用来存放程序运行标志位等;
- 0x08020000 ~ 0x08080000-1:384K(Secror5~Secror7),用于存放APP程序。
-
IAP工程在Keil中的设置
-
设置Flash分区:
因为用户Bootloader程序和APP程序都是单独的工程,所以要分别在Keil中设置IROM1的大小(踩坑点2):
① 用户Bootloader工程
Options for Target -> Target -> IROM1 设置0x08000000~0x0800FFFF的64K空间作为用户Bootloader程序存储区:
图5 用户Bootloader工程起始地址设置
② APP工程
Options for Target -> Target -> IROM1 设置0x08020000~0x0807FFFF的384K空间作为APP程序存储区:
图6 APP工程起始地址设置
2. 设置中断向量表偏移量:
在系统启动的时候,会首先调用SystemInit函数初始化时钟系统,同时SystemInit还完成了中断向量表的设置。STM32通过VTOR寄存器存放中断向量表的起始地址,对于Flash APP,需要设置中断向量表的偏移量为0x20000,设置方法为在Flash APP的main函数最开头处添加一行代码实现中断向量表的起始地址的重映射(踩坑点3):
SCB->VTOR = FLASH_BASE | 0x20000; //FLASH_BASE为0x80000000
图7 设置中断向量表偏移
3.生成.bin文件:
IAP更新需要用到.bin文件,而MDK默认生成的是.hex文件。可通过MDK自带的格式转换工具“fromelf.exe”来实现“.axf”文件到“.bin”文件的转换。该工具在MDK的安装目录\ARM\ARMCC\bin文件夹。“fromelf.exe”转换工具的语法格式为: fromelf [options] input_file。
① 打开Flash APP工程文件Options for Target ->USER选项卡,在After Build/Rebuild菜单下勾选Run #1,User Command栏中填写如下内容,注意修改生成的.bin文件名与.axf文件名相同:图8 编译后生成.bin文件
② 打开Linker选项卡,勾选“Use Memory Layout from Target Dialog”
图9 STM32的默认链接文件配置
编译成功后,会在相应文件夹下看到生成.bin文件。得到.bin文件之后,只需要将该文件传送给单片机,即可执行 IAP 升级。
图10 编译后生成.bin文件
4. APP 程序的生成步骤总结:
① 设置APP程序的起始地址和存储空间大小
② 设置中断向量表偏移量,即重新设置 SCB->VTOR 的值
③ 设置编译后运行fromelf.exe,生成.bin文件,用于IAP更新
经过以上3个步骤,就可以得到一个格式为.bin的APP程序,通过用户Bootloader程序即可实现更新。
-
跳转函数iap_load_app()相关
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(uint32_t appxaddr)
{
if(((*(__IO uint32_t*)appxaddr) & 0x2FFE0000) == 0x20000000) //检查栈顶地址是否合法.在0x2000 0000 - 0x 2000 2000之间
{
jump2app=(iapfun)*(__IO uint32_t*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(__IO uint32_t*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
该函数位于iap.c文件中,用于实现用户Bootloader程序到APP程序的互相跳转。
1. if(((*(__IO uint32_t*)appxaddr) & 0x2FFE0000) == 0x20000000)
判断栈顶地址值是否在0x20000000 ~ 0x20002000之间。在iap.h文件中定义了应用程序的起始地址为0x08020000,那么*(__IO uint32_t*)appxaddr即取0x08020000开始到0x08020003的4个字节的值。因为APP应用程序的“中断向量表”放置在0x08020000开始的位置,而中断向量表里第一个放的就是栈顶地址,参考图4。
该语句即通过判断栈顶地址值是否正确(是否在0x20000000 ~ 0x20002000之间,因为栈顶地址就是整个地址空间中SRAM所在的位置) 来判断应用程序是否已经下载了,因为应用程序的启动文件刚开始就去初始化栈空间,如果栈顶值正确,说明已经下载应用程序并且已执行启动文件的初始化。
2. jump2app=(iapfun)*(__IO uint32_t*)(appxaddr+4);
appxaddr+4即0x08020004 ,该地址存放的是图4中第二个“中断向量表”的第二项“Reset_Handler”。
-
升级APP相关
升级即新程序替换旧程序的过程。因此升级的实质就是擦除原有数据所在的Flash扇区,再将新数据写入的过程。STM32F4的Flash在编程时,必须要求其写入地址的Flash扇区 是被擦除了的(扇区内的值必须是 0XFFFFFFFF),否则无法写入。对STM32F4的Flash编程步骤如下:
- 检查 FLASH_SR 中的 BSY 位,确保当前未执行任何 FLASH 操作
- 将 FLASH_CR 寄存器中的 PG 位置 1,激活 FLASH 编程
- 针对所需存储器地址(主存储器块或 OTP 区域内)执行数据写入操作:
- 并行位数为 x8 时按字节写入(PSIZE=00)
- 并行位数为 x16 时按半字写入(PSIZE=01)
- 并行位数为 x32 时按字写入(PSIZE=02)
- 并行位数为 x64 时按双字写入(PSIZE=03)
- 等待 BSY 位清零,完成一次编程。
按以上四步操作,就可以完成一次Flash编程。不过有几点要注意:
- 编程前,要确保要写如地址的Flash已经擦除
- 要先解锁(否则不能操作 FLASH_CR)
- 编程操作对OPT 区域也有效,方法相同
将新程序写入Flash指定区域函数具体在stmflah.c 和stmflash.h中实现。
-
APP程序编写和跳转相关
1. APP应用程序的主要功能包含两个方面:
① 除升级功能外的所有应用功能
② 跳转至用户Bootloader程序准备升级
2. 跳转至用户Bootloader的两种方式:
① 使用跳转函数iap_load_app();
② 软重启Reboot
Demo中使用软重启完成从APP程序到用户Bootloader的跳转。
-
Demo程序实现
1. 使用STM32F411CEU6芯片,上电后运行APP程序,串口显示Start to Execute APP Program...,LED(PB4)以10Hz频率闪烁
2. 通过串口调试助手发送abc到串口6,程序跳转至用户Bootloader,等待接收文件更新,此时LED频率变为2Hz。注意串口参数设置,勾选“发送新行”:
若发送的数据不是abc,则提示“Error Message! Please Check the Command and Try Again!”
3. 在串口调试助手中点击打开文件,选择需要更新的APP的bin格式文件,点击发送。此时串口调试助手会显示接收到的文件大小并判断接收到的文件格式是否正确,若发送的文件格式正确,则完成更新后重新开始执行APP程序
若发送的文件不正确,则提示“Illegal Flash Applications! Please Check Your File and Try Again!”,此时可重新发送正确文件。
Demo源码:利用STM32F411实现IAP测试程序https://download.csdn.net/download/Tiffany982/76638993