STM32单片机IAP(In-Application Programming)功能介绍

1、什么是IAP?

首先区分下两个概念:ISP和IAP:

        ISP:In System Programming (在系统中编程),通过芯片专用的串行编程接口对其内部的程序存储器进行擦写。

        IAP:In Application Programming( 在应用中编程),通过调用特定的bootloader程序,对程序存储器的指定段进行读/写操作,从而实现对目标板的程序的修改,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。

        简单来说ISP就是平时我们用JLINK之类的下载器通过专门的SWD接口来下载程序,IAP就是通过调用bootloader来充当下载器的功能实现更新程序的作用。

2、IAP功能概述

        通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、USART)接收程序或数据,执行对第二部分代码的更新。这就是上面说的“bootloader程序”,这部分程序必须通过ISP的方式烧录;第二个项目代码才是真正的用户功能实现代码,这一部分代码可以和第一部分代码一起通过ISP的方式烧录,也可以通过第一部分代码使用IAP进行烧录。

        这两部分项目代码都同时烧录在单片机的Flash 中,当芯片上电后,首先运行的是bootloader程序,它可以bootloader程序运行,可以是延时一段时间没检测到更新操作就跳转到第二个程序进行运行;也可以一直运行在等待更新的状态,直到受到触发才(如按键按下触发等)执行跳转到第二个程序的操作;

3、程序的执行流程

        STM32H7 的内部闪存(FLASH)地址起始于0X08000000,一般情况下,程序文件就从此地址开始写入。此外STM32H743是基于Cortex-M7内核的微控制器,其内部通过一张“中断向量表”来响应中断。程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是0x08000004,当中断来临,STM32H743的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。

        总结起来就是在正常情况下,当单片机复位后,首先从地址0x08000004读取复位中断向量执行复位中断程序完成启动。之后开始执行main函数,一般来说main函数都是在不断循环执行while(1)里面的内容,一旦收到中断请求(发生了中断),此时STM32H743强制将PC指针指回中断向量表处,根据中断源进入相应的中断服务程序,在执行完中断服务程序以后程序再次返回main函数继续执行。

        这是正常情况下程序的运行逻辑,因为bootloader程序在FLASH的开头,所以bootloader程序的运行逻辑就和正常的逻辑一样,没有特别的地方,但用户程序就不太一样了。

        上面提到过,用户程序和bootloader程序同时保存在FLASH内,并且bootloader程序在FLASH的最前面。由于用户程序的首地址并不是FLASH的首地址,但是“中断向量表”的地址还是没变,那应该怎们办呢?这里就涉及到一个新的知识点叫做“中断向量表的偏移”。

        “中断向量表的偏移”的作用就是告诉单片机我现在的程序它的复位中断程序不是在默认的位置。在执行用户程序时,它最开始还是进入0x08000004读取复位中断向量,而不是进入复位以后的复位中断向量地址。为了解决这个问题,中断向量表偏移便应运而生了。通过中断向量表偏移将新的中断向量表的起始地址映射到旧的中断向量表起始地址上,之后再根据新的中断向量表找到复位中断向量执行复位中断程序完成启动。这个时候由于新的中断向量表地址已经映射过去了,所以现在系统的向量地址已经是第二个程序的中断地址,所以现在时候的程序执行过程完全和正常程序一样了。

        这里便产生了一个问题,“中断向量表的偏移”该如何设置呢?在H7的HAL_库文件“system_stm32h7xx.c”里面有以下几行代码:

/* Configure the Vector Table location -------------------------------------*/

#if defined(USER_VECT_TAB_ADDRESS)

SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal D1 AXI-RAM or in Internal FLASH */

#endif /* USER_VECT_TAB_ADDRESS */

        这几行代码的意思就是如果定义了USER_VECT_TAB_ADDRESS,那就执行中断向量表偏移的语句。USER_VECT_TAB_ADDRESS是用户中断向量表偏移标志,VECT_TAB_BASE_ADDRESS是SRAM的偏移量,VECT_TAB_OFFSET是FLASH的偏移量。

        对于我们的用户程序来说,要实现中断向量表偏移有2种方式,一种是使用HAL_库文件中上面说到的HAL库变量,直接对USER_VECT_TAB_ADDRESS标志和偏移量进行赋值,但这个会改变HAL库系统文件。第二种就是在main函数开始就执行中断向量表偏移语句,采用这种方法可以保证HAL库系统级别的文件不被更改,只修改用户程序,便于以后的移植。

4、bootloader程序运行过程

        bootloader程序在上电后首先将各项外设进行初始化,尤其是用于传输升级文件的通讯外设。在外设初始化结束后一般都在while(1)循环中等待接收需要用于升级的数据。一旦数据接收完成则调用“iap_write_appbin”函数将接收的信息写入到用户程序对应的内存地址中,在写入完成后再通过“iap_load_app”函数跳转到用户程序进行执行。亦或者在等待一定时间后如果还没接收到需要升级的文件就跳转到用户程序进行执行。

“iap_write_appbin”函数需要调用FLASH读写函数将接收的信息写入到FLASH的指定区域。函数如下:

//appxaddr:应用程序的起始地址

//appbuf:应用程序CODE.

//appsize:应用程序大小(字节).

void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)

{

u32 t;

u16 i=0;

u32 temp;

u32 fwaddr=appxaddr;//当前写入的地址

u8 *dfu=appbuf;

for(t=0;t<appsize;t+=4)

{ 
   
temp=(u32)dfu[3]<<24;   

temp|=(u32)dfu[2]<<16;    

temp|=(u32)dfu[1]<<8;

temp|=(u32)dfu[0];   

dfu+=4;//偏移4个字节

iapbuf[i++]=temp;     

if(i==512)

{

i=0;

STMFLASH_Write(fwaddr,iapbuf,512);

fwaddr+=2048; //偏移2048  512*4=2048

}

}

if(i)STMFLASH_Write(fwaddr,iapbuf,i);   //将最后的一些内容字节写进去.  

}

        STM32单片机中不同内核的芯片其内存管理方式不太一样。

        STM32H7系列单片机采用的是存储区(BANK)和扇区(SECTOR)的结构。STM32H7的FLASH分为两个独立的存储区(即BANK1和BANK2),每个存储区再由多个128K大小的扇区组成。由于STM32H7的FLASH最小1M,最大2M,所以每一个存储区就被分成了4或8个扇区,分别对应(SECTOR_0至SECTOR_7)。由于扇区是结构中最小的单位,这就决定了FLASH的最小单位就扇区(即128K大小),所以在设置内存地址时需要注意,不要在擦除时不小心擦除了需要使用的区域。

        STM32F1系列单片机采用的是页面(Page)的结构,FLASH被分割成了多个Page。根据FLASH的大小不同,Page的大小有2K或者1K的不同。由于F1系列最大FLASH为512K,所以Page最大为256个(Page0至Page255)。这就决定了FLASH每次擦除最小都要擦除一个页面的大小。

        “iap_load_app”函数先判断栈顶地址是否合法,在得到合法的栈顶地址后,通过MSR_MSP 函数设置栈顶地址,最后通过一个虚拟的函数(jump2app)跳转到 APP 程序执行代码,实现 IAP→APP 的跳转。函数如下:

//跳转到应用程序段

//appxaddr:用户代码起始地址.

void iap_load_app(u32 appxaddr)

{

if(((*(vu32*)appxaddr)&0x2FF00000)==0x24000000)//检查栈顶地址是合法性.

{

       printf("跳转到APP\r\n");

jump2app=(iapfun)*(vu32*)(appxaddr+4); //第二个字为复位地址)

MSR_MSP(*(vu32*)appxaddr);      //初始化APP堆栈指针

jump2app();  //跳转到APP.

}

}

5、如何设置程序的起始地址

        在MDK5编译器中,点击Options for Target→Target选项卡,其中的IROM便是用来设置程序存储区的大小。其后面2个框框分别填写程序存储区的起始地址和大小。比如我要将FLASH的前256K字节大小区域用于bootloader程序,由于FLASH的首地址为0X8000000,所以第一个框框就应该填0X8000000。由于只允许bootloader程序使用前256K字节大小的内存,所以总的大小为256*1024=262144字节,转换成16进制为0X40000,所以第二个框框就应该填0X40000。如下图所示:

 6、bin文件的生成

        在使用IAP进行升级时,使用bin文件会更加方便,所以在编写完APP文件以后需要将MDK5默认生成的hex文件转换成bin文件。

        在MDK中自带有一个hex文件到bin文件的转换工具,它就是“fromelf.exe”,它的位置为“D:\Keil5\ARM\ARMCC\bin|”(D:\Keil5是自己MDK5的安装目录)。它的基本语法为{“fromelf.exe软件地址” –bin -o “bin文件地址”  “axf文件地址”}(上述地址需要包含本文件的完整名称,地址可以是相对地址,例如:"fromelf.exe" –bin -o .\WAM-USART-IAP\App.bin .\WAM-USART-IAP\App.axf)。z

        如果要在MDK中使用该工具,只需点击Options for Target→User选项卡,找到下面的“After Build/Bebuild”选项。勾选“选Run #1”并在后面的框框中填入fromelf.exe软件的语法语句,最后点击OK即可。操作如下图:

物联沃分享整理
物联沃-IOTWORD物联网 » STM32单片机IAP(In-Application Programming)功能介绍

发表评论