简介
IAP(In Application Programming)即在应用编程, IAP 是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、 USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:
1)检查是否需要对第二部分代码进行更新
2)如果不需要更新则转到 4)
3)执行更新操作
4)跳转到第二部分代码执行
第一部分代码必须通过其它手段,如 JTAG 或 ISP 烧入;
第二部分代码可以使用第一部分代码 IAP 功能烧入,也可以和第一部分代码一起烧入,以后需要程序更新时再通过第一部分 IAP代码更新。我们将第一个项目代码称之为 Bootloader 程序,第二个项目代码称之为 APP 程序,他们存放在 STM32F4 FLASH 的不同地址范围,一般从最低地址区开始存放 Bootloader,紧跟其后的就是 APP 程序,如果 FLASH 容量足够,是可以设计很多 APP 程序的。STM32F4 的 APP 程序不仅可以放到 FLASH 里面运行,也可以放到 SRAM 里面运行。
一般程序运行过程:
1、 STM32复位后,会从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序,如图1中标号○1所示。
2、 复位中断服务程序执行的最终结果是跳转至C程序的main函数,如图1中标号○2所示,而main函数应该是一个死循环,是一个永不返回的函数。
3、 在main函数执行的过程中,发生了一个中断请求,此时STM32的硬件机制会将PC指针强制指回中断向量表处,如图1中标号○3所示。
4、 根据中断源进入相应的中断服务程序,如图1中标号○5所示。
5、 中断服务程序执行完毕后,程序再度返回至main函数中执行,如图1中标号○6所示。
IAP-APP程序运行过程:
1、 STM32复位后,从地址为0x8000004处取出复位中断向量的地址,并跳转执行复位中断服务程序,随后跳转至IAP程序的main函数,如图2中标号○1、○2所示。这个过程和图1相应部分是一致的。
2、 执行完IAP过程后(STM32内部多出了新写入的程序,图2中以灰色底纹方格表示,地址始于0x8000004+N+M)跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,其过程如图2的标号○3所示。新程序的main函数应该也具有永不返回的特性。同时应该注意在STM32的内部存储空间在不同的位置上出现了2个中断向量表。
3、 在新程序main函数执行的过程中,一个中断请求来临,PC指针仍会回转至地址为0x8000004中断向量表处,而并不是新程序的中断向量表,如图2中标号○5所示。注意到这是由STM32的硬件机制决定的。
4、 根据中断源跳转至对应的中断服务,如图2中标号○6所示。注意此时是跳转至了新程序的中断服务程序中。(中断向量表对应的中断服务函数地址发生偏移)
5、 中断服务执行完毕后,返回main函数。如图2中标号○8所示。
从上述两个过程的分析可以得知,对将使用IAP过程写入的程序要满足2个要求:
1、新程序必须从IAP程序之后的某个偏移量为x的地址开始;
2、必须将新程序的中断向量表相应的移动,移动的偏移量为x;
而设置程序起始位置的方法是(keil uvision4集成开发环境)在工程的“Option for Target….”界面中的“Target”页里将“IROM”的“Start”列改为欲使程序起始的地方。
STM32程序启动过程(以M3,M4也相同)
下图是M3内核手册内的程序启动过程讲解说明,它是以程序起始地址为0x0000 0000为例进行说明讲解的,实际使用时将程序储存到flash是从0x0800 0000开始储存的。
STM32F4 的内部闪存(FLASH)地址起始于 0x08000000(程序从该地址开始存放程序)(打开魔法棒可以看到程序默认储存地址就是它),默认情况下,程序文件就从此地址开始写入和启动,后面app程序的储存地址就是0x0800 0000+N(N为偏移地址)。
STM32F4 是基于 Cortex-M4 内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先取出中断向量表中地址0x08000000处的栈顶的值赋值给MSP,然后PC指针指向0x08000004内储存的地址值,该地址值就是“中断向量表”的起始地址,该起始地址对应的就是复位中断函数(Reset_Handler)的位置,如下图所示:
看上图可以看到PC指针所指的地址就是复位中断函数(Reset_Handler)的地址。Reset_Handler中断用的是汇编语言进行编写,在Reset_Handler中断内可以看到,通过SystemInit()函数进行了系统初始化,然后通过_main进入main函数开始执行main函数。自此,程序完成启动,进入main函数开始执行我们所编写的C语言程序。
同理,IAP程序中的app程序的执行也是相同的流程,需要将栈顶的值赋值给MSP,将复位中断函数的地址值赋值给SP指针。在下面的Bootload程序中就有体现。
Bootload程序
IAP函数主要执行两个工作:
1.检查是否更新,需要更新则接收并保存更新到的新程序,将保存的程序转移到flash(或SRAM)中;
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize) //将数据进行处理后写入到flash中
2.将程序从IAP程序跳转到APP程序中;
void iap_load_app(u32 appxaddr) //程序从IAP程序跳转到APP程序
如下图,在正点原子的例程中的delay_ms(10);这个延时函数最好加上,因为他这个例程判断是否接收完成是看USART_RX_CNT=oldcount,即接收到到字节数是否发生变化来判断的,单片机程序执行的速度快于串口助手发送数据的速度,如果不加延时就会出现短暂的USART_RX_CNT=oldcount的误判,然后让USART_RX_CNT和oldcount清零,造成如图3所示,串口一直发送代码长度的情况出现,而且会影响后面 iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth );的执行,造成无法写入flash,如果把橙色那句代码改成iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,sizeof(USART_RX_BUF));这样就可以不用管延时了。
这里 if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)这句代码是用来判断APP文件的正确性,在FLASH_APP1_ADDR地址开始的第一个4个字节地址内存放的是MSP初始值,第二个4个字节地址内存放的是复位中断向量的入口地址。因为flash地址范围是0x0800 0000~0x080F FFFF,最高两位总是0X08,所以复位中断向量的入口地址开头也应该是0x08,而if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)这句代码就是取复位中断向量的入口地址判断最高位是否是0x08;即通过这种方法来判断APP文件的正确性。
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}else
{
printf("非FLASH应用程序,无法执行!\r\n");
LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP!");
}
上面是对于app程序写入到flah的,同理,app程序写道SRAM中的就有 if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX.这句代码进行判断app文件的正确性 。
跳转到APP程序
上图是跳转到APP的程序,它执行的操作和iap程序启动时做的内容是一样的,就是把存放在appxaddr地址中的MSP初始值赋值给MSP,将appxaddr+4处的复位中断地址值赋值给SP指针,然后程序会跳转执行执行新的复位中断函数,然后执行新的main函数(即app函数);
APP程序
IAP 程序必须满足两个要求:
1) 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;2)必须将新程序的中断向量表相应的移动,移动的偏移量为 x;
3)app程序写入到flsah的偏移地址=app程序在中断向量表的偏移地址=app程序中【魔法棒】设置的内存起始地址;
1.起始地址和内存大小设置
上图左边是app储存在flash中的配置方法,右图是SRAM的配置方法,其中要注意,不管是flash还是sram,他们的地址偏移量都必须是0x200的整数倍,
2.设置中断向量表地址偏移
为了尽可能不修改系统级别文件,我们可以也可以在APP 的 main 函数最开头(如果main函数内有SystemInit函数则在SystemInit函数后添加)处添加如下代码实现中断向量表的起始地址的重设:
FLASH的通过:SCB->VTOR = FLASH_BASE | 0x----;进行设置;
SRAM的通过:SCB->VTOR = SRAM_BASE | 0x----;进行设置;
3.生成下载文件bin文件
通过MDK自带的fromelf.exe进行转换,fromelf.exe转换工具的语法格式如下:fromelf [options] input_file
其中[options]包括的选项及详细描述见表一。