最近因项目需求要实现STM32的在线升级即IAP功能,先将这几天的学习体会和IAP的具体实现总结出来,分享给大家,希望对同样实现IAP的童鞋有所帮助,文中最后会上传名为STM32_Update.zip的压缩文件里面包含了STM32_App、STM32_MyBoot_V1.0和升级软件STM32_UpdateSoftware的源码文件供大家参考。所有程序都经过测试,可以直接在原子哥的开发板上跑,上位机的升级软件大家可以直接打开 STM32_Update\STM32_UpdateSoftware\Release\STM32_UpdateSoftware.exe来升级,如果需要查看源码请用VS2010打开工程文件。

最终要实现的是:
单片机每次上电会先运行Boot程序,检查标志位如果标志位为FLAG_TO_APP则直接跳转到App程序运行,如果标志位为FLAG_TO_BOOT,则运行Boot程序准备升级。在运行App程序时,当接收到升级的指令后会在FLASH中的某处空间写下升级的标志位FLAG_TO_BOOT,并且加载Boot程序,Boot程序会接受新的程序文件并且存储在相应的FLASH空间里,完成升级后会在标志位的空间写下FLAG_TO_APP,并且运行新的程序。

帖子包含如下几个方面:
1. 什么是IAP?
2. STM32的启动模式?
3. STM32的FLASH分布?
4. STM32程序的运行过程?
5. BootLoader程序的编写(如何实现程序的动态加载)?
6. App程序的编写?
7. bin文件的转换?
8. 上位机串口升级软件的简介
-------------------------------------------------------------------------------------------------
1. 什么是IAP?
IAP的知识网上的各种资料也说的比较明白,在此简单介绍一下。IAP( In Application Programming)即在线应用编程,也就是用户可以使用自己的程序对单片机的User Flash的某一区域(一般为存放自己程序的区域)进行烧写。在真正的工作中产品发布后,可以很方便的使用预留的通信接口(串口、USB、网口、蓝牙等)来完成程序的升级,从而避免了把机器拆开使用下载器烧写程序。要实现IAP功能一般要设计两部分代码,一是BootLoader程序,这部分程序存储在FLASH的某一位置,主要用来引导、升级App程序;二是App程序,这个程序才是实现产品的功能程序。通过BootLoader来完成对App程序的更新升级,这就是IAP功能。
2. STM32的启动模式
很多初学者对于STM32的启动并不是很了解,这在《STM32的参考手册》以及网上各种资料里也有介绍,下面再简单介绍一下:
STM32有三种启动方式,主要是通过管脚BOOT0和BOOT1的连接方式来控制的,如下图所示,因为我们要让程序从主存储器启动,因而在硬件设计时要选择第一种方式把BOOT0接到GND,BOOT1任意,可以拉高也可以拉低。

note: STM32上电启动并不是直接进入main函数,而是先进行系统初始化,这个函数的调用是在启动文件startup_stm32f10x_hd.s(因为我的单片机是STM32F103RCT6,大容量芯片所以是这个文件)中执行复位中断Reset_Handler时被调用的,执行完复位中断才会进入main函数。
3.  STM32 FLASH的分布
STM32每种型号单片机的FLASH大小及地址分配在芯片手册里都有介绍,我使用的是STM32F103RCT6的型号其FLASH为256K属于大容量产品其FLASH的分布如下图:主存储块的地址是从0x08000000到0x0803FFFF共256K


我们在设计程序时把FLASH分成3部分,第一部分从0x08000000到0x0800FFFF共64K来存放BootLoader程序,第二部分为0x08010000到0x0802FFFF共128K来存放App程序,第三部分从0x08030000开始到0x803FFFF共64K来存放程序运行的标志位和其他,如下所示:

4. STM32程序的运行过程
STM32的程序运行过程在很多资料里也都有介绍,因为STM32F103的单片机是基于Cortex-M3核的,它的内部主要是通过中断向量表来响应各种中断,内部闪存的起始地址是0x08000000,中断向量表的起始地址是0x8000004,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,当中断来临时STM32 的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行相应的中断服务程序。

如上图所示STM32的正常启动流程是:
a. STM32上电后会从 0x8000004 处取出复位中断向量的地址,并跳转执行复位中断服务程序,如标号1所示;
b. 复位中断复位程序执行完成之后就会跳转到我们的main函数如标号2所示;
c. main函数一般为死循环,当其收到某一中断请求之后STM32会强制把PC指针指向中断向量表,如标号3所示;
d. 查询中断向量表,根据中断源来跳转到相应的中断服务程序中执行响应的操作;如标号4、5所示;
e. 执行完中断服务程序之后会再回到main函数中,如标号6所示。
以上是STM32的正常运行过程,而当加入IAP程序之后,运行流程就如下所示:

加入IAP后程序运行如下:
a. STM32复位之后还是从0x8000004处获取中断向量表的地址,并跳转执行复位中断服务程序,如标号1所示;
b. 执行完复位中断服务程序之后回调转到IAP的main函数中,如标号2所示;
c. IAP的过程就是通过某种选定的通信方式(如串口)来接收程序文件,并且存储在指定的FLASH空间里,随后会加载新的程序,而新程序的复位中断向量起始地址为0X08000004+N+M,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如标号3、4所示;
d. 此时在STM32的FLASH里面会有两个中断向量表,在新程序 main 函数执行的过程中,当中断来临时PC指针仍会回跳转至地址为
0x8000004 中断向量表处,而并不是新程序的中断向量表,这是由STM32的硬件机制决定的,如标号5所示;
e. 查询中断向量表,根据中断源来跳转到新的中断服务程序中执行响应的操作,如标号6所示;
f. 执行完中断服务程序之后会再回到main函数中,如标号7、8所示。
note:
由上可知新的程序在FLASH中必须放在IAP程序之后的某个地址里,这里我的程序中设置的是0x08010000 即偏移量为0x10000,而且新程序的中断向量表也要做相应的偏移,偏移量也为0x10000 (地址的设置可以通过编译软件来实现,下文会有介绍)。

5. BootLoader程序的编写
   BootLoader程序主要的功能是接收新的程序并把它存储在FLASH的特定位置,然后加载新的程序运行。单片机每次上电都会先读取一个标志位,根据此标志位来决定是运行APP程序还是来运行自己来升级。
flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG); (FLASH_ADDR_UPDATE_FLAG 是0x08030000的地址)
当flag = FLAG_TO_APP 则加载App程序,否则执行升级程序。在我的程序中通过串口来完成程序bin文件的传输,为了通信安全制定通信协议,串口接收分为两种:
a. 指令的接收,长度为16个字节,协议示例为
test[16] = {55, aa, 01, 指令长度,命令码,00,00,...00, 和校验位}
和校验位 = 0 - 前15字节的和,
b. 程序文件的接收,每包数据为(2048 + 6)个字节,示例为:
test[2054] = {55, aa, 01, 包号,命令码,数据文件2048字节,和校验位}之所以设置以上的通信协议就是为了保证数据传输的正确性。

  • Boot程序的主函数

Boot程序的main函数里主要是读取标志位flag根据flag的值来决定是加载现有的App程序还是运行自身的升级程序,在自身运行时会定时给上位机软件发送BOOT准备完成的指令,告诉上位机我准备好了,并运行 ReceiveUsartData(); 根据串口中断里的标志信息来完成对指令和程序文件的接收。

int main(void)
{ int flag = 3;int nCount = 0;delay_init();  uart_init(115200);LED_Init();TIM3_Init(99, 719);  //10ms定时flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG);  //读取标志位while(1){        //FLASH_EraseAllPages();  //仅在擦除所有FLASH时打开if(flag == FLAG_TO_APP){Iap_Load_App(FLASH_ADDR_APP);}        else{      ReceiveUsartData();   //串口接收if(Flag10MS == 1)  {          Flag10MS = 0; nCount++;if(nCount == 10)  //100ms{nCount = 0;USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_PREPAR_BOOT_OK); //不能发送过快否则会有脏数据LED0 = !LED0;}                            }}}
}
  • 串口初始化程序

使用STM32的USART1,设置波特率为115200、8位数据长度、1个停止位、无校验位,        
  具体实现见源码的uart_init()函数。

  • 串口中断服务程序 void USART1_IRQHandler(void)

在串口中断服务程序中主要是接收上位机升级软件发过来的数据,当UpdateFlag置1时为接收bin程序文件的数据,当UsartRxCodeCount 的计数等于每包传输的总字节数USART_RECEIVE_CODE_DATA_SIZE时,置接收完成标志位UsartReceiveFlag = 1并且置NextPageFlag = 1 跳出中断去ReceiveUsartData()处理,把接收到的数据存储在FLASH的指定位置,不断循环直到文件全部接收完成。升级指令的接收方法 相同,详见代码。
  ( note:在中断服务函数里,尽量不要做其他的操作,只设定标志位,具体的操作去外面的函数执行。 )

  • 重新加载代码的程序

为了实现Boot和App程序之间跳转,则必须在升级完成之后重新加载新的程序文件,其中涉及到在C语言里内嵌汇编语言,代码如下:

void MSR_MSP(u32 addr)
{//asm("MSR MSP, r0");  //使用Keil内嵌汇编时使用这两句//asm("BX r14");__ASM("msr msp, r0");  //set Main Stack value 将主堆栈地址保存到MSP寄存器(R13)中__ASM("bx lr"); //跳转到lr中存放的地址处。bx是强制跳转指令 lr是连接寄存器,是STM32单片机的R14
}typedef  void (*IapFun)(void);                                //定义一个函数类型的参数
IapFun JumpToApp; //跳转到应用程序 AppAddr:用户代码起始地址.
void Iap_Load_App(u32 AppAddr)
{if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000)        //检查栈顶地址是否合法.{ JumpToApp = (IapFun)*(vu32*)(AppAddr+4); //用户代码区第二个字为程序开始地址(新程序复位地址)                MSR_MSP(*(vu32*)AppAddr);                 //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)JumpToApp();        //设置PC指针为新程序复位中断函数的地址,往下执行}
}

首先 if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000) 的作用是检查栈顶地址是否合法,(*(vu32*)AppAddr)是去除用户程序首地 址里面的数据,而这个数据就是用户代码的堆栈地址,而堆栈地址指向RAM,RAM的起始地址是0x20000000,因此可以用上免得语句判断用 户的堆栈地址是否合法。 当判断栈顶地址合法之后取出新的复位中断函数的地址即(vu32*)(AppAddr+4),并把它赋值给函数指针JumpToApp,然后调用 MSR_MSP()函数把主堆栈指针赋值给MSP寄存器,最后调用JumpToApp();来执行新的程序。
   (这里涉及到函数指针的知识,一定要理解函数名本身就是该函数的入口地址,它的实质是一个地址。)
上面涉及到嵌入汇编的知识,可能讲解不是很透彻感兴趣的朋友可以参考《Cortex-M3 权威指南》获取更多的了解。

  • 中断向量表的设置和起始地址的设置(IAR软件)

在IAR软件中设置程序的中断向量表和程序的入口地址的方法如下:
1. 打开工程,在工程名STM32_BOOT_v1.0上右键--Options

2. 选择Linker--Edit.

3.  设置中断向量表的地址 Vector Table  和  Memory Regions的值

6. App程序的编写
App程序相对简单,它主要包含两部分,一是程序要实现的主体功能(比如点亮LED),主要是你想让App做什么就实现什么;二是通过串口来查询升级指令,当收到升级的命令后要在FLASH_ADDR_UPDATE_FLAG 的地址处写入FLAG_TO_BOOT的标志位,并且调用 Iap_Load_App()l 加载运行BootLoader的程序来完成升级,详细请看源码。
对于App程序的要设置其中断向量表的偏移通过语句 SCB->VTOR = FLASH_BASE | FLASH_VTOR_OFFSET;来实现,FLASH_VTOR_OFFSET这个变量在程序中是 #define FLASH_VTOR_OFFSET  ((uint32_t) 0x10000 ) 因为我们的App程序存储地址是 0x08010000 相对于 0x08000000 来说偏移量即为 0x10000, 而且在程序编译时要设置 Vector Table  和  Memory Regions的值为 0x08010000

7. bin文件的转换
升级程序时编译出的程序文件最好选用bin格式的文件,因为bin文件比hex文件要小的多从而占用的FLASH更小,这是比较主观的优点,使用IAR软件编译时可以通过对软件的设置来输出bin格式的可执行文件,设置如下:
a. 打开工程的Options选项卡选择选择Output Converter

b. 在Output format选项中选择 binary格式,同时把Override default输出文件的后缀改为.bin,这样在相应的工程目录下(我的是 STM32_App\Project\EWARM5\Debug\Exe) 路径下就可以找到编译输出的bin格式的可执行文件了。
8. 上位机升级软件的简介
   我的上位机升级软件是使用C++写的,具体编码不做介绍了,想了解的朋友可以参考源码。对话框界面如下:

首先设置 端口号 和 波特率 ,然后连接串口,连接成功之后,点击“选择要升级的文件”来实现升级。

升级完成之后会提示“升级完成”。

      到此我的IAP实现介绍完成,如果大家有什么问题或者我的程序中大家发现了什么bug可以提出来一起探讨,希望以上内容会对大家学习STM32有所帮助。

STM32实现IAP功能之一相关推荐

  1. STM32通过USB实现Bootlader/IAP功能

    前沿: 最近在做STM32的USB Bootlader/IAP功能,也就是通过USB实现固件升级,本文介绍下实现的基本思路,希望对实现IAP的同学一个参考,改方法已经在产品中得到实际应用并验证是比较合 ...

  2. 关于STM32的IAP与APP互相跳转

    关于STM32的IAP与APP互相跳转 之前做了一个不带系统的IAP与APP互相跳转,在网上找了资料后,很顺畅就完成了,后来在IAR集成开发环境下,IAP无系统,APP用UCOS系统做互相跳转出现了很 ...

  3. STM32开发 -- IAP详解

    如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/87978183 现在只是简单的预研知道有这个功能,怎么实现我真的没做过... ...

  4. STM32单片机IAP介绍

    1.什么是IAP? 首先区分下两个概念:ISP和IAP: ISP:In System Programming (在系统中编程),通过芯片专用的串行编程接口对其内部的程序存储器进行擦写. IAP:In ...

  5. 关于STM32单片机IAP升级中if(((*(__IO uint32_t*)ulAddr_App) 0x2FFE0000) == 0x20000000)语句的理解

    没有标题 初见if(((*(__IO uint32_t*)ulAddr_App) & 0x2FFE0000) == 0x20000000) 语句理解 语句功能 为什么ulAddr_App里存的 ...

  6. STM32FXX IAP 功能应用介绍

    ST MCU 提供了一个IAP功能,即STM32 in-application programming (IAP),这个程序通过ST 原始方式(比如常见的拉BOOT管脚进BOOT,然后进行升级)烧录至 ...

  7. STM32芯片IAP升级机制详解

    一.先了解ICP和ISP.IAP之间的区别: 1. ISP(In System Programing 在系统编程)的实现相对要简单一些,一般通用做法是内部的存储器可以由上位机的软件通过串口来进行改写. ...

  8. 基于STM32的IAP升级程序

    基于STM32的IAP升级程序 IAP介绍 功能设计 硬件设计 软件设计 软件设计流程如下 完整程序 具体实现 设计功能程序 IAP实现程序 主程序 后续 IAP介绍 IAP(In Applicati ...

  9. 基于STM32的多功能门禁系统(AS608指纹识别、密码解锁、刷卡解锁)

    目录 一.项目功能 二.视频 三.原理图 4.材料选择 5.部分程序 资料下载地址:基于STM32的多功能门禁系统 一.项目功能 1.AS608指纹解锁:可以录入.删除.验证指纹: 2.密码解锁:可以 ...

最新文章

  1. 【经验】配置Anaconda源
  2. 冰刃——IceSword
  3. zookeeper安装教程(zookeeper3.4.5为例)
  4. 利用人体肤色从图像中分割出人体区域的OpenCV代码
  5. RL之Q Learning:利用强化学习之Q Learning实现走迷宫—训练智能体走到迷宫(复杂迷宫)的宝藏位置
  6. Intel Realsense D435 python 如何获取(打印)所有摄像头序列号信息?
  7. IOS定位核心与地图
  8. 原码、反码、补码的运算 【2分钟掌握】
  9. 谷歌Android无障碍套件,谷歌为无障碍套件添加盲文键盘:无需额外硬件就能打字...
  10. SQL 创建随机时间的函数
  11. 冒险岛079实现按地图编号自由传送的功能
  12. 微信公众号 菜单 { “errcode“: 47001, “errmsg“: “data format error rid: 61b36b ...“}
  13. Android 文件选择/文件管理
  14. 使用Tin快速安装 Apache APISIX(全网最快)
  15. 微信小程序头像怎么改变形状_微信小程序 上传头像的实例详解
  16. Java+Springboot+Mybatis+Mysql+Bootstrap+Maven实现景区旅游管理系统
  17. 三个div怎么分别靠左、居中、靠右显示
  18. 面对人工智能,我们应有的态度
  19. (持续更新)Ubuntu22.04双系统的安装、扩容、重装及配置
  20. 微信开发之微信网页授权 完整示例

热门文章

  1. .Net软件测试化之道 [James D.MCCaffrey]
  2. [BZOJ 1879][SDOI 2009]Bill的挑战 题解(状压DP)
  3. Codeforces 963A Alternating Sum 【数论+数学】
  4. Apriori算法介绍(Python实现)
  5. 兼容低版本浏览器的一些方法
  6. Java异常信息处理
  7. sublime text3输入中文的问题.
  8. SQL Server:APPLY表运算符
  9. kill()函数 详解
  10. Python运算符及注释