当前位置: 代码迷 >> 综合 >> STM32F10x IAP技术
  详细解决方案

STM32F10x IAP技术

热度:39   发布时间:2024-01-11 11:40:05.0

STM32F10x IAP技术

(BOOTLOADER)

第一部分

一 计算固件占用FLASH大小

每次生成固件时,Keil 会有一个固件信息显示,如下列图

图1

固件占用FLASH大小 = Code + RO + RW + ZI,上图中的固件大小为:11,016字节,16进制为:0x2B08。

二 固件数据结构

http://www.51hei.com/UpFiles/up/0/491511221618834.jpg

图2

STM32F10x有一个中断向量表,这个中断向量表存放在代码开始部分的后4个字节处(即0x08000004),代码开始的4个字节存放的是堆栈栈顶的地址,当发生中断后程序通过查找该表得到相应的中断服务程序入口地址,然后再跳到相应的中断服务程序中执行。上电后从0x08000004处取出复位中断向量的地址,然后跳转到复位中断程序的入口(标号①所示),执行结束后跳转到main函数中(标号②所示)。在执行main函数的过程中发生中断,则STM32强制将PC指针指回中断向量表处(标号③所示),从中断向量表中找到相应的中断函数入口地址,跳转到相应的中断服务函数(标号④所示),执行完中断函数后再返回到main函数中来(标号⑤所示)

:Stm32源代码中有说明。堆栈栈顶地址需为0x200的整数倍,原文为” Vector Table base offset field. This value must be a multiple of 0x200” 。即:”向量表基本偏移量字段。此值必须是0x200的倍数”。 因此,图1中,如该固件在FLASH中的起始地址为0x8000000。若想在该固件后继续放置其它固件,则最近的可用地址为 0x2C00

 

三 固件程序间的跳转说明

若在STM32F103x中使用IAP方案,则内置的Flash分配情况大致如下图:

http://www.51hei.com/UpFiles/up/0/491511231663247.jpg

图3

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

http://www.51hei.com/UpFiles/up/0/491511231667910.jpg

图4

上电初始程序依然从0x08000004处取出复位中断向量地址,执行复位中断函数后跳转到IAP的main(标号①所示),在IAP的main函数执行完成后强制跳转到0x08000004+N+M处(标号②所示),最后跳转到新的main函数中来(标号③所示),当发生中断请求后,程序跳转到新的中断向量表中取出新的中断函数入口地址,再跳转到新的中断服务函数中执行(标号④⑤所示),执行完中断函数后再返回到main函数中来(标号⑥所示)。

以上 参考链接:https://blog.csdn.net/wzy15965343032/article/details/88545225

对于步骤④⑤,是跳转到0x8000004 还是跳转到 0x8000004+N+M,由②③来协同决定。

若想跳转到0x8000004+N+M,则:② 在跳转前要关闭所有中断外设,清除所有中断向量的使用。若是从RTOS固件跳转到其它固件,除放弃一切中断和中断服务外,还要修改CPU的模式为特权模式、关闭SysTick、激活MSP等操作;③的main中重定中断向量表地址为0x8000004+N+M。否则固件程序在④⑤的时候会跑到0x8000004的向量表中,运行结果不可预知。通常会进入 XXXX_Handler ,如 :HardFault_Handler、SysTick_Handler等。

四 不同固件在FLASH中的地址参数

         前面已经提到了固件在FLASH中占用空间的算法;堆栈栈顶地址需为0x200的整数倍。下面举例说明,想将多个固件放在同一个MCU FLASH中的地址参数配置方法如下。

         现有BootLoader ,APP1, APP2 三个程序要放入一片 STM32F103C8T6中。三个程序在FLASH中的存储安排如下:

BOOTLOADER

APP1

APP2

 

         BOOTLOADER 程序编译后的固件信息,算下来,约占0x2C00空间,(下一个APP的堆栈栈顶地址需为0x200的整数倍,因此取了一个你好我好大家好的占用空间0x2C00)如下图:

因此BOOTLOADER 使用的是默认地址设置,如下图:

         重点是 Start : 0x8000000。(有人说Size也要设置为固件占用大小,我没有改,验证过程中也没有发现有问题)

         APP1程序编译后的固件信息,算下来,约占0x2C00空间 (实际上APP1只是把BOOTLOADER的程序改了几行代码),如下图:

         算上BOOTLOADER占用的空间,APP1的地址设置如下图:

         重点是,Start : 0x8002C00

         APP2 使用的是CMSIS RTOS2框架,实现的功能不多,占用的空间不小,约0x5C00的空间,如下图:

         算上BOOTLOAD、APP1所占用的空间,APP2的起始地址要从 0x5800开始,设置如下:

         为了使APP1、APP2初始化时,同步指定中断向量表,APP1要将中断向量偏移到程序所在地址(固件在FLASH中存储的起始地址),在system_stm32f10x.c中:

         

         同理 APP2中的也要修改:

        

         如果使用JLINK 直接烧写固件,3个固件,分3次独立烧写。无需额外设置。有朋友说也要设置地址,我验证过,没必要。但要注意不能每次烧写时把所有的FLASH清除,这样每次烧写都会把前面烧写的内容都清除掉了,这样是没有办法验证的。Jlink 烧写配置如下:

 

第二部分 程序跳转代码

说那么多都是云,代码才是重点,有多少朋友跨过千山万水,历经各种摩擦,总是不能正常跳转,就是跳不过去。以下代码专治各种跳转不服,从非RTOS固件到RTOS固件,再跳回非RTOS固件,随便跳!妥妥地!

/*程序中转:app_addr 程序在FLASH中的地址。不能在中断中调用*/
void Run_App(u32 app_addr)
{u32 spinitval;u32 jumpaddress;App_Function app = NULL;//确定中转的地址是否在正确的范围内if(((*(__IO u32 *)app_addr) & 0x2FFF0000) == 0x20000000)   //64K  的FLASH RAM
//    if(((*(__IO u32 *)app_addr) 0x2FFE0000) == 0x20000000)   //128K  的FLASH RAM
//    if(((*(__IO u32 *)app_addr) & 0x2FF80000) == 0x20000000)   //512K  的FLASH RAM{//为了不给要跳转的程序初始化造成麻烦,初始化所有端口GPIO_DeInit(GPIOA);GPIO_DeInit(GPIOB);GPIO_DeInit(GPIOC);GPIO_DeInit(GPIOD);GPIO_DeInit(GPIOE);GPIO_DeInit(GPIOF);GPIO_DeInit(GPIOG);if(CONTROL_nPRIV_Msk & __get_CONTROL())		//RTOS{__asm("SVC #0");	//确保CPU处于特权模式}//常规RCC_DeInit();	//关闭所有外设时钟NVIC_DeInit();	//禁用NVIC中的所有已启用中断和请求 ,高版本固件库中好像没有这个函数了,可以自己写一个//RTOSSysTick->CTRL = 0 ;	//关闭 RTOS 的心跳时钟SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;	//清除其异常挂起位//如果引导加载程序使用它们,则禁用各个故障处理程序SCB->SHCSR &= ~( SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk | SCB_SHCSR_MEMFAULTENA_Msk ) ;//常规spinitval = *(__IO u32 *)app_addr;	//	取得栈顶地址jumpaddress = *(__IO u32 *)(app_addr + 4);	//取得复位中断向量app = (App_Function)jumpaddress;//如果发现核心当前与PSP一起运行(在RTOS的线程中运行时使用的是PSP),则激活MSPif( CONTROL_SPSEL_Msk & __get_CONTROL( ) ){  /* MSP 未激活 */__set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ;	//激活MSP}//将MSP设置为用户应用程序向量表中的值__set_MSP(spinitval); //如果未在中断或没有使用系统的时候,那么这个生效  //将用户应用程序的向量表地址加载到SCB-> VTOR寄存器中。确保地址符合对齐要求 0x200的倍数(这一句可能没有意义)SCB->VTOR = ( uint32_t )app_addr; // 将用户应用程序的向量表地址加载到SCB-> VTOR寄存器中app(); //中转到指定程序地址}

以上代码参考连接:https://www.keil.com/support/docs/3913.htm

第三部分 固件HEX合并烧写说明

         产品出厂前,通常是两个固件要同时写入到MCU中,每一片要写两次,没效率。因此需要将多个HEX文件合并为一个文件,再一次性烧写,注:HEX文件是文本格式,可以用文本编辑器打开。操作方法如下:

  1. bootloader的最后一行(文件结束记录行)删除;
  2. APP1HEX文件内容直接COPYbootloader的后面,不能有空行;
  3. 同样把合并后的最后一行(原APP1 固件Hex文件的最后一行)删除;
  4. 把APP2的HEX文件内容直接COPY在合并文件的后面,不能有空行;
  5. 保存合并文件成为新的HEX文件,即可使用Jlink或Hex烧写工具直接烧写

以上合并文件烧写结果,验证通过。

特别说明:本说明是以STM32F103C8T6为列说明的,该MCU的FLASH扇区大小为1024。程序地址参数是否受MCU的FLASH扇区大小制约,目前并未验证。

验证工程下载地fh址:https://download.csdn.net/download/SCY820114/12915364