一、慎用固件库
1.HAL_FLASH_Program有问题,写入不正常
2.采用直接操作寄存器操作flash
好处是写好一次,后面基本不会出问题
我使用的是正点原子的寄存器版本,接口比较清晰,
但也有一个问题STMFLASH_EraseSector 操作内部有延时,会莫名卡在里面
发现HAL_FLASHEx_Erase的操作正常并且很快,
将STMFLASH_Write函数中擦除flash替换为HAL库操作方式,速度瞬间提升。
并且兼容了BANK1,BANK2的操作
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite)
{
FLASH_EraseInitTypeDef FlashEraseInit;
HAL_StatusTypeDef FlashStatus=HAL_OK;
u32 SectorError=0;
u8 status=0;
u32 addrx=0;
u32 endaddr=0;
if(WriteAddr<STM32_FLASH_BASE||WriteAddr%32)return; //非法地址
STMFLASH_Unlock(); //解锁
addrx=WriteAddr; //写入的起始地址
endaddr=WriteAddr+NumToWrite*4; //写入的结束地址
if(addrx<0X1FF00000) //只有主存储区,才需要执行擦除操作!!
{
while(addrx<endaddr) //扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
{
if(STMFLASH_ReadWord(addrx)!=0XFFFFFFFF)//有非0XFFFFFFFF的地方,要擦除这个扇区
{
#if 1
FlashEraseInit.TypeErase=FLASH_TYPEERASE_SECTORS; //擦除类型
FlashEraseInit.Sector=STMFLASH_GetFlashSector(addrx)%8;//要擦除的扇区
FlashEraseInit.Banks=addrx>=BANK2_FLASH_SECTOR_0?FLASH_BANK_2:FLASH_BANK_1; //操作 BANK1
FlashEraseInit.NbSectors=1; //一次只擦除一个扇区
FlashEraseInit.VoltageRange=FLASH_VOLTAGE_RANGE_3; //电压范围
if(HAL_FLASHEx_Erase(&FlashEraseInit,&SectorError)!=HAL_OK)
{
break; //发生错误了
}
SCB_CleanInvalidateDCache(); //清除无效的 D-Cach
#else
//status=STMFLASH_EraseSector(STMFLASH_GetFlashSector(addrx));
// if(status)break; //发生错误了
// SCB_CleanInvalidateDCache(); //清除无效的D-Cache
#endif
}else addrx+=4;
}
}
if(status==0)
{
while(WriteAddr<endaddr)//写数据
{
if(STMFLASH_Write8Word(WriteAddr,pBuffer))//写入数据
{
break; //写入异常
}
WriteAddr+=32;
pBuffer+=8;
}
}
STMFLASH_Lock();//上锁
}
二、测试函数
- 我们可以通过一个结构体将所有需要保存的参数放到一起管理并保存,这是最简单的方式
#define GP_LENTH sizeof(PARAM_T) //数组长度
#define SAVE_SIZE GP_LENTH/4+((GP_LENTH%4)?1:0) // 必须是4的整数倍
PARAM_T gp;
void SPIFlash_test(void)
{
memset(&gp,3,sizeof(gp));
STMFLASH_Write(FLASH_SAVE_ADDR,(u32*)&gp,SAVE_SIZE);
STMFLASH_Read(FLASH_SAVE_ADDR,(u32*)&gp,25);
memset(&gp,1,sizeof(gp));
STMFLASH_Read(FLASH_SAVE_ADDR,(u32*)&gp,SAVE_SIZE);
}
2.通常可以将结构体的首字节,末字节作为验证是否数据已经保存或正确保存的标志