STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入

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中的数据

    说明:

    1. Header用来标识存储的头部信息,在Flash读取或写入时用到
    2. 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最底层操作(解锁,加锁,擦除,读写)

    说明:

    1. XSUPER_STM32G0_LL_LIB_ENABLE 被使能使用LL库
    2. 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块中读取数据

    1. vRead_System_Parameter开始打印每个结构体大小,便于调试对齐使用

    t/30e0161f1a734f6b96899354ad4e0cd4.png)

    
    /* 获取读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)




    作者:凌盛羽

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入

    发表回复