一篇文章搞定虚拟串口IAP

    在上一篇博客中我提到了STM32F407+虚拟串口在线升级的基本原理,这一篇我讲一下关于一些,在虚拟串口在线升级的代码和注意的一些细节。
    在线升级原理不清楚的请移步参考上一篇[我用一张图了解虚拟串口IAP](https://blog.csdn.net/cs111211/article/details/105142049)

1.升级代码讲解

  1)拷贝app代码到flash。
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite) 
{ 
  FLASH_Status status = FLASH_COMPLETE;
 u32 addrx=0;
 u32 endaddr=0; 
  if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return; //非法地址
 FLASH_Unlock();         //解锁
  FLASH_DataCacheCmd(DISABLE);//FLASH擦除期间禁止数据缓存。
   
 addrx=WriteAddr;    //写入起始地址
 endaddr=WriteAddr+NumToWrite*4; //写入结束地址
 if(addrx<0X1FFF0000)   //只有主存储区才执行擦除操作 !
  while(addrx<endaddr)  //对非0xFF的地方先擦除
   if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//擦除非0XFFFFFFFF地方
   {   
    status=FLASH_EraseSector(STMFLASH_GetFlashSector(addrx),VoltageRange_3);//VCC=2.7~3.6V之间
    if(status!=FLASH_COMPLETE)break; //发生错误了
   }else addrx+=4;
  } 
 }
 if(status==FLASH_COMPLETE)
 {
  while(WriteAddr<endaddr)//写数据
  {
   if(FLASH_ProgramWord(WriteAddr,*pBuffer)!=FLASH_COMPLETE)//写入数据
   { 
    break; //写数据异常
   }
   WriteAddr+=4;
   pBuffer++;
  } 
 }
  FLASH_DataCacheCmd(ENABLE); //FLASH擦除结束,开启数据缓存
   FLASH_Lock();//上锁
} 

u32 iapbuf[512];  //缓冲
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);// 
}

2)代码的使用方法
上面这部分代码是将app写入到FLASH的指定区域的方法,需要在工程里把flash的库加到工程中。
也就是stm32f4xx_flash.c。
这些函数是用来写app的,我们需要调用接口是void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)。这里有三个输入的参数:

  1. appxaddr:是写入flash的指定地址。具体哪些地址可写入可以参考我的写入初始地址。我是把app栈顶地址写到了。#define FLASH_APP1_ADDR 0x08010000这个地址。因为boot的程序不会太大,所以我们给boot程序预留了64k的内存也就是0x10000 ,flash的起始地址是0x08000000所以第一个app的起始地址是0x080000000+0x10000=0x08010000。当然这里可以下载多个app到flash里。(只要FLASH内存够)具体哪些地方可以存app可具体查看芯片datasheet里的flash扇区的地址。
  2. appbuf:是app程序的缓冲区,也就是app的code()
  3. appsize:app程序的大小(字节)

2.跳转代码以及注意点

先上代码,转跳到app的程序

void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法
{
 __disable_irq();//关闭所有中断
 RCC_AHB2PeriphResetCmd(RCC_AHB2Periph_OTG_FS, ENABLE); 
 RCC_AHB2PeriphResetCmd(RCC_AHB2Periph_OTG_FS, DISABLE); //复位USB OTG
 TIM_Cmd(TIM3,DISABLE);//定时器失能
 jump2app=(iapfun)*(vu32*)(appxaddr+4);  //代码区第二个字是程序开始地址(复位地址) 
 MSR_MSP(*(vu32*)appxaddr);     //初始化app堆栈指针(第一个字用于存放栈顶地址)
   jump2app();         //转跳到app
}
}   

上面这段代码是转跳到应用程序的代码,这里要画个重点。
1)关闭所有中断
在转跳到app程序前要把已经开启的所有中断关闭掉。因为升级只上一次电,如果不关闭中断的话,中断寄存器什么的都没有变。在跳到app程序后中断仍然在,但是中断函数却没了,程序不知道中断是来自哪个地方的,所以会导致程序卡死。所以使用__disable_irq();//关闭所有中断
2)失能已经开启的外设
这个和上一条的注意点要类似,因为在我的bootloader 程序中用到了定时器和usb虚拟串口,而app程序中也用到了定时器和虚拟串口。所以要想在app里也能够正常使用这些外设的话必须在转跳之前将这些外设失能,而失能外设的方法就是disable它的时钟。
RCC_AHB2PeriphResetCmd(RCC_AHB2Periph_OTG_FS, ENABLE);
RCC_AHB2PeriphResetCmd(RCC_AHB2Periph_OTG_FS, DISABLE); //这两句就是复位USB OTG
TIM_Cmd(TIM3,DISABLE);//是定时器失能。
3)实现跳转
jump2app=(iapfun)(vu32)(appxaddr+4); 这句对应的是app中断向量表的第二项,复位地址强制转化为函数指针
MSR_MSP((vu32)appxaddr); //初始化app堆栈指针(第一个字用于存放栈顶地址)
这句话是设置主函数栈指针。
最后一句 jump2app(); //转跳到app调用函数,去app复位地址去执行复位操作

3.app程序的生成方法

1)设置栈顶地址
APP程序在主函数变量定义之后加入这句话
SCB->VTOR=0x8000000|0X10000;
这句话的意思是app栈顶地址是0x08010000,同理可设其他地址。
2)设置add
在target for options中的target选项中照下图编辑,在IROM1后的第一个框后的地址改为0x08010000这个地址是app的起始地址,第二个框改成0xf0000这个框是app程序的大小我这里用的flash是1m的所以size=1M-64k=0xf0000。这两个框里面的内容必须是0x400的整数倍。
在这里插入图片描述
4)生成bin文件
在设置完地址之后,我们需要一个把程序转化成bin文件的东西,在keilv5里面可以实现这个功能首先找到 fromelf.exe的路径然后在找到你output里的文件名我这里工程是USART,然后编译后会生成一个.axf后缀的文件.
在这里插入图片描述
然后在user选项中的Run#1前点上勾,在后面的框填入如下格式的一段路径
在这里插入图片描述
E:\MDK5\ARM\ARMCC\bin\fromelf.exe --bin -o …\OBJ\USART.bin …\OBJ\USART.AXF
前面的是 fromelf.exe的路径\ --bin -o …\OBJ\USART.bin …\OBJ\USART.AXF
然后点ok,点击编译然后在输出区出现下面的提示说明成功生成bin文件。在OBJ文件夹中可以找到USART.bin这个文件。
在这里插入图片描述

4.app程序的一些注意点

1.在app中开启中断,需要在mian开头的位置,开启中断以及初始化用到的外设和使能相应的外设时钟。
2.user里的路径一定要输入正确不然会报错。我的是在 E:\MDK5\ARM\ARMCC\bin\fromelf.exe路径下,根据自己的MDK装的位置不同这个会不同。
3.一定要在程序中加入 SCB->VTOR=0x8000000|0X10000;。不然不升级之后代码不能运行。

发布了3 篇原创文章 · 获赞 2 · 访问量 112

猜你喜欢

转载自blog.csdn.net/cs111211/article/details/105533524