前言
IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
一、IAP是什么?
以上是百度的搜素结果,直白点说就是一个MCU里有两个不同程序分别叫Bootloader和APP,其中Bootloader主要是为了升级APP而存在,当MCU上电开机会先运行Bootloader判断是否需要升级APP程序,是的话会擦除APP程序对应储存区域重写写入新的APP程序,否的话会直接跳转到APP程序。APP程序收到升级请求会跳转到Bootloader然后升级APP程序。
二、如何实现
1.Bootloader如何更新APP
以GD32E10R8为例IAP 程序通常由两个部分组成:Bootloader 和 APP。Bootloader 和 APP 分别为两个工程程序,存放在 Flash 的 Main Flash 区,即 0x08000000 开始的区域。如Bootloader 必须放在0x08000000 开始的区域,而APP就看你的Bootloader 程序有多大如你的Bootloader 有8k,那么你的8k换算成十六进制为0x2000,那么你的APP就必须放在0x08002000为开始 的位置或者更后面的位置。
2.Bootloader如何跳转到APP
代码如下(示例):
#define AppversionAddress 0x0800C000
#define USER_FLASH_BANK0_FIRST_PAGE_ADDRESS 0x08002000
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
uint32_t JumpAddress = 0;
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
/* init modules … */
……
/* if no need to update APP */
if(……){
/* Check if valid stack address (RAM address) then jump to user application */
if (((*(__IO uint32_t*)USER_FLASH_BANK0_FIRST_PAGE_ADDRESS ) & 0x2FFE0000 ) == 0x20000000) //ApplicationAddress新程序起始地址是否合法
{
/* disable all interrupts */
nvic_irq_disable(EXTI0_IRQn);
…
/* Jump to user application */
JumpAddress = *(__IO uint32_t*)(USER_FLASH_BANK0_FIRST_PAGE_ADDRESS + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*)USER_FLASH_BANK0_FIRST_PAGE_ADDRESS);
Jump_To_Application();
} else {
/* LED2 ON to indicate bad software (when not valid stackaddress) */
gd_eval_led_on(LED2);
/* do nothing */
while(1){
}
}
/* Bootloader codes for update APP areas */
} else {
/* Bootloader realizing codes */
/* including commands of operating flash */
……
while (1){
/* Bootloader realizing codes */
}
}
}
3.APP代码
代码如下(示例):
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
/* set the NVIC vector table base address to APP code area */
nvic_vector_table_set(NVIC_VECTTAB_FLASH, APP_OFFSET);
/* enable global interrupt, the same as __set_PRIMASK(0) */
__enable_irq();
/* init modules … */
……
while (1){
/* APP realizing codes */
}
}
4.代码解读
if (0x20000000 ==(((__IOuint32_t*)USER_FLASH_BANK0_FIRST_PAGE_ADDRESS) & 0x2FFE0000))
这里 USER_FLASH_BANK0_FIRST_PAGE_ADDRESS 宏存储的是 APP 程序起始地址,而 APP
程序起始地址存储的是栈顶指针(查看启动文件向量表的前一个地址),如果下载了 APP 程序的话,
则 APP 程序起始地址处必然是写入了栈顶指针,所以可以通过查看栈顶指针值是否位于 SRAM 地
址范围来判断是否已经下载了APP程序。SRAM地址范围可以通过查看对应型号MCU的datasheet
得知,例如本例是 96K,即 0x18000 字节,应当查看 SP 是否位于 0x20000000~0x20017FFF,可
以检查 SP 的 bit 17-31,即与 0x2FFE0000 相与后判断值,但这个比较并不准确,可以直接采用准
确的范围比较方式。如果判断结果为已经下载了 APP 程序,则进行后续的跳转动作。
nvic_irq_disable(EXTI0_IRQn);
在跳转到 APP 程序之前需要关闭所有中断,这么做是为了避免 APP 程序运行出错或卡死。一个原
因为在运行 Reset_Handler 函数的__main 时会初始化 APP 应用的 RAM 区数据,如果由于未关闭
其他中断而来了一个中断,这个中断此时还是 Bootloader 程序的中断,可能恰好改变了 RAM 区的
数据,那么在 APP 程序运行时就会出问题。另一个原因为在跳到 APP 程序后,由于我们跳转过程
中只会对系统时钟进行重新配置,而不会影响到其他模块的寄存器,因此其他已配置的寄存器信息
将保持 Bootloader 时的配置,如果并未初始化所有模块,而在跳转之前又没有关闭所有中断,那么
在 Bootloader 程序中运行的一些模块可能在 APP 程序中依然在运行,并自动触发中断,而 APP 程
序中如果没有对相应中断服务函数的清标志处理,则 APP 程序可能会陷入中断死循环而无法正常
运行,因此需要关闭所有中断。
JumpAddress = (__IO uint32_t) (USER_FLASH_BANK0_FIRST_PAGE_ADDRESS + 4);
Jump_To_Application = (pFunction) JumpAddress;
USER_FLASH_BANK0_FIRST_PAGE_ADDRESS + 4 地址处存储的是 Reset_Handler 向量,该
向量为 Reset_Handler 处理函数的入口地址,由于已经将 pFunction 自定义为 void 类型的函数指
针,所以下一句将 Jump_To_Application 指针指向 Reset_Handler 函数的入口地址。
__set_MSP((__IO uint32_t) USER_FLASH_BANK0_FIRST_PAGE_ADDRESS);
执 行 APP 程 序 的 第 一 条 指 令 , 即 将 主 堆 栈 指 针 设 置 为 APP 程 序 起 始 地 USER_FLASH_BANK0_FIRST_PAGE_ADDRESS,需要在真正运行 APP 程序之前就准备好 MSP,因为可能 Reset_Handler 的第一条指令还没来得及执行,就发生了 NMI 或者其他 fault,此时就需要 MSP 来提供堆栈。
Jump_To_Application();
执行 Jump_To_Application 指针指向的函数,即 Reset_Handler 函数,在 Reset_Handler 函数中执行完__main 函数后,将自动跳转到 main()函数,也就是 APP 主程序部分。
keil配置
Bootloader 配置,使用keil生成的文件是带地址的可执行文件并非实际的可执行文件大小因此我们得先配置keil生成Bootloader的bin文件,才知道实际大小因为MCU的Flash储存空间寸土寸金,具体方式参考此链接keil生成bin文件配置,得到的bin文件才是Bootloader实际大小,我以8K为例起始地址为0x8000000,长度为0x2000(8k)
但是APP要从0x08002000就需要配置烧录的起始地址了,APP也需要配置生成bin文件因为keil默认生成的文件带地址并不能直接写入Flash,我们要配置生成bin文件只有bin文件才能直接写入flash方法参考上面的链接,APP配置: