By dave | July 22, 2018

EEPROM implementations that are seamless between 8 and 32 bit

If like me you use both 8 bit AVR and 32 bit boards, you’ve probably already noticed that there’s no EEPROM support on many 32 bit boards. I think that’s quite unfortunate as EEPROM storage is very useful for many applications.

Relying on memory backup like many systems today do, is nowhere near as reliable (a flat battery loses everything). Further, I’ve even seen discussions where people suggest using an area of program Flash as an EEPROM, I don’t personally like it as flash write cycles are generally an order of magnitude lower than EEPROM, but it’s now supported via the EEPROM class wrapper.

There’s a readily available series of EEPROM IC’s from Atmel in the form of the AT24Cx range; which offers various capacities in a 8 pin package over i2c. This library abstracts either AVR or I2C ROMs with a single interface, so you can easily interchange them just changing the implementation.


The EEPROM Types

EepromAbstraction (the interface)

All the below classes implement this interface, when you want to work with an EEPROM in a function, always use this. For example:

void myFunctionNeedingRom(EepromAbstraction* anyEeprom) {
    // your code here!
}

AvrEeprom

This implementation is based on the AVR EEPROM storage and therefore is only available when the platform is AVR (Uno, Mega etc). It wraps all the eeprom functions, and makes it easy should you later want to port to a 32 bit board. Below shows how to create this type of ROM.

AvrEeprom rom;

I2cAt24Eeprom

This is an internal, ground up implementation that follows the i2c EEPROM standard, it is compatible with most devices including the At24Cx range of eeproms. To use this you need to set the address for the device, normally 0x50 to 0x57 depending on the address selection pins and the page size for the device; the below table shows page sizes for common devices.

First include the required header:

#include <EepromAbstractionWire.h>

To create this type of ROM we need the i2c address and page size:

ROM PageSize
PAGESIZE_AT24C32 32
PAGESIZE_AT24C64 32
PAGESIZE_AT24C128 64
PAGESIZE_AT24C256 64
PAGESIZE_AT24C512 128

Here’s the code

// then create the eeprom abstraction giving address and page size
I2cAt24Eeprom rom(0x50, PAGESIZE_AT24C64);

// during the setup() you must initialise the wire library using Wire.begin()
void setup() {
    Wire.begin();
    
    // your other tasks.
}

The circuit for an i2c EEPROM (nearly all share same pin-outs):

Diagram showing wiring of an i2c eeprom

EEPROM i2c wiring diagram

EEPROMWrapper

This class wraps the EEPROM class available on many Arduino boards for use with this abstraction. Firstly to use the wrapper you must include:

#include <ArduinoEEPROMAbstraction.h>

Unlike using the usual eeprom classes, where we recommend you create the object globally in this case you should create it locally just as you’re about to use it. Also, on 32 bit boards such as the ESP range, you’ll need to call EEPROM.begin(size) before using and EEPROM.commit() after the writes are complete yourself. Bear in mind what I said earlier, FLASH has far less cycles than regular EEPROM, so save sparingly on 32 bit boards.

ArduinoEEPROMAbstraction eepromWrapper(EEPROM);

NoEeprom

Fulfills the interface but actually does nothing, useful for when EEPROM support is optional. Below is an example of how to create this type:

NoEeprom rom;

MockEepromAbstraction for unit testing

If you tend to unit test your embedded code, you can also mock out the EEPROM. This implementation uses a little RAM instead, defined in EEPROM_MOCK_SIZE and defaults to 128 bytes:

#include <MockEepromAbstraction.h>

MockEepromAbstraction mockEeprom;

It has an additional method useful for error checking, to see if all is good.

mockEeprom.hasErrorOccurred();

Other than this, it behaves identically.

Reading and writing values - all implementations

There are method to read and write both primitive types and arrays, 8 bit, 16 bit and 32 bit values can be written easily with arrays also supported.

To read a byte from ROM at location romStart

byte by = anEeprom.read8(romStart);

To read a 16 bit word from ROM at location romStart

unsigned int i16 = anEeprom.read16(romStart);

To read a 32 bit long from ROM at location romStart

unsigned long i32 = anEeprom.read32(romStart);

To write a byte back to ROM storage at location romStart

anEeprom.write8(romAddr, byteVal);

To write a 16 bit word back to ROM storage at location romStart

anEeprom.write16(romAddr, value16);

To write a 32 bit long back to ROM storage at location romStart

anEeprom.write32(romAddr, value32);

To read an array into memory starting at data from ROM starting at romStart with length len

anEeprom.readIntoMemArray(data, romStart, len);

To write an array from memory starting at data to ROM starting at romStart with length len

anEeprom.writeArrayToRom(romStart, data, len);

A word about EEPROM performance

EEPROM storage is slow, don’t read it frequently during program run, it will slow down your code, especially the i2c variant; which probably has a read bandwidth of around 40K/sec. Further, writing is even slower, and there’s a limited number of cycles so only write when you need to. One common method is to capture the power down state and write out any updated values. See my article about power detection. Another is to have a save menu option or button that will perform the save.

Go back to the IoAbstraction page

Other pages within this category

comments powered by Disqus

We use cookies to analyse traffic and to personalise content. We also embed Twitter, Youtube and Disqus content on some pages, these companies have their own privacy policies.

Please see our privacy policy should you need more information or wish to adjust your settings.