STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入
- 参考例程
- 例程说明
- 一、存储到Flash中的数据
- 二、Flash最底层操作(解锁,加锁,擦除,读写)
- 三、从Flash块中读取数据
- 五、测试验证
参考例程
STM32G0xx HAL和LL库Flash读写擦除操作
STM32G030Cx HAL库Flash擦除或编程操作出错的解决办法
例程说明
1.Flash存储数据采用页分块方式,实现一次擦除多次写入,每块128Byte总共16块,当存满16块后擦除一次
2.将数据写入Flash前,需要前后对比数据,只有数据发生变化才能写入Flash中
3.从Flash读取数据后,进行CRC校验,校验不通过往前继续读取数据,然后重新校验数据
4.将数据写如Flash前需要确保改块都是0xFF,保证写入数据成功
一、存储到Flash中的数据
说明:
- Header用来标识存储的头部信息,在Flash读取或写入时用到
- ubRes用来作块对齐使用,默认进行128Byte对齐
#define FLASH_STORE_PARM_HEADER_TAG (0x6C5A) //固定头信息
#define FLASH_STORE_PARM_BLOCK_SIZE (128) //块大小
#define FLASH_STORE_PARM_BLOCK_COUNT (u16)(FLASH_PAGE_SIZE / FLASH_STORE_PARM_BLOCK_SIZE) //1页分块的数量
#pragma pack(1)
typedef struct
{
............
............
}GlobalParamStore, * GlobalParamStore_t;
typedef struct
{
............
............
}DemarParamStore, * DemarParamStore_t, * pDemarParamStore;
typedef struct
{
............
............
}ExceptionTrace;
typedef struct
{
............
............
}ExtraLibPara;
typedef struct
{
u16 HeaderTag;
u16 StoreIndex;
}FlashStoreHeader, * FlashStoreHeader_t;
typedef struct
{
FlashStoreHeader Header;
GlobalParamStore SysPar;
DemarParamStore DemarPar;
ExceptionTrace ExpTrace;
ExtraLibPara ExtLibPar;
u8 ubRes[16];
u8 ubCRC8;
}SystemParamStore, * SystemParamStore_t;
#pragma pack()
二、Flash最底层操作(解锁,加锁,擦除,读写)
说明:
- XSUPER_STM32G0_LL_LIB_ENABLE 被使能使用LL库
- Flash相关操作失败可多次尝试,尽可能让操作成功
#define FLASH_OPT_OVERTIMER (0x1FFFF)
#define FLASH_OPT_TRY_COUNT (5)
/* Unlock the FLASH control register access */
static u8 ubFLASH_Unlock(void)
{
u8 sta = 0;
if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0x00U)
{
/* Authorize the FLASH Registers access */
WRITE_REG(FLASH->KEYR, FLASH_KEY1);
WRITE_REG(FLASH->KEYR, FLASH_KEY2);
/* verify Flash is unlock */
if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0x00U)
{
sta = 1;
}
}
return sta;
}
/* Lock the FLASH control register access */
static u8 ubFLASH_Lock(void)
{
u8 sta = 1;
/* Set the LOCK Bit to lock the FLASH Registers access */
SET_BIT(FLASH->CR, FLASH_CR_LOCK);
/* verify Flash is locked */
if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0x00u)
{
sta = 0;
}
return sta;
}
/* Gets the page of a given address */
static u32 ulGetPage(u32 startAddr)
{
return ((startAddr - FLASH_BASE) / FLASH_PAGE_SIZE);
}
/* Erase the specified FLASH memory page */
static u8 ubFLASH_PageErase(u32 page)
{
u32 tmp = 0;
u32 time = 0;
u8 res = 0;
/* Get configuration register, then clear page number */
tmp = (FLASH->CR & ~FLASH_CR_PNB);
/* Set page number, Page Erase bit & Start bit */
FLASH->CR = (tmp | (FLASH_CR_STRT | (page << FLASH_CR_PNB_Pos) | FLASH_CR_PER));
/* wait for BSY1 in order to be sure that flash operation is ended before allowing prefetch in flash */
while ((FLASH->SR & FLASH_SR_BSY1) != 0x00U)
{
if ((++time) > FLASH_OPT_OVERTIMER)
{
res = 1;
break;
}
}
/* If operation is completed or interrupted, disable the Page Erase Bit */
CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
return res;
}
/* Program double-word (64-bit) at a specified address */
/* Must EN PG bit before and DIS PG bit after */
static u8 ubFLASH_Program_DoubleWord(u32 addr, u64 data)
{
u32 time = 0;
/* Wait for last operation to be completed */
while ((FLASH->SR & FLASH_SR_BSY1) != 0x00U)
{
if ((++time) > FLASH_OPT_OVERTIMER)
{
return 1;
}
}
/* Set PG bit */
SET_BIT(FLASH->CR, FLASH_CR_PG);
/* Program first word */
*(u32 *)addr = (u32)data;
/* Barrier to ensure programming is performed in 2 steps, in right order
(independently of compiler optimization behavior) */
__ISB();
/* Program second word */
*(u32 *)(addr + 4U) = (u32)(data >> 32U);
/* Wait for last operation to be completed */
while ((FLASH->SR & FLASH_SR_BSY1) != 0x00U)
{
if ((++time) > FLASH_OPT_OVERTIMER)
{
return 2;
}
}
return 0;
}
/* Wait for a FLASH operation to complete */
static u8 ubFlash_WaitFor_Operate(u32 timeOut)
{
u32 timer = 0;
u32 error = 0;
while ((FLASH->SR & FLASH_SR_BSY1) != 0x00U)
{
if ((++timer) >= timeOut)
{
return 1;
}
}
#if ( XSUPER_STM32G0_LL_LIB_ENABLE > 0)
/* check flash errors */
error = (FLASH->SR & FLASH_FLAG_SR_ERROR);
/* Clear SR register */
FLASH->SR = FLASH_FLAG_SR_CLEAR;
#endif
#if ( XSUPER_STM32G0_HAL_LIB_ENABLE > 0)
/* check flash errors */
error = (FLASH->SR & FLASH_SR_ERRORS);
/* Clear SR register */
FLASH->SR = FLASH_SR_CLEAR;
#endif
if (error != 0x00U)
{
return 2;
}
timer = 0;
while ((FLASH->SR & FLASH_SR_CFGBSY) != 0x00U)
{
if ((++timer) > timeOut)
{
return 3;
}
}
return 0;
}
/* Read double-word (64-bit) at a specified address */
void vFlash_Read_DoubleWord(u32 startAddr, u64 * pDat, u16 len)
{
u16 i = 0;
for(i = 0; i < len; ++i)
{
*pDat++ = *(volatile u64 *)(startAddr + (i << 3));
}
}
static u8 xSuper_Flash_Unlock(void)
{
u8 tryCount = 0;
for (tryCount = 0; tryCount < FLASH_OPT_TRY_COUNT; ++tryCount)
{
if (!ubFLASH_Unlock()) return 0;
ubFlash_WaitFor_Operate(FLASH_OPT_OVERTIMER);
}
return 1;
}
static u8 xSuper_Flash_Lock(void)
{
u8 tryCount = 0;
for (tryCount = 0; tryCount < FLASH_OPT_TRY_COUNT; ++tryCount)
{
if (!ubFLASH_Lock()) return 0;
ubFlash_WaitFor_Operate(FLASH_OPT_OVERTIMER);
}
return 1;
}
static u8 xSuper_Flash_EreasePage(u32 startAddr)
{
u8 tryCount = 0;
u32 page = ulGetPage(startAddr);
for (tryCount = 0; tryCount < FLASH_OPT_TRY_COUNT; ++tryCount)
{
if (!ubFLASH_PageErase(page)) return 0;
ubFlash_WaitFor_Operate(FLASH_OPT_OVERTIMER);
}
return 1;
}
static u8 xSuper_Flash_Program(u32 startAddr, u64 * pDat, u16 len)
{
u64 rData = 0;
u16 i = 0;
u8 tryCount = 0;
for (i = 0; i < len; ++i)
{
for (tryCount = 0; tryCount < FLASH_OPT_TRY_COUNT; ++tryCount)
{
if(!ubFLASH_Program_DoubleWord(startAddr , pDat[i]))
{
rData = *(volatile u64 *)(startAddr);
if (rData != pDat[i])
{
return 1;
}
else
{
startAddr += 8;
tryCount = 0;
break;
}
}
else
{
ubFlash_WaitFor_Operate(FLASH_OPT_OVERTIMER);
}
}
if (tryCount) return 2;
}
return 0;
}
u8 ubFlash_Write_DoubleWord_EreasePage(u32 startAddr, u64 * pDat, u16 len, u8 mode)
{
//避免HardFault,三级流水线
__asm volatile("NOP"); __asm volatile("NOP"); __asm volatile("NOP");
__DMB(); __DSB(); __ISB();
__asm volatile("NOP"); __asm volatile("NOP"); __asm volatile("NOP");
if (xSuper_Flash_Unlock())
{
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Unlock Error...\r\n");
#endif
return 1;
}
if (mode)
{
if (xSuper_Flash_EreasePage(startAddr))
{
xSuper_Flash_Lock();
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Erease Error...\r\n");
#endif
return 2;
}
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Erease OK...\r\n");
#endif
}
if (xSuper_Flash_Program(startAddr, pDat, len))
{
xSuper_Flash_Lock();
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Program Error...\r\n");
#endif
return 3;
}
if (xSuper_Flash_Lock())
{
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Lock Error...\r\n");
#endif
return 4;
}
return 0;
}
三、从Flash块中读取数据
- vRead_System_Parameter开始打印每个结构体大小,便于调试对齐使用
/* 获取读Flash块索引 */
static u16 usGet_Flash_Read_Index(void)
{
u16 dat = 0, i = 0;
u8 crc = 0;
for (i = 0; i < FLASH_STORE_PARM_BLOCK_COUNT; ++i)
{
dat = *(volatile u16 *)(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * (FLASH_STORE_PARM_BLOCK_COUNT - 1 - i)));
if (dat == FLASH_STORE_PARM_HEADER_TAG)
{
crc = ubCheckSum_CRC8((void *)(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * (FLASH_STORE_PARM_BLOCK_COUNT - 1 - i))), XOFS(SystemParamStore , ubCRC8));
dat = *(volatile u8 *)(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * (FLASH_STORE_PARM_BLOCK_COUNT - 1 - i)) + (FLASH_STORE_PARM_BLOCK_SIZE - 1));
if (dat == crc)
{
return (FLASH_STORE_PARM_BLOCK_COUNT - 1 - i);
}
}
}
return FLASH_STORE_PARM_BLOCK_COUNT;
}
static void vRead_GlobalSystem_Parameter(void)
{
u16 index = usGet_Flash_Read_Index();
if (index < FLASH_STORE_PARM_BLOCK_COUNT)
{
/* 读取块内容 */
vFlash_Read_DoubleWord(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * index), (u64 *)&SystemParam, (FLASH_STORE_PARM_BLOCK_SIZE >> 3));
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Read Param OK...%u\r\n", SystemParam.Header.StoreIndex);
#endif
}
else
{
/* 块索引无效 恢复默认值 */
vRestoreDefault_GlobalSystemParam();
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Restore Param...%u\r\n", SystemParam.Header.StoreIndex);
#endif
}
/* 打印块数据 */
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("\r\n");
dprintf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F \r\n");
dprintf("-------------------------------------------------------------\r\n");
for (index = 0; index < FLASH_STORE_PARM_BLOCK_SIZE; ++index)
{
if (index && (index % 16 == 0)) dprintf("\r\n");
if (index % 16 == 0) dprintf("0x%08X ", (SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * SystemParam.Header.StoreIndex) + index));
dprintf("%02X ", *(volatile u8 *)(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * SystemParam.Header.StoreIndex) + index));
}
dprintf("\r\n\r\n");
#endif
}
void vRead_System_Parameter(void)
{
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Header Size :%u\r\n", sizeof(FlashStoreHeader));
dprintf("SysPar Size :%u\r\n", sizeof(GlobalParamStore));
dprintf("DemarPar Size :%u\r\n", sizeof(DemarParamStore));
dprintf("ExpTrace Size :%u\r\n", sizeof(ExceptionTrace));
dprintf("ExtLibPar Size:%u\r\n", sizeof(ExtraLibPara));
dprintf("SystemParamStore Size......%u\r\n", sizeof(SystemParamStore));
if (sizeof(SystemParamStore) != FLASH_STORE_PARM_BLOCK_SIZE) while (1);
#endif
vRead_GlobalSystem_Parameter();
}
四、将数据写入Flash块中
static u16 usGet_Flash_Write_Index(void)
{
u16 dat = 0, i = 0, x = 0;
u8 uFlg = 0;
for (i = 0; i < FLASH_STORE_PARM_BLOCK_COUNT; ++i)
{
dat = *(volatile u16 *)(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * i));
if (dat == 0xFFFFU)
{
uFlg = 0;
for (x = 0; x < FLASH_STORE_PARM_BLOCK_SIZE; ++x)
{
dat = *(volatile u8 *)(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * i) + x);
if (dat != 0xFFU)
{
uFlg = 1;
break;
}
}
if (!uFlg)
{
return i;
}
}
}
return FLASH_STORE_PARM_BLOCK_COUNT;
}
static void vSave_GlobalSystem_Parameter(void)
{
u8 * pSrc = (u8 *)&SystemParam;
u8 mode = 0, dat = 0;
u16 index = 0;
for (index = 0; index < FLASH_STORE_PARM_BLOCK_SIZE; ++index)
{
dat = *(volatile u8 *)(SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * SystemParam.Header.StoreIndex) + index);
if (dat != *pSrc++)
{
mode = 1;
break;
}
}
if (!mode)
{
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Pram Same...%u\r\n", SystemParam.Header.StoreIndex);
#endif
return;
}
mode = 0;
index = usGet_Flash_Write_Index();
if (index < FLASH_STORE_PARM_BLOCK_COUNT)
{
SystemParam.Header.StoreIndex = index;
}
else
{
SystemParam.Header.StoreIndex = 0;
mode = 1;
}
SystemParam.ubCRC8 = ubCheckSum_CRC8((void *)(&SystemParam), XOFS(SystemParamStore , ubCRC8));
if (ubFlash_Write_DoubleWord_EreasePage((SYSTEM_ARG_STORE_START_ADDRE + (FLASH_STORE_PARM_BLOCK_SIZE * SystemParam.Header.StoreIndex)), (u64 *)&SystemParam, (FLASH_STORE_PARM_BLOCK_SIZE >> 3), mode))
{
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Write Error...%u\r\n", SystemParam.Header.StoreIndex);
#endif
}
else
{
#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)
dprintf("Flash Write OK...%u CRC:%02X\r\n", SystemParam.Header.StoreIndex, SystemParam.ubCRC8);
#endif
}
}
void vSave_System_Parameter(void)
{
vSave_GlobalSystem_Parameter();
}
五、测试验证
只需调用一下函数即可:
从Flash块中读取数据
void vSave_System_Parameter(void)
将数据写入Flash块中
void vRead_System_Parameter(void)