现在的智能硬件很多都带有WIFI或BLE等无线联网模块,因此可以通过OTA在线升级的方式来更新设备逻辑功能,即通过WiFi等方式从网络云端下载新的固件,然后通过IAP在线更新单片机的固件,而不影响设备的运行。对于使用MCU单片机为核心的硬件设备,基本都是通过MCU的IAP功能来实现OTA升级的。IAP是In Application Programming的缩写,即在应用编程,设备的程序在运行过程中对flash的部分区域重新烧写新的逻辑功能。
实现IAP功能,通常需要用户编写两个独立的代码,第一个代码一般称为BootLoader程序,其功能是接收需要升级的程序数据,并对第二个代码进行烧录更新;第二个代码则是真正用于实现设备的逻辑功能的,一般称为APP程序。这两部分代码都烧录在flash中,第一个代码烧录后不会发生变更,第二个代码就利用第一个代码使用在线升级的方式进行更新维护。代码在设备上的实际运行流程如下:上电后单片机先运行第一个代码,并检查是否需要更新第二个代码;如果需要更新就执行升级操作,升级完毕后,跳转到第二个代码运行;如果不需要更新就直接跳转到第二个代码运行。
以STM32为例,网络上关于IAP实现的讲解已经比较详细具体,但对于为什么需要这样做的本质原因没有细讲。
IAP的本质其实是设置存放中断向量表的地址或设置中断向量值。一般网络上推荐的IAP实现方法都是前者,实际后者也是可行的,最近项目中遇到的就是通过设置中断向量值实现。
1. 实现方式一
中断向量表中存放着一条条的中断向量,不同的中断向量对应不同的中断函数名,而函数名本质上是32位的地址值,代表该中断函数的入口存放地址。因此,
中断向量表可以理解为一个用于存放32bit地址的整型数组,数组中的每个值对应着一个中断函数的32bit入口地址。这些中断函数的入口地址如果没有进行人为固定的话,其值是变化的,
随着程序的每次修改而重新编译生成可执行文件时,中断向量表中的中断向量值都会发生变化,如图1所示,由于STM32是32bitMCU,因此内存地址是4Byte递增。

图1
中断向量表这个数组在flash中的存放地址则一般是固定的,比如STM32的0x08000000,当然也可以人为修改,代码示例为:SCB->VTOR= FLASH_BASE | 0x10000,表示将中断向量表从地址0x08000000移动到0x08010000,这样就有了两张分别存放在地址0x08000000的中断向量表1和地址0x08010000的中断向量表2。表1属于BootLoader代码段,由于BootLoader程序不会更改,因此表1中的各中断向量值不会发生变化;表2属于APP代码段,由于我们每次修改APP代码生成可执行文件时,表2中的各个中断向量值都会发生变化。因此,我们无法只使用表1,因为表1的中断向量值不变,无法对应每次APP更改时生成的中断函数地址,所以需要重新设置一个表2。这种方式就是上述所说的第一种方案,也是目前网络上讲解的最多的实现方式。当BootLoader代码检测到有固件需要更新时,就将该固件烧写到从地址0x08010000开始的flash中,待烧录完毕,再通过函数指针跳转到储存于0x08010000地址的APP代码段运行,之后发生的所有中断都按照表2进行响应,如图2所示。

图2
2. 实现方式二
通过上述方式1的分析可知,我们无法只使用一个中断向量表的原因是,BootLoader代码的中断向量表中的值是固定的,而APP代码发生更改时中断向量表中的值会跟着发生变化。那如果我们
人为地将APP代码的中断向量表中的中断向量值固定死不变,并保持和BootLoader的中断向量值相等,那就可以共享一个中断向量表了,BootLoader和APP使用相同的中断函数入口,如图3所示。同时在每个中断函数中声明一个变量,用来区分当前是运行在BootLoader还是APP中,然后执行相应的中断处理逻辑。这样即使APP每次更改重新生成可执行文件时,中断向量表中的值也不会发生改变,可以正常响应中断函数。这种方法在前段时间的一个项目中应用实践过,没有问题,在此跟大家分享一下。

图3
你好,我是电子小白菜,如果你喜欢我的文章,就请点个在看,并关注我吧。
