Introduction: Reading and Writing Data to External EEPROM Using Arduino

EEPROM stands for Electrically Erasable Programmable Read-Only Memory.

EEPROM is very important and useful because it is a non-volatile form of memory. This means that even when the board is powered off, the EEPROM chip still retains the program that was written to it. So when you power off the board and then power it back on, the program that was written to the EEPROM can be run. So basically, the EEPROM stores and runs a program no matter what. This means you can turn off a device, keep it off for 3 days, and come back and turn it on and it can still run the program that was programmed in it. This is how most consumer electronic devices work.

This project is sponsored by LCSC. I have been using electronic components from LCSC.com. LCSC has a strong commitment to offering a wide selection of genuine, high-quality electronic components at the best price with a global shipping network to over 200 countries. Sign up today and get $8 off on your first order.

EEPROM is also very efficient in that individual bytes in a traditional EEPROM can be independently read, erased, and rewritten. In most other kinds of non-volatile memory, this can't be done. Serial EEPROM devices like the Microchip 24-series EEPROM allow you to add more memory to any device that can speak I²C.

Supplies

Step 1: EEPROM Basics

The Microchip 24LC2512 chip can be purchased in an 8 pin DIP package. The pins on the 24LC512 are pretty straight-forward and consist of power(8), GND(4), write protection(7), SCL/SDA(6,5), and three address pins(1, 2, 3).

A Brief History of ROM

Early "Stored-Program" type computers — such as desk calculators and keyboard interpreters — began using ROM in the form of Diode Matrix ROM. This was a memory made up of discrete semiconductor diodes placed on a specially organized PCB. This gave way to Mask ROM with the advent of integrated circuits. Mask ROM was a lot like Diode Matrix ROM only it was implemented on a much smaller scale. This meant, however, that you couldn't just move a couple of diodes around with a soldering iron and reprogram it. Mask ROM had to be programmed by the manufacturer and was thereafter not alterable.

Unfortunately, Mask ROM was expensive and took a long time to produce because each new program required a brand new device to be manufactured by a foundry. In 1956, however, this problem was solved with the invention of PROM (Programmable ROM) which allowed developers to program the chips themselves. That meant manufacturers could produce millions of the same unprogrammed device which made it cheaper and more practical. PROM, however, could only be written to once using a high-voltage programming device. After a PROM device was programmed, there was no way to return the device to its unprogrammed state.

This changed in 1971 with the invention of EPROM (Erasable Programmable ROM) which — besides adding another letter to the acronym — brought with it the ability to erase the device and return it to a "blank" state using a strong UV light source. That's right, you had to shine a bright light on the IC to reprogram it, how cool is that? Well, it turns out it's pretty cool unless you're a developer working on firmware in which case you'd really like to be able to reprogram the device using electrical signals. This finally became a reality in 1983 with the development of EEPROM (Electrically Erasable Programmable ROM) and with that, we arrive at the current day unwieldy acronym.

Step 2: Quirks of EEPROM

There are two major drawbacks to EEPROM as a method of data storage. In most applications, the pros outweigh the cons, but you should be aware of them before incorporating EEPROM into your next design.

First of all, the technology that makes EEPROM work also limits the number of times that it can be re-written. This has to do with electrons becoming trapped in the transistors that make up the ROM and building up until the charge difference between a "1" and a "0" is unrecognizable. But don't worry, most EEPROMs have a maximum re-write number of 1 million or more. As long as you're not continuously writing to the EEPROM it's unlikely you'll hit this maximum. Secondly, EEPROM will not be erased if you remove power from it, but it won't hold onto your data indefinitely. Electrons can drift out of the transistors and through the insulator, effectively erasing the EEPROM over time. That said, this usually occurs over the course of years (although it can be accelerated by heat). Most manufacturers say that your data is safe on EEPROM for 10 years or more at room temperature. And there's one more thing you should keep in mind when selecting an EEPROM device for your project. EEPROM capacity is measured in bits and not bytes. A 512K EEPROM will hold 512Kbits of data, in other words, just 64KB.

Step 3: Arduino Hardware Hookup

Okay, now that we know what EEPROM is, let's hook one up and see what it can do! In order to get our device talking, we'll need to connect power as well as I²C serial lines. This device, in particular, runs at 5VDC so we'll connect it to the 5V output of our Arduino UNO. Also, the I²C lines will need pull-up resistors for communication to happen correctly. The value of these resistors depends on the capacitance of the lines and frequency you want to communicate it, but a good rule of thumb for non-critical applications is just kept it in the kΩ range. In this example, we'll use 4.7kΩ pull-up resistors.

There are three pins on this device to select the I²C address, this way you can have more than one EEPROM on the bus and address them each differently. You could just ground them all, but we'll be wiring them so that we can drop in a higher-capacity device later in the tutorial.

We'll use a breadboard to connect everything together. The diagram below shows the correct hookup for most I²C EEPROM devices, including the Microchip 24-series EEPROM that we sell.

Step 4: Reading and Writing

Most of the time when you're using an EEPROM in conjunction with a microcontroller you won't actually need to see all of the contents of the memory at once. You'll just read and write bytes here and there as needed. In this example, however, we're going to write an entire file to EEPROM and then read all of it back off so we can view it on our computer. This should get us comfortable with the idea of using EEPROM and also give us a feeling for how much data can really fit on a small device.

Write Something

Our example sketch will simply take any byte that comes in over the serial port and writes it to the EEPROM, keeping track along the way of how many bytes we've written to memory.

Writing a byte of memory to the EEPROM generally happens in three steps:

  1. Send the Most Significant Byte of the memory address that you want to write to.
  2. Send the Least Significant Byte of the memory address that you want to write to.
  3. Send the data byte that you would like to store at this location.

There are probably a few key words there that bare explaining:

Memory Addresses

If you imagine all of the bytes in a 512 Kbit EEPROM standing in a line from 0 to 64000 — because there are 8 bits to a byte and therefore you can fit 64000 bytes on a 512 Kbit EEPROM — then a memory address is the place in line where you would find a particular byte. We need to send that address to the EEPROM so it knows where to put the byte that we're sending.

Most Significant and Least Significant Bytes

Because there are 32000 possible places in a 256 Kbit EEPROM — and because 255 is the largest number you can encode in one byte — we need to send this address in two bytes. First, we send the Most Significant Byte (MSB) — the first 8 bits in this case. Then we send the Least Significant Byte (LSB) — the second 8 bits. Why? Because this is how the device expects to receive them, that's all.

Page Writing

Writing one byte at a time is fine, but most EEPROM devices have something called a "page write buffer" which allows you to write multiple bytes at a time the same way you would a single byte. We'll be taking advantage of this in our example sketch. The EEPROM uses an internal counter that automatically increases the memory location with each following data byte it receives. Once a memory address has been sent we can follow it with up to 64 bytes of data. The EEPROM assumes (rightly) that an address of 312 followed by 10 bytes will record byte 0 at address 312, byte 1 at address 313, byte 2 at address 314, and so on.

Read Something

Reading from the EEPROM basically follows the same three-step process as writing to the EEPROM:

  1. Send the Most Significant Byte of the memory address that you want to write to.
  2. Send the Least Significant Byte of the memory address that you want to write to.
  3. Ask for the data byte at that location.

Step 5: Schematics and Code

Code:

#include <Wire.h>
#define eeprom 0x50 //defines the base address of the EEPROM
void setup()  {
  Wire.begin(); //creates a Wire object
  Serial.begin(9600); 
  unsigned int address = 0; //first address of the EEPROM
  Serial.println("We write the zip code 22222, a zip code");
  for(address = 0; address< 5; address++) 
    writeEEPROM(eeprom, address, '2'); // Writes 22222 to the EEPROM
  for(address = 0; address< 5; address++) {
    Serial.print(readEEPROM(eeprom, address), HEX); 
    }
  }
void loop() {
  /*there's nothing in the loop() function because we don't want the arduino to 
  repeatedly write the same thing to the EEPROM over and over. 
  We just want a one-time write, so the loop() function is avoided with EEPROMs.*/
}
//defines the writeEEPROM function
void writeEEPROM(int deviceaddress, unsigned int eeaddress, byte data ) {
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));      //writes the MSB
  Wire.write((int)(eeaddress & 0xFF));    //writes the LSB
  Wire.write(data);
  Wire.endTransmission();
  }
//defines the readEEPROM function
byte readEEPROM(int deviceaddress, unsigned int eeaddress ) {
  byte rdata = 0xFF;
  Wire.beginTransmission(deviceaddress);
  Wire.write((int)(eeaddress >> 8));      //writes the MSB
  Wire.write((int)(eeaddress & 0xFF));    //writes the LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,1);
  if (Wire.available()) 
    rdata = Wire.read();
  return rdata;
  }