STM32F407 IAP升级
前言
1、BOOT完成软件更新;
2、APP程序实现备份代码的读入和保存。
一、内存分配说明
1、使用STM32F407VET6内部Flash大小512K,一共分为8个扇区
/*-来自.h文件:stm32f4xx_flash.h-*/
#define FLASH_Sector_0 ((uint16_t)0x0000) 扇区0:16K
#define FLASH_Sector_1 ((uint16_t)0x0008) 扇区1:16K
#define FLASH_Sector_2 ((uint16_t)0x0010) 扇区2:16K
#define FLASH_Sector_3 ((uint16_t)0x0018) 扇区3:16K
#define FLASH_Sector_4 ((uint16_t)0x0020) 扇区4:64K
#define FLASH_Sector_5 ((uint16_t)0x0028) 扇区5:128K
#define FLASH_Sector_6 ((uint16_t)0x0030) 扇区6:128K
#define FLASH_Sector_7 ((uint16_t)0x0038) 扇区7:128K
2、自己的分配说明
我自己分配BOOT使用48K空间,APP程序使用208K,备份空间256K空间,分配的很大目前程序没有使用这么大的空间,大家可以备份控制多分配几个这个可以选择跳转多个程序。
//宏定义
/*---------------------------------------------------------------------------------------------------*/
/* BOOT(16K+16K+16K)| APP(16K+64K+128K) | Backups(128K+128K) */
/*---------------------------------------------------------------------------------------------------*/
#define ADDR_FLASH_SECTOR_0 ((u32)0x08000000) //扇区0起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_1 ((u32)0x08004000) //扇区1起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_2 ((u32)0x08008000) //扇区2起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_3 ((u32)0x0800C000) //扇区3起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_4 ((u32)0x08010000) //扇区4起始地址, 64 Kbytes
#define ADDR_FLASH_SECTOR_5 ((u32)0x08020000) //扇区5起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_6 ((u32)0x08040000) //扇区6起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_7 ((u32)0x08060000) //扇区7起始地址, 128 Kbytes
#define UPDATA_BOOT_ADDRESS 0x08000000//BOOT程序起始地址
/* BOOT程序 空间48K */
#define UPDATA_APP_ADDRESS 0x0800C000//APP程序起始地址
/* APP程序 空间208K */
#define UPDATA_BACKUPS_ADDRESS 0x08040000//备份程序起始地址
/* Backups程序 空间256K */
#define UPDATA_TIME_ADDRESS 0x0805c000//升级时间(4字节)
#define UPDATA_SIZE_ADDRESS 0x0805c004//升级文件大小(4字节)
#define UPDATA_CRC_ADDRESS 0x0805c008//升级文件校验(4字节)
#define UPDATA_FlAG_ADDRESS 0x0805c00c//升级标注(4字节)
/* 其他数据 空间16K */
二、BOOT程序
1.功能说明
首先说明我程序的升级方式,APP程序在运行时上位机发送bin文件给MCU串口接收,MCU接收到文件存入到备份区域,然后关闭设备重新上电,上电后运行BOOT程序检查是否需要更新,读到更新标志校验备份区域的程序是否正确,正确后进行程序升级擦除APP程序将备份程序写入最后跳转到APP程序再次运行,此时运行的是新程序。
2.BOOT程序
代码如下(示例):
/**************************
* 设备主任务
* 参数:00-通用设备
* 返回值:NULL
**************************/
void DeviceTask(BYTE TYPE)
{
while(1)
{ /*-读取升级标志是否需要升级-*/
if(STMFLASH_ReadWord(UPDATA_FlAG_ADDRESS) == UPDATA_ALLOW_FLAG)
{
if(Load_App() == TRUE) /*-固件写入成功-*/
NVIC_SystemReset();
else /*-固件写入失败-*/
JumpTo_APP(UPDATA_APP_ADDRESS);
}
else /*-跳转APP程序-*/
JumpTo_APP(UPDATA_APP_ADDRESS);
}
}
/*-其他相关函数-*/
/*****************************************
* 加载APP程序
* 参数:null
* 返回值:TRUE-加载完成,FALSE-加载失败
*****************************************/
BOOL Load_App(void)
{
DWORD Offset;
DWORD FirmWareSize; //固件大小
DWORD FirmWareCheck; //固件校验
DWORD FirmWareData; //固件大小
DWORD TempData; //临时数据
/*-读取固件-*/
FirmWareSize = STMFLASH_ReadWord(UPDATA_SIZE_ADDRESS);
if(FirmWareSize == 0x00000000 || FirmWareSize == 0xFFFFFFFF)
{
/*-擦除使用的区域-*/
//Flash_EraseSectorUser();
/*-固件异常-*/
TempData = UPDATA_FAIL_FLAG;
STMFLASH_Write(UPDATA_FlAG_ADDRESS, (unsigned int*)&TempData, 1);
return FALSE;
}
FirmWareCheck = 0x00000000;
/*-检查数据正确性-*/
for(Offset = 0; Offset < FirmWareSize; Offset += 4)
{
/*读取一遍备份数据累计校验和,对比校验是否正确,异常调回原程序不进行升级-*/
TempData = STMFLASH_ReadWord(UPDATA_BACKUPS_ADDRESS + Offset);
FirmWareCheck += (BYTE)(TempData>>24) & 0xFF;
FirmWareCheck += (BYTE)(TempData>>16) & 0xFF;
FirmWareCheck += (BYTE)(TempData>>8) & 0xFF;
FirmWareCheck += (BYTE)TempData & 0xFF;
}
if(FirmWareCheck != STMFLASH_ReadWord(UPDATA_CRC_ADDRESS))
{
/* 固件异常:
* 1、传输不完整或者文件损坏
* 2、累加校验不正确
* 处理方式:
* 1、写入升级失败标志
* 2、擦除备份区全部数据
*/
/*-擦除使用的区域-*/
//Flash_EraseSectorUser();
TempData = UPDATA_FAIL_FLAG;
STMFLASH_Write(UPDATA_FlAG_ADDRESS, (unsigned int*)&TempData, 1);
return FALSE;
}
/*-升级程序-*/
for(Offset = 0; Offset < FirmWareSize; Offset += 4)
{
/*读取一遍备份数据累计校验和,对比校验是否正确,异常调回原程序不进行升级-*/
FirmWareData = STMFLASH_ReadWord(UPDATA_BACKUPS_ADDRESS + Offset);
/*-写入新备份程序-*/
STMFLASH_Write(UPDATA_APP_ADDRESS + Offset, (unsigned int*)&FirmWareData, 1);
}
/*-写入升级完成标志-*/
TempData = UPDATA_SUCCESS_FLAG;
STMFLASH_Write(UPDATA_FlAG_ADDRESS, (unsigned int*)&TempData, 1);
return TRUE;
}
/**************************
* 跳转APP程序
* 参数:APP程序起始地址
* 返回值:null
**************************/
void JumpTo_APP(DWORD appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
jump2app(); //跳转到APP.
}
}
/***************************
* 写STM32F407内部Flash数据
***************************/
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)
{
FLASH_Status status = FLASH_COMPLETE;
u32 addrx=0;
u32 endaddr=0;
if(WriteAddr < UPDATA_BOOT_ADDRESS || WriteAddr%4)
return; //非法地址
FLASH_Unlock(); //解锁
FLASH_DataCacheCmd(DISABLE); //FLASH擦除期间,必须禁止数据缓存
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
addrx=WriteAddr; //写入的起始地址
endaddr=WriteAddr+NumToWrite*4; //写入的结束地址
if(addrx<0X1FFF0000) //只有主存储区,才需要执行擦除操作!!
{
while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
{
if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
{
status=FLASH_EraseSector(STMFLASH_GetFlashSector(addrx),VoltageRange_3);//以4字节为单位进行擦除
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();//上锁
}
未完再续。。。