STM32学习之IAP

最近在学习基于STM32的IAP,前前后后折腾了好久,也踩了许多的坑,所以在此分享一些我在使用IAP是遇到的问题以及解决方法,希望可以给需要的人一些帮助。

什么是IAP?

IAP其实就是一段提前写在单片机内部的程序,负责与上位机沟通后下载要更新的程序到指定位置,然后再跳转到应用程序的开始位置去执行新的应用程序。
关于IAP升级的方法主要有三种:固件原位升级、固件乒乓升级、固件冗余升级。
:

固件原位升级

STM32学习之IAP
固件原位升级就是应用程序接受到升级请求时跳转到BootLoader,在BootLoader中下载新的应用程序并覆盖原来的应用程序。通过这种方式进行IAP升级缺陷很明显,如果在升级过程被打断或者下载的代码由于传输问题本身就是错的,那么就会导致不仅没有升级成功,还丢失了原来的代码。容易让设备变成砖是这中方法的主要问题。

固件乒乓升级

STM32学习之IAP
固件乒乓升级顾名思义,就是程序接受到下载命令时如果程序此时在A区运行那么就将程序下载到B区,如果程序在B区运行那么就将程序下载到A区,就像打乒乓球一样。由于这种方式的应用程序不仅具有固件原位升级中APP的功能,还有着BootLoader的功能,所以这种方式充分的解决了上一个问题的缺陷,即使升级失败也不会影响原来的应用程序。其缺点是应用程序要在flash里存储两份,所以对flash的浪费比较厉害,对于一些flash资源比较紧张的芯片来说这种方法显然是行不通的。那有没有一种对于flash比较下的芯片还比较稳定的升级方案呢?肯定是有的,就是最后一种方案:固件冗余升级。

固件冗余升级

STM32学习之IAP
固件冗余升级和固件原位升级有着很大的相似之处,不同的是固件冗余升级中BootLoader程序只负责程序的加载,即将外部flash中应用程序加载到内部flash中的程序执行区域。应用程序负责固件的下载以及下载后的跳转动作的执行。这里有一点必须要说明,在外部flash中可以存储多个版本的应用程序,在BootLoader加载程序时可以选择加载那个版本的应用程序,这就是固件冗余升级主要区分固件原位升级的地方。在外部flash中一般分为三个应用程序存储区,出厂程序存储区、正在运行的应用程序存储区、下载固件的程序存储区。其中可以将正在运行的应用程序存储区和下载固件的程序存储区通过乒乓升级的方式来不断下载新的应用程序。

介绍完IAP实现的方式后再来说一说我在编写IAP程序时遇到的一些问题以及解决方法。

因为我用的芯片flash足够大,所以就采用了固件冗余升级的方式来做的,并且没有使用外部flash,但是在做的过程遇到了很多的问题。
1、因为我用的是stm32f4做的IAP,而f4有一个让人非常难受地方,就是flash只能按扇区擦除,而我又不可能开一个128k的ram空间,所以就在每次写入前先将目的扇区擦除,然后再慢慢写入。

2、不管是Bootloader(这里指用户自定义的Bootloader程序)跳到APP,还是APP跳到Bootloader,再跳转之前必须保证以下几点
①向量表正确偏移
可以使用SCB->VTOR来设置向量表的位置,这个设置应该在APP程序的开始处就执行。
②栈顶指针合法(即栈顶指针必须落在你的芯片的SRAM区域内)
中断向量表的第一个偏移就是MSP的栈顶指针的初始值,中断向量表的第二个偏移为复位向量,在程序跳转是先设置MSP的栈顶指针,然后在调用复位向量进行软件复位。
③清除用到的所有中断标志位以及失能当前中断
特别是第③点,假设Bootloader里用到了定时器更新中断,但是从bootloader跳转到APP之前没有清除该中断标志位以及未失能当前中断,那么跳过去大概率卡死,除非你的APP里也用到了该定时器中断,反过来,从APP跳回bootloader也一样,必须清除所有用到的中断标志位和失能该中断方可跳转。这里之前找资料看到有人说跳转前使用cpsid i进行关中断操作,这样确实可以跳转,但是跳转后的程序里只要发生中断就会死机,所以还是在跳转前将所有用到的中断全部关闭才是较为稳妥的方法。

3、在使用串口接受app固件时为了提高应用的效率,我使用了DMA+空闲中断的方式进行接受app固件,这通过网络更新时优势就更加明显了,因为tcp接受到的数据已经被分为大小相等的若干帧了,所以此时若进入空闲中断就可以判断为一帧数据接受完成了。

PS:关于DMA+空闲中断可以参考这篇帖子 STM32使用DMA加串口空闲中断接收数据
IAP具体的实现可以参考正点原子的IAP实验,不过要注意该例程并没有进行关中断的操作,但是在写自己的IAP代码时参考一下这个例程是很有帮助的。