1、IAP简介
IAP(In Application Programming)即在应用编程,IAP是用户自己的程序在允许过程中对User Flash的部分区域进行烧写,目的是为了产品发布后,可以方便地通过预留地通信口对产品中地固件程序进行更新升级。
2、 程序执行流程

STM32正常的程序运行流程是通过JTAG/SWD或者串口将APP下载到以0x08000000开始的地址空间中,先存储中断向量表,然后存储对应的中断服务函数入口地址,最后是主程序main。程序执行过程中,如果发生中断请求会跳转到中断向量表去查询中断号,并进入对应的中断入口执行完成后回到中断发生打断的断点处。
加入了IAP后程序执行流程如图所示:
加入了IAP后,APP还是从0x08000000开始存储,依旧是中断向量表、中断入口函数、main函数,只不过在main函数中添加了一个IAP功能,这个函数主要是将通过串口、网口、USB等外设通信接口接收到的bin文件加载到程序存储的某个未使用的地址空间中。这个bin文件中也是完整的程序包括:中断向量表、中断入口函数、main函数等。加载完成后,可以使用IAP将程序的执行指针PC跳转到新添加的bin文件的地址头部,也就是整个程序只执行bin文件的功能。
3、IAP下载流程

STM32正常使用中,用户应用程序存储在以0x08000000开头的地址空间中,ST公司的程序引导文件bootloader存储在0x1FFFF000 - 0x1FFF F7FF中。使用串口下载代码就会通过bootloader才能将用户应用程序下载到以0x08000000开头的地址空间中,使用JTAG/SWD可以直接将代码下载到以0x08000000开头的地址空间中。下载流程如图所示

IAP下载流程就是在原本正常执行的程序中,添加了自己编写的bootloader(这个和STM官方地bootloader没有关系)通过外设接收bin文件(这个时候文件存在闪存中,SRAM的某个区域中[可自定义开始地址])然后保存到code程序存储区中。 这里可以选择直接bin中的程序,或者跳转到bin程序中,依旧执行以前老版本的程序。

4、APP程序起始地址设置方法
也就是说,一个code区域中,存放了STM32芯片本身自带的bootloader,和用户开发的APP1,以及即将存入进来的APP2。系统提供的bootloader的地址空间时固定的,APP1的起始地址可以默认为0x08000000,大小可以通过Keil来查看当前APP所占内存时多少。
4.1 Code区内存
Code区的地址是从0x00000000-0x1FFFFFFF的512M空间,主要实现存储用户自定义代码和ST官方提供的bootloader。
-
bootloader存放在 0x1FFF F000 - 0x1FFF F7FF的2K的地址空间中
-
用户选择字节存放在0x1FFF F8000 - 0x1FFF F80F的16K地址空间中
-
用户自定义代码存放在 0x0800 0000 - 0x0804 0000 的256K地址空间中

CPU:STM32F103RCT6,LQFP64,FLASH:256KB,SRAM:48KB;
flash起始地址为0x8000000,大小为0x4000(16进制)--->262144字节(10进制)--->256KB。地址空间为:0x08000000 - 0x0803FFFF
RAM起始地址为0x20000000,大小为0xC000(16进制)--->49125字节(10进制)--->48KB。地址空间为:0x20000000 - 0x2000BFFF
4.2 APP地址设置
针对于用户开发的程序,需要知道程序会占用多大的内存(ROM和RAM)使用Keil对程序进行编译生成内容code、RO-data等

在keil编译后,会生成如图所示
Code: 程序代码,存储在rom中
RO-data:只读数据,存储在rom中
RW-data:可读可写数据,即存在ram中,也存储在rom中
ZI-data:未初始化数据,上电后在ram中划分一个区域统一存储
程序占用ROM = code + RO-data + RW-data
RAM = RW-data + ZI-data
在下载代码时,需要设置代码下载的起始地址,0x08000000是ROM的起始地址,0x02000000是RAM的起始地址,RAM是数据存储器,存储的是零时数据,每次执行不同程序,RAM都是从0x02000000开始。
这里设置了起始地址,会将程序29.7KB大小依次存入到以0x08000000(实际上是0x08000000+0x04 偏移地址)开头的地址空间中。全局变量,动态分配内存、局部变量等存在RAM中从0x02000000开始的地址空间,最大存到0x02000000 + 0xC0000
APP2
APP2的内存使用为ROM=30420+6804+256=37580->0x92CC->36.67K
RAM = 356+2012=2368->0x940->2.31K


最终,在code区呈现的状态为:
APP1存储在以0x08000000到0x080076C8的地址空间中,STM32F103RCT6有256K的用户程序地址空间,所以这里设备APP2的起始地址空间为0x08010000能够实现将APP1和APP2同时存入flash中。
只考虑APP1和APP2的ROM地址不发生冲突,为什么不考虑RAM是否发生冲突???
对于代码来说,所有的数据都是存放在ROM中,然后在初始化过程中,会清空以前RAM中的内容,将全局变量、内存分配等存放在RAM中。所有RAM中的内容是不会发生冲突的。
APP1正在运行,跳转到执行APP2时,会清空RAM中内容重新填入,也就是说同一时刻只有一个APP能使用RAM。
5、串口接收bin文件
5.1 如何生成bin文件
要想生成bin文件,需要在keil中进行一些配置,如图所示(先生成hex文件,然后将hex文件变为bin文件)


这个地方添加的地址为:
E:\KeilMDK\ARM\ARMCC\bin\fromelf.exe --bin -o ..\OBJ|LED.axf
E:\KeilMDK\ARM\ARMCC\bin\fromelf.exe --bin -o ../../Output/atk_f429.bin ../../Output/atk_f429.axf
前面为Keil中 fromelf.exe的路径,后面为这个工程生成的.axf路径,这里添加的是相对路径。点击编译,会在OBJ目录下生成一个.bin文件。
5.2 串口接收bin文件
配置好后,编辑编译在文件生成路径下会生成一个bin文件
使用串口工具,打开这个bin文件,然后点击上传。

这里第一个APP能够接收这个bin文件是因为在程序中编写了bootloader,实现串口接收数据,并将数据存入到Flash中。
6、IAP代码实现
6.1 写入程序

传入写入flash的起始地址,数据数组和数据大小
参考第七章 STM32内部FLASH读写,每次对flash是半字,也就是uint16类型。每次以1024个uint16写入到flash中。
如果APP为3.4K,也就是执行一次for 会写入2k uint8的数据,剩余的1.4k从if(i)中写入到flash中
6.2 跳转到应用程序起始地址

在iap_load_app()函数中,接收到的数据,首先存储在SRAM中,然后再写入到flash中,
这里使用appxaddr检测栈顶地址是否合法(这里有个疑惑,为啥appxaddr == 0x0801000 与上一个值会是0x20000000):这是因为STM32程序的前两个字(8字节)必须是栈顶地址和复位向量表。
也是就算是如果设置0x08010000为程序的起始地址,这个地址存储的栈顶地址,栈顶地址一定是指向SRAM(0x200000000~2001FFFF之间 具体要根据不同芯片,有不同的SRAM大小)。所以,0x08010000 的地址指向 0x20000000+size的区域。
为什么appxaddr+4是因为复位向量表的地址是这个,程序也是从复位开始执行的