STM32
直播中

石利军

7年用户 890经验值
私信 关注
[问答]

STM32加入IAP后代码运行流程是怎样的

为什么会出现使用旧版复现不能跳转IAP程序的情况?
STM32加入IAP后代码运行流程是怎样的?

回帖(1)

王媛媛

2021-11-9 10:09:15
  问题描述
  使用内部FLASH暂时保存IAP升级时的程序数据,在IAP升级的过程中,需要首先擦除内部FLASH中一块空间写入跳转标志,然后跳入IAP, 工程中有使用到IWDG,喂狗间隔大约2S,同样的看门狗时间旧版不能跳转IAP。
  1.问题分析
  使用旧版复现不能跳转IAP程序情况,主要代码如图1:
  
  开始认为是跳转之前没有关闭所有中断的问题,在收到跳转命令之后关闭所有中断经过测试问题没有得到解决。
  
  图2 关闭所有中断
  从跳转为什么需要这么长时间,经过测试发现在跳转没完成之前,没有及时喂狗,导致驱动器复位,在初始化过程中加入printf打印 Reset 具体结果如图:
  
  图3 看门狗导致系统复位
  发现在发送完跳转指令之后系统又进行了一次系统复位。
  
  图4 扇区擦除
  需要擦除的扇区起始地址为0x080C0000查阅F407手册,了解到该扇区大小为128KB,
  查阅F407VET6手册发现
  
  图5 扇区地址分布图
  
  图6 F407数据手册节选图
  在PSIZE=x 8 时,典型擦除时间为2S,最大时间为4S,经过测试FLASH擦除时间为2.284S与数据手册基本符合。
  
  图7 示波器测试擦除时间图
  2.结论
  新版驱动器的2S定时时间也在跳转bootloader需要的时间边缘,经过验证将新版的2S看门狗下调,新版也会发生不能跳转bootloader的问题,将FLSASH擦除PSIZE=x 8改为PSIZE=x 32可以节约1S左右的时间。
  二、IAP简介
  IAP(In Application Programming)即在应用编程,IAP 是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。 通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。
  
  图1 STM32加入IAP后代码运行流程
  APP程序,不仅可以放到 FLASH 里面运行,也可以放到 SRAM 里面运行
  2.1.正常情况下代码执行流程
  
  从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求(发生中断),此时 STM32F4 强制将 PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。
  2.2 IPA之后代码运行情况
  
  与正常情况不同的是,在执行完IAP函数之后(即将执行APP函数),跳转至新APP程序的复位向量表开始执行,然后进入APP函数的死循环,在APP函数中发生中断请求之后,C 指针仍强制跳转到地址0X08000004 也就是Bootloader代码的中断向量表处,而不是APP代码的中断向量表,如图标号④所示;再根据APP函数当中的中断向量表偏移量,跳转到APP函数的中断向量表,如图标号⑤所示。
  2.3 远程烧录功能实现流程图
  
  前提是已经将Bootloader和APP两部分代码烧录进去,Bootloader程序首次需要通过烧录器进行烧录,APP函数可以通过烧录器烧录,也可以直接通过Bootloadr写入。
  2.4. APP程序起始地址设置方法
  
  此处设置的是APP代码的烧录地址和大小,只给IAP代码留了64K的代码空间,一般情况下Bootloader代码比较小,这个空间根据实际情况进行调整。通过串口或者其他协议接收到的新代码bin文件缓存在SRAM当中或者FLAS当中。(需要确保SRAM或者FLASh空间够用)
  u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000))); //绝对定位
  其中的USART_REC_LEN决定缓冲区的大小,根据代码大小确定。
  可以通过绝对定位来决定新代码的存储位置。
  2.5 中断向量表的偏移量设置方法
  在系统启动的时候,会首先调用 SystemInit 函数初始化时钟系统,同时SystemInit 还完成了中断向量表的设置
  #ifdef VECT_TAB_SRAM
  SCB-》VTOR = SRAM_BASE | VECT_TAB_OFFSET;
  /* Vector Table Relocation in Internal SRAM. */
  #else
  SCB-》VTOR = FLASH_BASE | VECT_TAB_OFFSET;
  /* Vector Table Relocation in Internal FLASH. */
  #endif
  VTOR 寄 存 器 存 放 的 是 中 断 向 量 表 的 起 始 地 址,默认情况下VECT_TAB_OFFSET未定义,我们设置的偏移量为0x10000,在 FLASH APP 的 main 函数最开头处添加如下代码实现中断向量表的起始地址的重设。
  SCB-》VTOR = FLASH_BASE | 0x10000; //FLASH 情况下的地址偏移
  SCB-》VTOR = SRAM_BASE | 0x1000;  //SRAM 情况下的地址偏移
  2.6.生成bin文件
  
  方法1:绝对路径方式
  方法2:相对路径方式
  
  $KARMARMCCbinfromelf.exe --bin --output=@L.bin !L
  更换电脑之后不需要任何更改在编译操作后,在xxx.uvprojx当前目录下,可看到生成的bin文件。
  2.7 指针跳转函数
  //iap跳转到应用程序段
  //appxaddr:用户代码起始地址。
  void iap_load_app(u32 appxaddr)
  {
  if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法。
  {
  jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
  MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
  jump2app(); //跳转到APP.
  }
  }
  //应用程序段跳转到iap
  //appxaddr:用户代码起始地址。
  void app_load_iap(u32 appxaddr)
  {
  if(((*(vu32*)appxaddr)&0x2FF00000)==0x20000000) //检查栈顶地址是否合法。
  {
  Close_Devicer_Iint();
  jump2iap=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
  MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
  jump2iap(); //跳转到APP.
  }
  }
  2.8 写FLASH过程
  //appxaddr:应用程序的起始地址
  //appbuf:应用程序CODE.
  //appsize:应用程序大小(字节)。
  void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
  {
  u16 t;
  u16 i=0;
  u16 temp;
  u32 fwaddr=appxaddr;//当前写入的地址
  u8 *dfu=appbuf;
  for(t=0;t《appsize;t+=2)
  {
  temp=(u16)dfu[1]《《8;
  temp+=(u16)dfu[0];
  dfu+=2;//偏移2个字节
  iapbuf[i++]=temp;
  if(i==1024)
  {
  i=0;
  STMFLASH_Write(fwaddr,iapbuf,1024);
  fwaddr+=2048;//偏移2048 16=2*8.所以要乘以2.
  }
  }
  if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去。
  }
  STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去。 }
  其余的通讯协议可以自定义,包括控制指令以及响应指令。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分