一,备用区app
这个看个人,备用区可以固定放哪个app。
我是直接升级成功就将主程序app放备用区里了,通过flash读写直接把整块复制粘贴进备用区app。如果升级失败就将备用区的flash数据复制到主程序区。所以至始至终我们的跳转程序的起始地址都是0x8003000.
二,flash接口,看个人这个咋写的,我随便抄的
1.flash读数据
//读取指定地址的半字(16位数据)
//faddr:读地址(此地址必须为2的倍数!!)
//返回值:对应数据.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
return *(vu16*)faddr;
}
//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
{
u16 i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
ReadAddr+=2;//偏移2个字节.
}
}
2.flash写数据
#if STM32_FLASH_WREN //如果使能了写
//不检查的写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u16 i;
for(i=0;i<NumToWrite;i++)
{
FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
WriteAddr+=2;//地址增加2.
}
}
//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else
#define STM_SECTOR_SIZE 2048
#endif
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
s16 STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u32 secpos; //扇区地址
u16 secoff; //扇区内偏移地址(16位字计算)
u16 secremain; //扇区内剩余地址(16位字计算)
u16 i;
u32 offaddr; //去掉0X08000000后的地址
if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))
{
return 1;//非法地址
}
FLASH_Unlock(); //解锁
offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.
secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
if(NumToWrite<=secremain)
{
secremain=NumToWrite;//不大于该扇区范围
}
while(1)
{
STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
for(i=0;i<secremain;i++)//校验数据
{
if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
for(i=0;i<secremain;i++)//复制
{
STMFLASH_BUF[i+secoff]=pBuffer[i];
}
STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.
if(NumToWrite==secremain)break;//写入结束了
else//写入未结束
{
secpos++; //扇区地址增1
secoff=0; //偏移位置为0
pBuffer+=secremain; //指针偏移
WriteAddr+=(secremain*2); //写地址偏移
NumToWrite-=secremain; //字节(16位)数递减
if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
else secremain=NumToWrite;//下一个扇区可以写完了
}
return 0;
};
FLASH_Lock();//上锁
}
#endif
3.flash擦除
//按字节数除以2擦除
void STMFLASH_Erase(u32 EraseAddr,u16 NumToErase)
{
u32 secpos; //扇区地址
u16 secoff; //扇区内偏移地址(16位字计算)
u16 secremain; //扇区内剩余地址(16位字计算)
u16 i;
u32 offaddr; //去掉0X08000000后的地址
if(EraseAddr<STM32_FLASH_BASE||(EraseAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
FLASH_Unlock(); //解锁
offaddr=EraseAddr-STM32_FLASH_BASE; //实际偏移地址.
secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小
if(NumToErase<=secremain)
secremain=NumToErase;//不大于该扇区范围
while(1)
{ FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
if(NumToErase==secremain)
break; //擦除结束了
else //擦除未结束
{
secpos++; //扇区地址增1
secoff=0; //偏移位置为0
EraseAddr+=secremain; //地址偏移
NumToErase-=secremain; //字节(16位)数递减
if(NumToErase>(STM_SECTOR_SIZE/2))
secremain=STM_SECTOR_SIZE/2;//下一个扇区还是擦除不完
else secremain=NumToErase;//下一个扇区可以擦除完了
}
};
FLASH_Lock();//上锁
}
三,主程序区到备用程序区的复制
// 函数:从主应用程序复制到备份应用程序
void copy_app_to_backup(void) {
u16 key[256]={0};
u32 k = 0;
STMFLASH_Erase(BAK_ApplicationAddress,0xFFFF) ;
for(k = 0; k < 256; k++)
{
STMFLASH_Read(ApplicationAddress + 512*k ,key,256);
STMFLASH_Write( BAK_ApplicationAddress + 512*k ,key,256) ;
*key=0;
}
}
这里要注意数组大小不能太大否则可能内存不足啥的不能运行。
ApplicationAddress:0x8003000
BAK_ApplicationAddress:0x8023000
四,主程序区到备用程序区的复制
// 函数:从备份应用程序复制到主程序
void copy_backup_to_app(void) {
u16 key[256]={0};
u32 k = 0;
STMFLASH_Erase(ApplicationAddress,0xFFFF) ;
for(k = 0; k < 256; k++)
{
STMFLASH_Read(BAK_ApplicationAddress + k*512 ,key,256);
STMFLASH_Write( ApplicationAddress + k*512 ,key,256) ;
*key=0;
}
}
大小我们之前就订好了0x20000,所以很好确认,进行一些计算就可以了。
ApplicationAddress:0x8003000
BAK_ApplicationAddress:0x8023000
五,第一次烧录程序判断
// 检查是否第一次烧录flash
static u8 CheckfirstFlash() {
u16 key[3]={0};
STMFLASH_Read(BAK_ApplicationAddress,key,3);
if(key[0] == 0xFFFF && key[1] == 0xFFFF && key[2] == 0xFFFF)
{
return 1;
}
return 0;
}
BAK_ApplicationAddress:0x8023000
直接查备用区的flash数据,看是否有东西,没有的话执行copy_app_to_backup();这样就能确保备用区一直有备用程序。