STM32如何IAP升级用户程序

IAP简介

IAP( In Application Programming)即在应用编程,也就是用户可以使用自己的程序对MCU的中的运行程序进行更新,而无需借助于外部烧写器。ST官网也给出了IAP的示例程序。

注意:在MCU中,有一个特殊区域被称为 System memory。在这块区域中存放了ST公司自己的 bootloader 程序,它是在MCU出厂时,有ST固化到芯片中的,后续不能再更改。其中的 bootloader 程序也可以对MCU进行升级。

设计思想

设计分两部分:

1、Bootloader(或叫IAP程序,以下混用):主要接收待升级的bin文件,擦除旧的用户APP程序,并重新写入升级文件,之后跳转执行新的用户App。

2、用户程序App:用户主代码,实现正常的功能。

Bootloader和App分别是2个独立的完整的STM32工程。

包含Bootloader和用户主App的Flash存储空间分布如下:

Bootloader及App在FLASH分布及跳转流程

STM32内部FLASH的起始地址为0X08000000,Bootloader程序就存放此地址,存放APP程序的首地址设置在紧跟Bootloade之后。当程序开始执行时,首先运行的是Bootloader程序,然后Bootloader收到用户程序bin文件并将其复制到APP区域以更新,之后跳转到APP程序执行。

程序启动后,首先从“中断向量表”取出复位中断向量,执行复位中断程序Reset_Handler,在Reset_Handler最后跳转到用户main()函数。由此可见,在最后一步的设计中需要根据存放APP程序的起始地址以及中断向量表来设置栈顶地址,并获取复位中断地址跳转到Reset_Handler。

在常规工程即只有一个程序的情况下如下所示:

STM32有一个中断向量表,存放在flash起始地址+4个字节处(即0x08000004),Flash起始前4个字节存放的是栈顶地址(MSP)。当发生中断后程序通过查找该表得到相应的中断服务程序入口,然后跳到相应的中断服务程序中执行。上电后从0x08000004处取出复位中断向量的地址,然后跳转到复位中断程序Reset_Handler入口(标号1所示),执行结束后跳转到main函数。在执行main函数的过程中发生中断,则STM32强制将PC指针指回中断向量表处(标号3所示),从中断向量表中找到相应的中断函数入口地址,跳转到相应的中断服务函数,执行完中断服务函数后再返回到main函数中来。

在内置的Flash里面添加一个BootLoader程序,BootLoader程序和user application各有一个中断向量表。假设BootLoader程序占用的空间为N+M字节,则程序的走向应该如下图所示。

上电初始程序依然从0x08000004处取出复位中断向量地址,执行复位中断函数后跳转到IAP的main(标号①所示),在IAP的main函数执行完成后强制跳转到0x08000004+N+M处(标号②所示),最后跳转到新的main函数中来(标号③所示),当发生中断请求后,程序跳转到新的中断向量表中取出新的中断函数入口地址,再跳转到新的中断服务函数中执行(标号④⑤所示),执行完中断函数后再返回到main函数中来(标号⑥所示)。对于步骤④⑤我认为的是,在main函数的执行过程中,如果CPU得到一个中断请求,PC指针本来应该跳转到0x08000004处的中断向量表,由于我们设置了中断向量表偏移量为N+M,因此PC指针被强制跳转到0x08000004+N+M处的中断向量表中得到相应的中断函数地址,再跳转到相应新的中断服务函数,执行结束后返回到main函数中来。

IAP-Bootloader实现

BootLoader流程图大致应该如下:

1、初始化时钟。

2、初始化中断向量表地址。

在BootLoader程序的中断向量表设置中应有这么一句:

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);      //设置中断向量表指向(其中NVIC_VectTab_FLASH是个宏定义,的值为0x08000000。)

其中NVIC_VectTab_FLASH是个宏定义,的值为0x08000000。

3、初始化按键。 (使用按键触发方式,上电时如果按键被按下则进行用户程序更新操作)

4、初始化串口。(用于接收用户程序bin文件)

5、检测按键是否被按下,是则执行步骤6,否则执行步骤10。

6、擦除用户程序(擦除0x08003000—0x0807ffff地址空间Flash)。

7、从串口读取新的用户代码数据,把代码写入用户程序空间。

8、检测串口数据接收完毕?是则执行步骤9,否则跳回步骤7。

9、用户程序更新完毕,等待重新上电或硬件复位。

10、跳转到用户程序(强制将PC指针跳转到0x08003000+4处)。

具体可参考官方IAP源码,实际工程实现中需根据STM32型号和用户需求修改。

预先定义:

typedef        void (*pFunction)(void);     pFunction       Jump_To_Application;uint32_t       JumpAddress;                                 #define       ApplicationAddress       0x08003000  //可自定义用户App程序起始地址

main()函数:

此处省略了“串口接收用户程序bin文件,并烧写进FLASH的用户程序空间”这部分代码。其中,操作FLASH需要用到特定的库函数,比如解锁、上锁FLASH、擦除、烧写等。

代码释义:

1、

if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)   //判断栈定地址值 //是否在0x2000 0000 - 0x 2000 2000之间

*(__IO uint32_t*)ApplicationAddress) 即取0x08003000开始的前4个字节的值, 因为我们的应用程序APP中设置把中断向量表放置在0x08003000 开始的位置;而中断向量表里第一个放的就是MSP栈顶地址的值。这句话即判断MSP的地址值是否正确(是否在0x2000 0000 - 0x 2000 2000之间) 来判断是否应用程序已经下载了,因为应用程序的启动文件刚开始就去初始化化栈空间,如果栈顶值对了,说应用程已经下载了,启动文件的初始化也执行了。

2、

JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);

ApplicationAddress + 4 即为0x0800 3004,*(__IOuint32_t*) (ApplicationAddress + 4)即获取复位中断向量Reset_Handler的地址值,然后赋值给JumpAddress。

3、

 Jump_To_Application = (pFunction) JumpAddress;

此时跳转函数指针Jump_To_Application指向了Reset_Handler程序的地址。

4、

__set_MSP(*(__IO uint32_t*) ApplicationAddress);      \\设置用户主函数栈指存储空间为ApplicationAddress处,即用过App空间首地址0x0800 3000

把存储空间ApplicationAddress(即用户App空间首地址0x0800 3000)存放的内容(即栈顶地址)赋值给MSP。

5、

 Jump_To_Application();     \\执行复位函数(强制从PC指向的JumpAddress地址执行)

把用户代码的中断复位程序地址赋给PC指针,跳转执行用户函数。

用户App设计

用户程序App关键点在Flash地址的设置和中断偏移量的设置。在用户程序的main()函数里,必须有这句话:

int main(void)  {         NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x3000);      //设置中断向量表指向......}

程序烧写

下载Bootloader和用户App的.hex文件时,要分2次烧写,先烧写Bootloader,再烧写App,两次烧写时在烧写工具软件里要分别设置起始地址和size。

下图的值是示意,必须按代码实际定义来填。

(0)

相关推荐