Many microcontrollers have embedded EEPROM’s that enable the permanent storage of information when power is removed. Often the stated life of the EEPROM is in the order of 100,000 writes and this may seem quite a large number but if you’re developing an embedded, IOT or mission critical application that needs to regularly update a counter or a number of settings, 100,000 writes can be consumed within a very short space of time.
It is not always necessary to ensure that the stored value on a device is completely accurate or up to date when the data is transient in nature and only the current value is displayed but if the system is designed to accurately record event counts or perform statistical analysis then it must be resilient within itself in case of power failure or loss of Internet connectivity. In this case the reliability of local data storage becomes a very significant design consideration because cloud storage will only ever hold the values sent to it so if the source data is wrong or corrupt then no amount of distributed back up or redundant infrastructure will fix the problem.
In practice, even the smallest of microcontrollers such as the 8 pin Atmel ATTiny85 (shown above) will have 512 bytes of EEPROM available for storing data. Other devices can have significantly more. Typically, in the case of the ATTiny85, the amount of information stored is going to be limited to just a few bytes so it would make sense if the available embedded EEPROM space could be more effectively used and exploit all of its available capacity thereby extending the reliable life of an applications data storage.
Why not use backed up RAM?
In order to use RAM in this situation it needs to be connected via some physical electrical interface and a small device has limited available pins. The RAM needs to be very carefully backed up and in the case of a system that has been in the field for several years the battery will need replacing or servicing and a capacitor backup device will only hold the valuable content safe for a number of hours, maybe days at best. Batteries are known to leak and eat circuit boards and are less desirable in a number of hazardous situations such as in low or high pressure environments, hot or humid conditions. Finally, RAM and battries add cost to the BOM of a product and take up physical space. Overall, if data can be stored in an EEPROM device then it will result in a simpler, safer and more cost effective long term solution.
The EEPROM Solution
One solution that works for any simple or complex data type and exploits the full potential of an embedded EEPROM device is to assign either a portion or the whole EEPROM memory space to storing the data and then to keep using the same block of space for writing that data over and over again until it starts to fail. Once this happens then the data is stored in the next free block and so on. Depending on how much information needs to be stored in the EEPROM and the actual life of the EEPROM cells then the usable life of a device can be extended from 100,000 guaranteed writes to many, many millions.
The trick in keeping this solution simple and yet reliable is to know where the current update block is stored when the software first run without using additional EEPROM locations as indicators because updating these indicators alone would waste space and create their own failure mechanism. Unlike RAM memory which powers up with data values in random states, nearly all EEPROMS when new or erased have their bits set to ‘1’. This means that EEPROMs already have a built in mechanism that indicates which memory locations have been used before. All the algorithm needs to do then is to find the first ’empty’ block in the EEPROM and go back one to write the data into the previously used block. To ensure reliability when writing the data, each byte must be read back and compared to the written value. Should there be any differences then the whole operation is moved onto the next block in the EEPROM and repeated. The new block will have an endurance of at least 100,000 writes and so on until all of the available EEPROM space is exhausted.
This method is extremely lightweight and efficient because is uses no extra memory space to align or indicate which blocks have been used or are available. It’s only draw back is that should the data written be all ‘1”s across every byte in the block then it would be seen as the next unused block of storage and the data about to be written will be saved back into to the previous block which in theory has now started to fail. In reality, it is very unlikely that any combination of useable values will be all ‘1’ and there is much that can be done to ensure that one or more bits have been intentionally forced to ‘0’.
The result of using this method is that instead of having a limited life on a device that must be factored into the application software design from day one the EEPROM can now be used with a considerably higher degree of freedom. For example, by using one byte to store the current state of a number of buttons arranged as one bit per button (avoiding all 8 bits set to ‘1’) the EEPROM in an ATTiny85 would have a usable life of over 51,000,000 writes. That is equivalent to storing the state of these buttons if they changed once a minute for 97 years; about the same time as the EEPROM data retention is expected to remain stable.
Another example using such a small device would be if it was used to store the value of a 7 bit rotary encoder that must be recalled whenever an item is powered on. To avoid a large number of writes over the lifetime of a product that could exceed 100,000 a developer might need to add logic to only updates the EEPROM at given intervals or only if it has been idle for a while. However, with the method described above it would be possible for to the data to written much more frequently or whenever the encoder value changed.
The basic C language source code available for this solution can be downloaded and compiled with Atmel Studio 7.0 into any project that is targetted at a device with a built in EEPROM using the same register map as the AtTiny85.
It has a very lightweight interface with just 3 functions: –
// Public interface to the EEPROM code
void EEPROM_Initialise (uint16_t sizeofData, uint16_t sizeofEEPROM);
bool EEPROM_WriteData (uint8_t* pData);
bool EEPROM_ReadData (uint8_t* pData);
Initialisation sets up the size and data boundary conditions, read and write then store and load the data from the EEPROM using the method described above.
In the following example the data stored will fit into a single byte due to the use of bit-field operators but any reasonable number of data types can be combined and used. The C structure simply groups the settings together into a single block that can be easily moved into and out of the EEPROM regardless of its size or format.
int8_t modeValue : 1;
int8_t gainValue : 2;
The first thing to do is to initialise the EEPROM interface. This allows it work out the block size and the number of blocks that can be stored in the EEPROM device. EEPROM_SIZE is the number of bytes that can be stored in the EEPROM but if only a portion of the EEPROM is to be used then this value can be reduced accordingly.
#define EEPROM_SIZE 512
// Initialise high endurance EEPROM interface
EEPROM_Initialise (sizeof(SETTINGS_DATA), EEPROM_SIZE);
// If last settings not loaded...
if (!EEPROM_ReadData ((uint8_t*)&settingsData))
// Initialise default values
settingsData.gainValue = 0;
settingsData.modeValue = 0;
settingsData.gainValue = 3;
settingsData.modeValue = 1;
Once the EEPROM interface is initialised then the last set of stored values can be read from the EEPROM. If this is the first time the EEPROM has been accessed and no data has yet been stored the function returns FALSE and the default values can be set. (The read method intentionally sets the entire data structure to ‘0’ in order to avoid unused bit-field values from randomly initialising to ‘1’, which could cause an edge case failure on random products)
From then on, the stored values can be changed and written to the EEPROM knowing that the latest working block will always be updated. Should any write operation fail then the code will automatically move onto the next block and save the values there.
On subsequent start up’s of the application the read operation will find the last used storage block and return those values as the current settings. Writing then continues at the current good block.
The technique described here makes it possible to dramatically increase the resilience and reliability of the long term storage of values in an EEPROM for an embedded, IOT or mission critical application.
There are a number of enhancements that can be made to this code but probably the most obvious improvement would be to convert it to C++ using OO techniques so that the concrete code that saves and loads bytes in the EEPROM can be customised for different controllers or even used to access an external EEPROM connected over an I2C interface for example.
About Open Technologies Limited
OTL specialise in the design and development of real-time software and hardware applications. We strive to innovate and bring our customers both cutting edge technologies and efficient solutions to their problems. With many years of experience working in industrial and consumer sectors we are always conscious of the need for reliable, maintainable and sustainable products. We are always looking to work with new clients and welcome the opportunity to discuss how we may be able to help you.