2
votes

I'm using HAL_FLASHEx_Erase on stm32f103c8.

According to datasheet, Flash memory endurance is just 10k cycles.

my data is 16 bytes so i thought method how to use flash memory like little block with counter

before i notice it erase 1 page each time.

if i erase 1 page that having 16 bytes data at front

is all other bytes in page become lose there endurance cycle even it not written?

this is method what i thought

it use next frame when it lost there endurance

frame = data(16bytes) + counter(2bytes)

page (1k bytes each) data
1 frame_pointer
2 frame1, frame2, frame3 ...
3 frame56, frame57, frame58 ...
2
Indeed this will wear out the flash. The best solution is to pick a MCU with eeprom or data flash that come with more cycles or smaller segments.Lundin
@Lundin thanks for answer. but replacing MCU need the cost and it will meet same problem after more cyclesherrun
I don't think you understood what I wrote. If you pick a part with dedicated eeprom/data flash, you get far more cycles. STM32L071 for example can get up to 100k erase cycles of the eeprom, long as you refrain from writing to it during ridiculously high temperatures (in 100 dgr C etc). Data retention 30 years which is better than most. These are the kind of considerations you need to make before picking a MCU.Lundin
@Lundin thanks i'll check STM32L0 series for next project if i use data longer than 16bytes.herrun

2 Answers

2
votes

Indeed, the entire page needs to be erased if an existing value needs to be changed such your frame pointer. The only exception is if the value has still the initial value after erase, which is hexadecimal FF FF FF FF...

So the typical approach is to only append data until the page is full and to use the initial FF FF values to detect whether a slot has been used. In your case, it could look like so (note: I don't understand what the counter is for):

#define TAG_UNUSED 0xffff
#define TAG_USED 0x1111

typedef struct {
   uint16_t tag;
   uint16_t counter;
   uint8_t data[16];
} flash_slot;

#define FLASH_PAGE_SIZE 1024
#define NUM_FLASH_SLOTS (FLASH_PAGE_SIZE/sizeof(flash_slot))
#define FLASH_SLOTS ((volatile flash_slot*)0x800FC00)

void save_data(uint8_t* data) {
    // find next free slot
    int index;
    for (index = 0; index < NUM_FLASH_SLOTS; index++) {
        if (FLASH_SLOTS[index].tag == TAG_UNUSED)
            break;
    }

    if (index == NUM_FLASH_SLOTS) {
       // all slots are used; erase page
       flash_erase_page(FLASH_SLOTS);
       index = 0;
    }

    // prepare data to be written
    flash_slot slot;
    slot.tag = TAG_USED;
    slot.counter = 0;
    memcpy(slot.data, data, 16);

    // write slot without erasing page (as it's still at FF FF)
    flash_write_data(&FLASH_SLOTS[index], &slot, sizeof(slot));
}
0
votes

it work probably perfect for me.

pc.h used for only debug.

it look be helpful for understand it. so i doesn't erase it.

flash.h

#ifndef __FLASH_H
#define __FLASH_H

#define FLASH_NOK 0
#define FLASH_OK 1
#define FLASH_USED 0x1111
#define FLASH_UNUSED 0XFFFF
#define FLASH_MAX_CYCLES 10000
#define FLASH_COUNT_SIZE sizeof(uint16_t)
#define FLASH_STATE_SIZE sizeof(uint16_t)
#define FLASH_PAGE_COUNT(pageAddr) *((uint16_t *)pageAddr)
#define FLASH_BLOCK_STATE(blockAddr) *((uint16_t *)blockAddr)

typedef struct _FLASH_HANDLER_{
    uint32_t pageAddr;
    uint32_t blockAddr;
    uint16_t blockSize;
}_FLASH_HANDLER;

uint8_t FLASH_Init(_PC_HANDLER *pchandler);
uint8_t FLASH_Read(uint32_t pageAddr, void *data, uint16_t dataSize);
uint8_t FLASH_Write(uint32_t pageAddr, void *data, uint16_t dataSize);
#endif

flash.c

#include "flash.h"
#include "pc.h"
#include <string.h>
#include <stdio.h>
_PC_HANDLER *hpce;

static uint8_t FLASH_FindUseablePage(_FLASH_HANDLER *hflash);
static uint8_t FLASH_FindRecentBlock(_FLASH_HANDLER *hflash);
static uint8_t FLASH_ErasePage(_FLASH_HANDLER *hflash);
static uint8_t FLASH_InitPage(_FLASH_HANDLER *hflash);

uint8_t FLASH_Init(_PC_HANDLER *pchandler)
{
    hpce = pchandler;
    return FLASH_OK;
}

uint8_t FLASH_Read(uint32_t pageAddr, void *data, uint16_t dataSize)
{
    uint8_t msg[USB_BUFFER_SIZE];
    _FLASH_HANDLER hflash;
    hflash.pageAddr = pageAddr;
    hflash.blockSize = dataSize + FLASH_STATE_SIZE;
    
    FLASH_FindUseablePage(&hflash);
    FLASH_FindRecentBlock(&hflash);
    snprintf((char *)msg, sizeof(msg), "reading.. (%x)\r\n", hflash.blockAddr);
    PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
    memcpy(data, (void *)(hflash.blockAddr + FLASH_STATE_SIZE), dataSize);
    return FLASH_OK;
}

uint8_t FLASH_Write(uint32_t pageAddr, void *data, uint16_t dataSize)
{
    uint8_t msg[USB_BUFFER_SIZE];
    uint8_t res = FLASH_OK;
    uint16_t i;
    _FLASH_HANDLER hflash;
    hflash.pageAddr = pageAddr;
    hflash.blockSize = dataSize + FLASH_STATE_SIZE;
    // add padding for halfword flash write
    hflash.blockSize += hflash.blockSize % 2;
    // make block
    uint16_t block[hflash.blockSize];
    block[0] = FLASH_USED;
    memcpy(&block[1], data, dataSize);
    
    FLASH_FindUseablePage(&hflash);
    FLASH_FindRecentBlock(&hflash);
    
    // if block used, use next block
    if(FLASH_BLOCK_STATE(hflash.blockAddr) == FLASH_USED) hflash.blockAddr += hflash.blockSize;
    
    // page dosent have enough space for write 
    if(hflash.blockAddr + hflash.blockSize > hflash.pageAddr + FLASH_PAGE_SIZE)
    {
        snprintf((char *)msg, sizeof(msg), "page full %x\r\n", hflash.pageAddr);
        PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
        FLASH_ErasePage(&hflash);
        FLASH_FindUseablePage(&hflash);
        FLASH_FindRecentBlock(&hflash);
    }
    snprintf((char *)msg, sizeof(msg), "writing.. (%x)\r\n", hflash.blockAddr);
    PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
    HAL_FLASH_Unlock();
    for(i = 0; i < (hflash.blockSize / 2); i++)
    {
        if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, hflash.blockAddr + (i * 2), block[i]) != HAL_OK)
        {
            res = FLASH_NOK;
        }
    }
    HAL_FLASH_Lock();
    
    return res;
}

static uint8_t FLASH_FindUseablePage(_FLASH_HANDLER *hflash)
{
    uint8_t msg[USB_BUFFER_SIZE];
    uint16_t count;
    
    while((count = FLASH_PAGE_COUNT(hflash->pageAddr)) > FLASH_MAX_CYCLES)
    {
        if(count == FLASH_UNUSED) FLASH_InitPage(hflash);
        else hflash->pageAddr += FLASH_PAGE_SIZE;
    }
    
    snprintf((char *)msg, sizeof(msg), "found useable page! (addr %x, counter %d)\r\n", hflash->pageAddr, count);
    PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
    return FLASH_OK;
}

static uint8_t FLASH_FindRecentBlock(_FLASH_HANDLER *hflash)
{
    uint16_t state;
    uint8_t msg[USB_BUFFER_SIZE];
    // page structure : [counter] [block1] [block2] ... [blockN] 
    hflash->blockAddr = hflash->pageAddr + FLASH_COUNT_SIZE;
    // block structure : [state] [data]
    while((state = FLASH_BLOCK_STATE(hflash->blockAddr)) == FLASH_USED)
    {

        hflash->blockAddr += hflash->blockSize;
        // page dosent have enough space for write 
        if(hflash->blockAddr + hflash->blockSize > hflash->pageAddr + FLASH_PAGE_SIZE)
        {
            break;
        }
    }
    
    if(hflash->blockAddr > hflash->pageAddr + FLASH_COUNT_SIZE)
    {
        hflash->blockAddr -= hflash->blockSize;
    }
    
    state = FLASH_BLOCK_STATE(hflash->blockAddr);
    snprintf((char *)msg, sizeof(msg), "found recent block! (addr %x state %x)\r\n", hflash->blockAddr, state);
    PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);

    return FLASH_OK;
}

static uint8_t FLASH_InitPage(_FLASH_HANDLER *hflash)
{
    uint8_t msg[USB_BUFFER_SIZE];
    uint8_t res = FLASH_NOK;
    if(FLASH_PAGE_COUNT(hflash->pageAddr) == FLASH_UNUSED)
    {
        res = FLASH_OK;
        HAL_FLASH_Unlock();
        if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, hflash->pageAddr , 0x0001) != HAL_OK)
        {
            res = FLASH_NOK;
        }
        HAL_FLASH_Lock();
    }
    
    snprintf((char *)msg, sizeof(msg), "page init done! (addr %x res %d)\r\n", hflash->pageAddr, res);
    PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
    
    return res;
}

static uint8_t FLASH_ErasePage(_FLASH_HANDLER *hflash)
{
    uint8_t msg[USB_BUFFER_SIZE];
    uint8_t res = FLASH_OK;
    uint32_t PAGEerror = 0;
    uint16_t count;
    
    FLASH_EraseInitTypeDef eraseInitStruct;
    eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    eraseInitStruct.PageAddress = hflash->pageAddr;
    eraseInitStruct.NbPages = 1;
    
    // add count when erase page
    count = FLASH_PAGE_COUNT(hflash->pageAddr) + 1;
    
    HAL_FLASH_Unlock();
    if(HAL_FLASHEx_Erase(&eraseInitStruct, &PAGEerror) != HAL_OK) res = FLASH_NOK;
    else
    {
        if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, hflash->pageAddr , count) != HAL_OK)
        {
            res = FLASH_NOK;
        }
    }
    HAL_FLASH_Lock();

    snprintf((char *)msg, sizeof(msg), "page erasing done! (addr %x count %x)\r\n", hflash->pageAddr, count);
    PC_UART_Transmit(hpce, msg, strlen((char *)msg), 100);
    
    return res;
}