[TUT] [C] Smart EEPROM usage

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Reading the EEPROM need longer than reading the SRAM.
Writing the EEPROM need over 1000 times longer and was also limited in count.

Thus I use always the follwing approach:
I manipulate not the EEPROM directly, I establish an image on the SRAM and do all the access on it.
And to update the SRAM image (e.g. after reset) or write back to the EEPROM, I call a function to do so.
Since the AVR-GCC routines cause a big code bloat, I wrote my own function.
Since reading or writing the EEPROM need many similar steps, I combined both into a single function.
Also to avoid unneeded EEPROM wearout, I write only, if the EEPROM content was different to the new value.

Following the eeprom.c:

#include 


void eeprom_rw( uint16_t eep, uint8_t *sram, uint8_t len, uint8_t write )
{
  uint8_t val;

  do{
    EEAR = eep;
    EECR |= 1<<EERE;                    // read
    val = EEDR;
    if( write ){                        // write or read action
      if( *sram != val ){               // if not equal
        EEDR = *sram;                   // load data
        ATOMIC_BLOCK(ATOMIC_FORCEON){
          EECR |= 1<<EEMPE;
          EECR |= 1<<EEPE;              // write
        }
        while( EECR & 1<<EEPE );        // wait until write done
      }
    }else{
      *sram = val;
    }
    sram++;
    eep++;
  }while( --len );                      // 1..256 byte
}

And the eeprom.h:

void eeprom_rw( uint16_t eep, uint8_t *sram, uint8_t len, uint8_t write );

#define eeprom_read(sram, eep, len)     eeprom_rw( eep, (void*)sram, len, 0 )
#define eeprom_write(eep, sram, len)    eeprom_rw( eep, (void*)sram, len, 1 )

Then I combine all parameters, which must be stored, into a struct and update always the whole struct.
Since unneeded EEPROM wearout was suppressed, you can always store the whole struct, even if only one parameter was changed.

Following an example code:

#include 
#include "eeprom.h"


#define EE_ADDR 1


struct{
  uint32_t p0;
  float p1;
  int16_t p2;
  uint8_t p3;
}eeprom_data;



int main( void )
{
  eeprom_read( &eeprom_data, EE_ADDR, sizeof(eeprom_data) );

  eeprom_data.p0++;

  eeprom_write( EE_ADDR, &eeprom_data, sizeof(eeprom_data) );

  for(;;){
  }
}

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're writing one byte at a time, sequentially. According to the avr-libc FAQ (any independent confirmation?), when you write one byte, the hardware is actually erasing and rewriting an entire page, typically 4 bytes. So if you want to maximize EEPROM lifetime, consider having your block-writing routine spread the bytes across pages by rotating the two MSBs of the address down to the LSB positions. The details vary by EEPROM page size and capacity.

This works best if you're using less than 1/4th of the EEPROM (for 4-byte pages) and doesn't help at all if you're updating 3/4ths or more.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
According to the avr-libc FAQ (any independent confirmation?), when you write one byte, the hardware is actually erasing and rewriting an entire page, typically 4 bytes.

I have never heard this before, and looking through all the Atmel application notes concerning EEPROM that I could find turned up nothing. App note avr103 seems to directly contradict the page assertion. And avr101, which specifically deals with extending the life of EEPROM fails to mention it.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bretm wrote:
You're writing one byte at a time, sequentially.

Yes.

Until now, I have no ATtiny/ATmega seen, which support page write from the application.
Thus no page write can be implemented.

Page write was only possible with an external programmer.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a nice solution to bulk reading and writing the EEPROM, which is handy as I was thinking about doing just that.

It's a shame there doesn't appear to be a way of doing a page-wide write though, if that's what is happening behind the scenes.

A good (IMO) addition to the code would be to extend it to structures >255 bytes long so an array of program data could, for instance, be read out of EEPROM at startup.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
I have never heard this before, and looking through all the Atmel application notes concerning EEPROM that I could find turned up nothing.

The source of this seems to be avr-libc developer Bob Paddock, based on info recieved "from the factory":

http://lists.gnu.org/archive/htm...
"The factory has confirmed, via my FAE, that the listed Endurance figures are on a page bases, and not a byte bases."

http://blog.designer-iii.com/avr...
Commenting on adding the FAQ entry

Contradicts this:

http://www.atmel.com/dyn/resourc...
"When writing to EEPROM one byte is written at a time."

I think I'll just sacrifice a couple of pages of EEPROM to see what happens. Keep writing to one byte on one page and to four bytes on another page until the bytes stop reading back correctly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bretm wrote:
I think I'll just sacrifice a couple of pages of EEPROM to see what happens. Keep writing to one byte on one page and to four bytes on another page until the bytes stop reading back correctly.
I have tried that. Other freaks also tried similar things. See this thread, for example.

Eugene Zharkov

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, that seems pretty definitive. And bpaddock posted there, too. Sorry to pollute the thread with bad info! :D