最近做的项目涉及到利用单片机FLASH保存参数,由于我使用的单片机的扇区较大(64K),且最小擦除单位就是扇区,所以每次擦除浪费了大量时间,为了避免产品在掉电过程中保存数据,导致保存时间不够,参数保存错误,因此对这部分逻辑做了优化
我将保存的扇区分成N个块,所以仅仅在这N个部分都写满的情况下才去进行flash擦除,在每次数据保存的开头存储了一个标志位,每次上电后,遍历每个块的开头来检测是否有标志位,直达检测到没有标志位的块,然后读取最后一个带有标志位的块, 在进行数据校验,校验正确,程序正常跑。保存的数据还记录了下次保存的块,每次掉电之后直接保存数据到对应的块,而不需要重新擦除,这大大缩短了掉电所需要的保存时间,且大大提高了FLASH寿命
以下是代码示例
//上电查找上一次存储的数据
bool find_last_storaged(GLOBAL *gt)
{
bool res = false;
uint8_t i = 0;
DISABLE_INT();
for(i = 0; i < BLOCK_NUM; i++)
{
if(*((uint32_t *)(FLASH_USER_START + BLOCK_SIZE * i)) != GLOBAL_FLAG)
{
break;
}
}
if(i == 0)
res = false;
else
memcpy(gt, (void*)FLASH_USER_START + (BLOCK_SIZE *(i-1)), sizeof(GLOBAL));
ENABLE_INT();
return res;
}
//正常工作全局保存,至少保持最后一块空着,掉电保存用
void API_Save_GlobalSelections(GLOBAL *gt)
{
gt->u32SaveCrc = 0;
uint32_t target_block = gt->u32saveBlock;
if((0 == target_block) || ((BLOCK_NUM - 1) == target_block)){
bsp_flash_erase(FLASH_USER_START);
gt->u32saveBlock = 1;
gt->u32SaveCrc = API_GetCrc(gt, sizeof(GLOBAL));
bsp_flash_write(gt, FLASH_USER_START, sizeof(GLOBAL));
}else{
if(target_block < BLOCK_NUM - 1)
{
gt->u32saveBlock = (gt->u32saveBlock + 1) % BLOCK_NUM;
gt->u32SaveCrc = API_GetCrc(&g_tCtrl, sizeof(g_tCtrl));
bsp_flash_write(gt, FLASH_USER_START + (target_block * BLOCK_SIZE), sizeof(GLOBAL));
}
}
}
//掉电保存
void API_powerdown_SaveGlobal(GLOBAL *gt)
{
uint32_t target_block = gt->u32saveBlock;
gt->u32SaveCrc = 0;
gt->u32saveBlock = (gt->u32saveBlock + 1) % BLOCK_NUM;
gt->u32SaveCrc = API_GetCrc(gt, sizeof(GLOBAL));
bsp_flash_write(gt, FLASH_USER_START + (target_block * BLOCK_SIZE), sizeof(GLOBAL ));
}
注:在每次读取上电的数据后,如果检测到没有数据块可写了,那就进行预擦除,避免掉电之后再进行扇区擦除,浪费时间