Electronic – Efficient way of writing to flash memory without losing data

cflashspi

For my project, I am writing some code that gets some results and I want to store these results on external flash memory. The external flash memory in question is a MX25R8035F. I found that writing without erasing only works the first time I wrote something to an address.

But because of this I'm running into some memory issues. The smallest erase is a sector erase (4096 bytes.) If I want to write to a specific address I would first need to read the sector that contains that address, change the bytes that I want to be changed and then write the whole sector (writing is only per page.)

Is there a more efficient way of using this external flash memory? Specifically using less memory to change one or two bytes.

The function I use now this can be found below:

uint8_t EXT_FLASH_write(size_t address, uint8_t *buf, size_t length) {

/*
 * The entire sector will be erased when writing to an offset inside that sector
 * Therefore this function will first retrieve all data inside the sector and update the retrieved
 * data with the values inside buff which and will then write the entire content back to the sector
 */

    uint8_t wbuf[4];
    SPI_Transaction transaction;
    uint32_t sectorBaseAddr;
    uint8_t temp[EXT_FLASH_ERASE_SECTOR_SIZE];
    uint8_t tries;
    size_t ilen; /* interim length per instruction */
    uint32_t bufIndex = 0;
    uint8_t pageIterations;

    while (length > 0) {
        // first retrieve entire sector so it can be erased on the chip
        sectorBaseAddr = EXT_FLASH_SECTOR_BASE_ADDR(address);
        EXT_FLASH_read(sectorBaseAddr, temp, EXT_FLASH_ERASE_SECTOR_SIZE);

        // Erase the sector on the chip
        EXT_FLASH_erase(address, EXT_FLASH_ERASE_SECTOR_SIZE);

        ilen = EXT_FLASH_PROGRAM_PAGE_SIZE
                - (address % EXT_FLASH_PROGRAM_PAGE_SIZE);
        if (length < ilen)
            ilen = length;

        memcpy(temp + (address - sectorBaseAddr), buf + bufIndex, length);

        bufIndex += ilen;
        address += ilen;
        length -= ilen;

        tries = 0;
        if (EXT_FLASH_writeEnable() != 0) {
            if (tries == EXT_FLASH_MAX_TRIES) {
                return 1;
            }
            tries++;
        }
        tries = 0;
        while (EXT_FLASH_waitReady() != 2) {
            if (tries == EXT_FLASH_MAX_TRIES) {
                return 1;
            }
            EXT_FLASH_writeEnable();
            tries++;
        }

        // programming the external flash can only be done in pages
        // so divide current sector into pages and write each page separately
         for (pageIterations = 0;
                 pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE
                         < EXT_FLASH_ERASE_SECTOR_SIZE; pageIterations++) {
            EXT_FLASH_select();

            wbuf[0] = EXT_FLASH_CODE_PROGRAM;
            wbuf[1] = ((sectorBaseAddr
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE)) >> 16)
                    & 0xff;
            wbuf[2] = ((sectorBaseAddr
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE)) >> 8)
                    & 0xff;
            wbuf[3] = (sectorBaseAddr
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE)) & 0xff;

            // Configure the transaction
            transaction.count = sizeof(wbuf);
            transaction.txBuf = wbuf;
            transaction.rxBuf = NULL;

            if (!SPI_transfer(masterSpi, &transaction)) {
                /* failure */
                EXT_FLASH_deselect();
                return 1;
            }
            // Configure the transaction
            transaction.count = EXT_FLASH_PROGRAM_PAGE_SIZE;
            transaction.txBuf = temp
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE);
            transaction.rxBuf = NULL;

            if (!SPI_transfer(masterSpi, &transaction)) {
                /* failure */
                EXT_FLASH_deselect();
                return 1;
            }

            EXT_FLASH_deselect();
        }
    }
    return 0;
}

Best Answer

FLASH is cheap and provides lot of memory, but comes with the cost of sector erase. So you might want to use EEPROM instead.

Of course it is possible to overcome this flash limitation with some clever software, which tracks the bits and bytes with a journal (i.e. you don't overwrite a byte value but write a new copy of it and note in the journal that the new byte is the recent one). But this is a really big software overhead and you might not want to do that.