公司微信网站建设方案长沙网络推广
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;
#endifif (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");#endifreturn 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");#endifreturn 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");#endifreturn 3;}if (xSuper_Flash_Lock()){#if defined(DEBUG_RELEASE_VERSION_ENABLE) && (DEBUG_RELEASE_VERSION_ENABLE == 0)dprintf("Flash Lock Error...\r\n");#endifreturn 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);#endifvRead_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);#endifreturn;}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)