STM32 内部FLASH和外部FLASH两种方式实现IAP升级

芯片型号STM32F103RET6,flash大小512K,起始地址0x08000000

一般说STM32内部FLASH就是指主存储器区域
在这里插入图片描述
【注】此实验中启动方式设置为复位后从主闪存存储器启动(BOOT0拉低)

IAP实现流程为,上电复位后,先执行bootloader(启动程序),在bootloader中判断是否含有要更新升级的文件,若有,则将待升级的文件加载到APP,加载完成后跳转执行;若没有则直接跳转至现有APP程序中执行

基于外部flash的IAP升级

外部flash芯片型号为W25Q32,大小为32M。

将内部flash分为两部分,Bootloader(启动程序) + APP(应用程序),32K分配给bootloader;剩余的512-32=480k给应用程序(可根据自选芯片flash大小和应用程序需要分配)。

APP部分

  1. 设置app起始地址为0x08008000,大小为0x78000,即480k
    在这里插入图片描述

下载程序选项设置为擦除扇区,这样更新app程序时,不会擦除bootloader部分。
在这里插入图片描述

  1. 对中断向量表重定向

    #define NVIC_VectTab_FLASH ((uint32_t)0x08000000) //flash起始地址

    #define VECTOR_TABLE 0x8000 // 向量表偏移位置

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, VECTOR_TABLE);

  2. 外部32M flash(W25Q32)空间分配

    typedef struct
    {
    u8 updateFlag; //升级成功标志
    u8 Edition[10]; //升级文件程序版本号
    u8 CurrentCheckCode; //当前校验值
    u8 EndCheckCode; //结束总校验值
    u16 CurNumber; //当前传输包号
    u16 TotalNumber; //总包数
    u32 CurSize; //当前包大小
    u32 FileSize; //升级文件总大小
    u32 addr; //外部flash写入地址
    u8 FileName[40]; //升级文件名
    }_struUpdateFile ;

    _struUpdateFile TransmissionFile; //正在传输的升级文件描述信息

    _struUpdateFile MemoryFile; //本地存储的升级文件描述信息

    0x00000000-0x00004fff(20k)存放升级文件的文件名,文件大小,文件包数、程序版本号,当前升级包在外部flash中的存放地址、总校验码等和升级文件相关的描述信息(MemoryFile)
    0x00005000-0x0003700(200k)存放要升级的bin文件

  3. 服务器下发升级包及设备存储流程实现

  • 服务器下发升级文件相关描述信息,设备收到后,先读取外部flash中0x00000000中的存储的内容,看即将升级的文件是否和本地保存的文件信息一致,若不一致,则表示是新的升级文件,将外部flash中之前保存的文件描述信息和保存的升级文件清除,将服务器下发的升级文件描述信息保存,这样做的目的是防止升级过程中出现的断电等异常情况导致的升级终止,可以从上次中断的包数和存储地址继续升级;若一致,则表示是同一升级文件,接着上一次中断的升级包继续升级(外部FLASH采取SPI读写方式)

    W25Q32_Read(0x00000000, (u8*)&MemoryFile, sizeof(MemoryFile)); //将本地存储的文件描述信息读出,和正在升级的文件描述做对比,确定是不是同一个升级文件

    W25Q32_Write(0x00000000, (u8*)&TransmissionFile, sizeof(TransmissionFile));//新的升级文件,则将描述信息保存至本地

  • 服务器开始下发包含校验和的第一个升级包(一般为1k),设备收到后,计算校验和,和服务器下发的校验和对比,一致则表示传输无误,保存至起始地址为0x00005000的外部flash中,并将存储地址自加后(一般为1k)保存,随后应答服务器请求下一个升级包。不一致则表示传输有误,不保存,应该服务器再次请求该升级包

    W25Q32_Write(TransmissionFile.addr, data, TransmissionFile.CurSize); //将收到的单个升级包存储在外部flash中

    TransmissionParameter.addr += TransmissionFile.CurSize; //外部flash存储地址自加

    TransmissionParameter.CurNumber++; //准备请求下一个升级包

    W25Q32_Write((0x00000000, (u8*)&TransmissionFile, sizeof(TransmissionFile)); //将更新的flash地址、当前校验码、 传输 包数等文件信息保存,以防止升级中断后,可以接着上次升级包继续升级

  • 依次循环…

  • 下发最后一个包(当前传输包数和总包数相等),将所有包的校验和一起累加校验,和文件描述信息中的总检验码对比,一致则表示整个升级过程没有错误,置位升级成功,可以准备软件复位,从bootloader开始执行,准备加载新程序;不一致则表示存在丢包,清除保存的所有升级包,清零升级成功标志,请求重新升级。

    TransmissionParameter.updateFlag = 1 //升级成功

Bootloader部分

  1. 设置bootloader起始地址为0x08000000,大小为0x8000,即32k
    在这里插入图片描述

  2. 下载功能设置为擦除扇区,即每次更新bootloader程序只会擦除32k的BootLoader空间,不会影响app部分,要不然选择Erase Full C会将整个512kFlash全部擦除
    在这里插入图片描述

  3. Bootloader流程实现

  • 从外部flash中读取文件描述信息,若检测到升级成功标志,表示外部flash中有待升级的新程序,准备将待升级的文件加载到内部flash中;若为0,则表示无升级程序,直接跳往app执行
    W25Q32_Read(0x00000000, (u8*)&MemoryFile, sizeof(MemoryFile));

  • 解锁内部flash(过程可参考STM32内置闪存读写

    FLASH_Unlock();

  • 将外部flash中的程序包按包(设为1k)读取,并写入stm32内部flash中

    readAddr = 0x00005000;//外部flash升级程序存储起始地址

    writeAddr = 0x08008000;//app程序运行起始地址

    W25Q32_Read(readAddr,updateDataBuffer,1024);//从外部flash中读取第一个包

    Flash_Write(writeAddr , updateDataBuffer, 1024); //将第一个包加载到stm32内部flash中
    readAddr +=1024;//地址累加

    writeAddr += 1024;

  • 依次循环,直到从外部flash加载完最后一个升级包

  • 加载完成,跳转到APP程序执行

    Iap_Load_App(0x08008000);

    void Iap_Load_App(u32 appxaddr)

    {

    if((((u32)appxaddr)&0x2FFF0000)==0x20000000)//1.

    {

    jump2app=(iapfun)(vu32)(appxaddr+4); //2.
    MSR_MSP((vu32)appxaddr); //3.
    jump2app(); //4.
    }
    }

    __asm void MSR_MSP(u32 addr)

    {

    MSR MSP, r0

    BX lr
    }

备注:1.(u32)appxaddr :取0x08008000开始到0x08008003中4个字节的值,APP把中断向量表重定向至0x08008000,而中断向量表中第一个放的就是栈顶地址的值,栈顶地址和0x2FFF0000相与是否在0x20000000-0x2000FFFF之间,STM32RET6的SRAM是64K(如果SRAM是32k(0x20000000-0x20007FFF),则与值为0x2FFF8000),就可以判断判断栈顶地址的值是否正确,来判断应用程序是否已经加载成功。因为APP的启动文件(startup_stm32f10x_hd.s)刚开始就去初始化栈空间,如果栈顶值对了,说明程序已经加载成功

2.appxaddr+4即为0x08008004,里面存放的是中断向量表第二项复位中断,此时jump2app指向复位中断函数入口地址
在这里插入图片描述

3.初始化app堆栈指针(用户代码区第一个字用于存放栈顶地址)

4.跳转到APP,执行复位函数

基于内部flash的IAP升级

【说明】和外部flash升级相比,只是把升级文件存储到外部flash,改为存储到内部flash

将内部flash分为三部分,Bootloader(启动程序) + APP(应用程序) + Store(存储升级文件)+ 升级文件描述,

32K分配给bootloader(0x8000: 0x08000000-0x08007FFF)

200k给应用程序,(0x32000: 0x08008000-0x08039FFF)

200k用存储升级文件(0x32000: 0x0803A000-0x0806BFFF) //这样的好处是,升级出现意外时,不会

20k用于存储升级文件描述(0x5000:0x0806C000 - 0x08070FFF)

(可根据自选芯片flash和应用程序大小需要分配)。

APP部分

  1. 设置app起始地址为0x08008000,大小为0x32000,即200k
    在这里插入图片描述
    下载程序选项设置为擦除扇区,这样更新app程序时,不会擦除bootloader和存储的升级文件部分(插图和外部FLASH升级一致)

  2. 对中断向量表重定向

    #define NVIC_VectTab_FLASH ((uint32_t)0x08000000) //flash起始地址

    #define VECTOR_TABLE 0x8000 // 向量表偏移位置

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, VECTOR_TABLE);

  3. 服务器下发升级包及设备存储流程实现

    操作步骤基本和外部FLASH升级一致,写入外部FLASH改为写入内部FLASH

  • 服务器下发升级文件相关描述信息,设备收到后,先读取stm32flash中0x0806C000中的存储文件描述信息,看即将升级的文件是否和本地保存的文件信息一致,若不一致,则表示是新的升级文件,将flash中之前保存的文件描述信息和保存的升级文件清除,将服务器下发的升级文件描述信息保存,这样做的目的也同样是防止升级过程中出现的断电等异常情况导致的升级终止,可以从上次中断的包数和存储地址继续升级;若一致,则表示是同一升级文件,接着上一次中断的升级包继续升级
    FLASH_Unlock(); //解锁flash,否则会写入不了

    Flash_Read(0x0806C000, (u8*)&MemoryFile, sizeof(MemoryFile)); //将本地存储的文件描述信息读出,和 //正在升级的文件描述做对比,确定是不是同一个升级文件

    Flash_Write(0x0806C000, (u8*)&TransmissionFile, sizeof(TransmissionFile));//新的升级文件,则将描述信息保存至本地

  • 服务器开始下发包含校验和的第一个升级包(一般为1k),设备收到后,计算校验和,和服务器的校验和对比,一致则表示传输无误,保存至起始地址为0x0803A000(存储APP首地址)的flash中,并将存储地址自加后(一般为1k)保存,随后应答服务器请求下一个升级包。不一致则表示传输有误,不保存,应该服务器再次请求该升级包
    Flash_Write(TransmissionFile.addr, data, TransmissionFile.CurSize); //将收到的单个升级包存储在外部flash中

    TransmissionParameter.addr += TransmissionFile.CurSize; //外部flash存储地址自加

    TransmissionParameter.CurNumber++; //准备请求下一个升级包

    Flash_Write((0x0806C000, (u8*)&TransmissionFile, sizeof(TransmissionFile)); //将更新的flash地址、当前校验码、 传输 包数等文件信息保存,以防止升级中断后,可以接着上次升级包继续升级

  • 依次循环…
    下发最后一个包(当前传输包数和总包数相等),将所有包的校验和一起累加校验,和文件描述信息中的总检验码对比,一致则表示整个升级过程没有错误,置位升级成功,可以准备软件复位,从bootloader开始执行,准备加载新程序;不一致则表示存在丢包,清除保存的所有升级包,清零升级成功标志,请求重新升级。
    TransmissionParameter.updateFlag = 1 //升级成功

Bootloader部分

  1. 设置bootloader起始地址为0x08000000,大小为0x8000,即32k,(插图和外部FLASH升级的插图一致)

  2. 下载功能设置为擦除扇区,即每次更新bootloader程序只会擦除32k的BootLoader空间,不会影响app和存储的升级文件,要不然选择Erase Full C会将整个512kFlash全部擦除

  3. Bootloader流程实现

  • 从外部flash中读取文件描述信息,若检测到升级成功标志,表示外部flash中有待升级的新程序,准备将待升级的文件加载到内部flash中;若为0,则表示无升级程序,直接跳往app执行
    W25Q32_Read(0x00000000, (u8*)&MemoryFile, sizeof(MemoryFile));

  • 解锁内部flash(过程可参考STM32内置闪存读写)

    FLASH_Unlock();

  • 将外部flash中的程序包按包(设为1k)读取,并写入stm32内部flash中

    readAddr = 0x00001000;//外部flash升级程序存储起始地址

    writeAddr = 0x08008000;//app程序运行起始地址

    W25Q32_Read(readAddr,updateDataBuffer,1024);//从外部flash中读取第一个包

    Flash_Write(writeAddr, updateDataBuffer, 1024); //将第一个包加载到stm32内部flash中

  • 依次循环,从外部flash加载完最后一个升级包

  • 加载完成,跳转到APP程序执行

Iap_Load_App(0x08008000);

内部和外部FLASH升级的选择

  • 内部flash升级:
    读写速度快、节约外部flash芯片的成本;对stm32flash空间要求高,价格和空间大小成正比

  • 外部flash升级:
    flash分区更简单,对stm32flash空间大小要求不高;读写速度慢

可以根据实际情况自行选择