Tuesday, May 10, 2011

Discovering Arduino’s internal EEPROM lifespan

Hello readers

Some time ago I published a short tutorial concerning the use of the internal EEPROM  belonging to the Atmel ATmega328 (etc.) microcontroller in our various Arduino boards. Although making use of the EEPROM is certainly useful, it has a theoretical finite lifespan - according to the Atmel data sheet (download .pdf) it is 100,000 write/erase cycles.

One of my twitter followers asked me "is that 100,000 uses per address, or the entire EEPROM?" - a very good question. So in the name of wanton destruction I have devised a simple way to answer the question of EEPROM lifespan. We will write the number 170 (10101010 in binary) to each EEPROM address, then read each EEPROM address to check the stored number. The process is then repeated by writing the number 85 (01010101 in binary) to each address and then checking it again. The two binary numbers were chosen to ensure each bit in an address has an equal number of state changes.

After both of the processes listed above has completed, then the whole lot repeats. The process is halted when an incorrectly stored number is read from the EEPROM - the first failure. At this point the number of cycles, start and end time data are shown on the LCD.

Question - What is the largest integer that can be stored in an ATmega328 EEPROM address?

In this example one cycle is 1024 sequential writes then reads. One would consider the entire EEPROM to be unusable after one false read, as it would be almost impossible to keep track of  individual damaged EEPROM addresses. (Then again, a sketch could run a write/read check before attempting to allocate data to the EEPROM...)

If for some reason you would like to run this process yourself, please do not do so using an Arduino Mega, or another board that has a fixed microcontroller. (Unless for some reason you are the paranoid type and need to delete some data permanently). Once again, please note that the purpose of this sketch is to basically destroyyour Arduino's EEPROM. Here is the sketch (download):

/* Arduino EEPROM killer John Boxall - http://tronixstuff.com - March 2011 CC by-sa Note: This sketch will destroy your Arduino's EEPROM Do not use with Arduino boards that have fixed microcontrollers Sketch assumes DS1307 already contains current date and time */  #include <EEPROM.h> #include <LiquidCrystal.h> LiquidCrystal lcd(4,5,6,7,8,9);  #include "Wire.h" #define DS1307_I2C_ADDRESS 0x68  // all these bytes necessary for time and date data byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; byte sday, smonth, ssecond, sminute, shour; byte fday, fmonth, fsecond, fminute, fhour;  // to store number of cycles. Should be enough long cycles=0; // maximum size is 2,147,483,647  int zz=0;  void setup() { lcd.begin(16, 2); // fire up the LCD Wire.begin(); // and the I2C bus }  // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); }  // Convert binary coded decimal to normal decimal numbers byte bcdToDec(byte val) { return ( (val/16*10) + (val%16) ); }  // Gets the date and time from the ds1307 void getDateDs1307(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) { // Reset the register pointer Wire.beginTransmission(DS1307_I2C_ADDRESS); Wire.send(0); Wire.endTransmission(); Wire.requestFrom(DS1307_I2C_ADDRESS, 7);  // A few of these need masks because certain bits are control bits *second     = bcdToDec(Wire.receive() & 0x7f); *minute     = bcdToDec(Wire.receive()); *hour       = bcdToDec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm *dayOfWeek  = bcdToDec(Wire.receive()); *dayOfMonth = bcdToDec(Wire.receive()); *month      = bcdToDec(Wire.receive()); *year       = bcdToDec(Wire.receive()); }  void killEEPROM() { boolean dead=false; // record start time getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); sday=dayOfMonth; smonth=month; ssecond=second; sminute=minute; shour=hour; lcd.clear(); lcd.setCursor(0,0); lcd.print("Cycles so far..."); do // write and read EEPROM addresses until the first failure { for (int a=0; a<1024; a++) { EEPROM.write(a, 170); // write binary 10101010 to each EEPROM address } for (int a=0; a<1024; a++) // check each address { zz = EEPROM.read(a); if (zz!=170) { dead=true; // uh-oh, an address has died cycles-=2; // easy way out to calculate cycles once died } } cycles++; lcd.setCursor(0,1); lcd.print(cycles); for (int a=0; a<1024; a++) { EEPROM.write(a, 85); // write binary 01010101 to each EEPROM address } for (int a=0; a<1024; a++) // check each address { zz = EEPROM.read(a); if (zz!=85) { dead=true; // uh-oh, an address has died --cycles; // easy way out to calculate cycles once died } } cycles++; lcd.setCursor(0,1); lcd.print(cycles); // update the LCD } while (dead!=true); // so now one address write/read has failed, the game's up // record end time getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); fday=dayOfMonth; fmonth=month; fsecond=second; fminute=minute; fhour=hour; // display final statistics (endless loop) do { lcd.clear(); lcd.setCursor(0,0); lcd.print("Start date/time: "); lcd.setCursor(1,1); lcd.print(sday, DEC); lcd.print("/"); lcd.print(smonth, DEC); lcd.print(" "); lcd.print(shour, DEC); lcd.print(":"); if (sminute<10) { lcd.print("0"); } lcd.print(sminute, DEC); lcd.print(":"); if (ssecond<10) { lcd.print("0"); } lcd.print(ssecond, DEC); delay(2000); lcd.clear(); lcd.setCursor(0,0); lcd.print("End date/time: "); lcd.setCursor(1,1); lcd.print(fday, DEC); lcd.print("/"); lcd.print(fmonth, DEC); lcd.print(" "); lcd.print(fhour, DEC); lcd.print(":"); if (fminute<10) { lcd.print("0"); } lcd.print(fminute, DEC); lcd.print(":"); if (fsecond<10) { lcd.print("0"); } lcd.print(fsecond, DEC); delay(2000); lcd.clear(); lcd.setCursor(0,0); lcd.print("  # of cycles:"); lcd.setCursor(0,1); lcd.print(cycles); delay(2000); } while(1>0); }  void loop() { // nice intro display with countdown lcd.clear(); lcd.setCursor(0,0); lcd.print("EEPROM Destroyer"); lcd.setCursor(0,1); for (int a=99; a>0; --a) // gives user 99 seconds to change mind before start { lcd.setCursor(0,1); lcd.print("Starting in "); lcd.print(a); lcd.print("s "); delay(995); } lcd.clear(); killEEPROM(); }

If you are unfamiliar with the time-keeping section, please see part one of my Arduino+I2C tutorial. The LCD used was my quickie LCD shield - more information about that here. A quicker, painless option would be a Freetronics 16x2 LCD shield as tested here. Or you could always just send the data to the serial monitor box - however you would need to leave the PC on for a loooooong time... So instead the example sat on top of an AC adaptor (wall wart) behind a couch (sofa)  for a couple of months:

The only catch with running it from AC was the risk of possible power outages. We had one planned outage when our house PV system was installed, so I took a count reading before the mains was turned off, and corrected the sketch before starting it up again after the power cut. Nevertheless, here is a short video - showing the start and the final results of the test:

[youtube=http://www.youtube.com/watch?v=t8X0zip7TvU]

So there we have it, 1230163 cycles with each cycle writing and reading each individual EEPROM address. If repeating this odd experiment, your result will vary.

Well I hope someone out there found this interesting. Please refrain from sending emails or comments criticising the waste of a microcontroller - this was a one off. Otherwise, to keep up with new activities here at tronixtuff please subscribe to the blog, RSS feed (top-right), twitter or joining our Google Group.

If you have any questions about the processes or details in this article, please ask in our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, there is the odd competition or give-away –  and we can all learn something.

Otherwise, have fun, stay safe, be good to each other – and make something!

No comments:

Post a Comment