EEPROM Test Code

This test program stores a string of seven character values "abcdefg" to the EEPROM. Then, it retrieves them back and prints them in the SimpleIDE Terminal.  To do so, the code performs these steps:

  1. Create an ID for the I2C bus
  2. Set up the I2C bus
  3. Send data to the I2C device on the bus
  4. Check if the I2C device is ready again
  5. Retrieve data from the I2C device on the bus
  6. Print the retrieved data to the terminal

Library Alert! This example relies on Simple Libraries in the (5/14/2014) Learn folder or later. Update your Learn Folder.

  • Open Test 24LC512 with I2C.side from Documents/SimpleIDE/Learn/Examples/Protocols 
  • Click the Run with Terminal button.
  • Verify that it displays testStr = abcdefg.

  • Now, try commenting out the two lines that make up the i2c_out function call. 
  • Then, turn off power, turn it back on, and re-run.

The i2c_in function will still be able to fetch and display the string, since the EEPROM is designed to hold data without power.

/*
  Test 24LC512 with I2C.c
  Test writes data to I2C EEPROM, then reads it back and displays it.   
*/

#include "simpletools.h"                      // Include simpletools header

i2c *eeBus;                                   // I2C bus ID

int main()                                    // Main function
{
  eeBus = i2c_newbus(28,  29,   0);           // Set up I2C bus, get bus ID

                                              // Use eeBus to write to device
  i2c_out(eeBus, 0b1010000,                   // with I2C address 0b1010000,
          32768, 2, "abcdefg", 8);            // send 2 byte address of 32768
                                              // and 8 byte data string.

  while(i2c_busy(eeBus, 0b1010000));          // Wait for EEPROM to finish
 
  char testStr[] = {0, 0, 0, 0, 0, 0, 0, 0};  // Set up test string
     
                                              // Use eeBus to read from device
  i2c_in(eeBus, 0b1010000,                    // with I2C address 0b1010000,   
         32768, 2, testStr, 8);               // send 2 byte address of 32768
                                              // & store data in 8 byte array.

  print("testStr = %s \n", testStr);          // Display result
}

 

How the I2C Test Code Works

On this page, we’ll go through the Test 24LC512 with I2C.c code a few lines at a time, to see how the i2c_out and i2c_in functions communicate with the I2C device.

First, let’s get the EEPROM datasheet.  You would need the datasheet to prototype code for any I2C device that doesn’t already have library support. 

 

About the i2c Functions

The i2c_out and i2c_in functions we’ll be using have six parameters:

  • i2c *busID – I2C bus identifier
  • int i2cAddr – The device’s 7-bit I2C address
  • int memAddr – A memory address for the chip to use for reporting or recording data 
  • int memAddrCount – Number of bytes in memAddr
  • int *data – Pointer to data byte(s) to store data that was read from, or that stores data to write to the device. 
  • int dataCount – The number of data bytes to send/receive

First, the simpletools library is included, to let us use its i2c_in and i2c_out functions. It also includes other libraries, like simpletext, which has the print function. 

#include "simpletools.h"

 

Create an ID for the I2C Bus

Each I2C bus you declare needs a bus identifier.  This identifier keeps the address in Propeller memory where information about the bus is stored.  It is declared globally — above main and not inside a function — so it can be used by any function in the program.   

i2c *eeBus;

 

Set up the I2C Bus

Next, inside main, use the identifier to set up the I2C bus. The i2c_newbus function has three parameters: sclPin, sdaPin, mode.     

int main()
{
  eeBus = i2c_newbus(28, 29, 0);

Setting sclPin to 28 and sdaPin to 29 corresponds to the circuit we are using.  Mode = 0 is for our circuit’s normal I2C configuration, with a pull-up resistor on the SCL line.  Although less common, some boards do not have a pull-up resistor on the SCL line and let the microcontroller drive it, which is when mode = 1 would be used.  The Propeller Demo Board and PE Kit platform are examples. 

The i2c_newbus function call returns information for the I2C bus ID, which gets stored in eeBus.  From this point forward, your code can pass eeBus to the i2c_in and i2c_out function’s busID parameter to select this bus (and not some other I2C bus a project might have) for communication. 

 

Send Data to the I2C Device on the Bus

Sending data to an I2C device with i2c_out involves telling it to find some memory address and store a value there.  Or, in the case of our EEPROM, find a memory address, and store eight values starting from that memory address.  Let’s look at how the i2c_out function parameters are used here:

  • i2c *busID – I2C bus identifier (eeBus)
  • int i2cAddr – The device’s 7-bit I2C address (0b1010000)
  • int memAddr – A memory address (32768, don’t go lower! See “Did You Know?” on the next page.) 
  • int memAddrCount – Number of bytes in memAddr (2)
  • int *data – Pointer (automatically generated by the compiler when it sees a string like "abcdefg") 
  • int dataCount – The number of data bytes (8)

Why 8 bytes for 7 characters?  The string are zero terminated, so there’s a 0 that follows those characters that doesn’t show.  The print function needs that zero to properly display strings.  So, make sure to add 1 to the size of your character strings contained in quotes.

  i2c_out(eeBus, 0b1010000, 32768, 2, "abcdefg", 8);

So, how would you go about finding out details like the EEPROM’s I2C address? Or the number of bytes that you have to send it to pick one of its internal memory addresses?  Read the device’s datasheet.  They are usually written for industry professionals, so you might need to read and re-read certain sections and write pieces of test code to check how the device replies.  But with time and persistence, you will succeed, and it gets easier with practice.  

Read some selections from the I2C datasheet to understand the I2C address above. As you read, the main tasks are to focus on the I2C address, how to select one of the EEPROM’s memory addresses, and how to send it data bytes.  The i2c_out and i2c_in functions take care of details like start conditions, read/write bits, and ACK/NACK signals, so don’t worry about those portions.

  • Open the 24LC512 series datasheet.
  • Read Section 2.1, the first paragraph of section 5.0, and Figure 5-1.  They describe the device’s I2C address.
  • Check half-way through the second paragraph in Section 5.0 (and Figure 5-2) to see an example of how it describes the two address bytes.
  • Read sections 6.1 and 6.2 (and Figures 6-1 and 6-2) for the data transfer rules.  The i2c_out call is taking advantage of the EEPROM’s page write features by sending it multiple bytes.

Some I2C devices don’t also need a memory address, just data.  If that’s the case for your device, use NULL in place of the memory address, and 0 for the number of bytes in the memory address.  Also use NULL and 0 in transactions that involve just issuing a memory address without data, in which case, NULL and 0 would be in place of *data and dataCount.  

 

Check if the I2C Device is Ready Again

Some I2C devices stop responding while they are processing, such as our EEPROM while it is storing data it just received.  Devices like this can be polled for availability with the i2c_busy function, which returns 1 if the device is not responding (because it’s busy), or 0 if it responds (which means it’s ready for more).  Putting the i2c_busy function call in a while loop lets the program execution pause until the I2C device is available again.

  while(i2c_busy(eeBus, 0b1010000));

 

Retrieve Data from the I2C Device on the Bus

To verify that the i2c_out function actually stored the data in the EEPROM, we’ll use i2c_in to retrieve it.  But first, we need to make a character array full of zeroes as a place to store the data that’s about to be retrieved from the EEPROM. 

  char testStr[] = {0, 0, 0, 0, 0, 0, 0, 0};

(An alternate approach to this string of zeroes would be char testStr[8]; followed by memset(testStr, 0, 8);)

Now the program is ready for the read operation. This i2c_in call selects the same I2C bus, I2C chip address, and points to the EEPROM’s 32768 memory address.  The difference is the *data parameter.  Instead of sending a pointer to "abcdefg" string, this call passes a pointer to the testStr array that was just created.

  i2c_in(eeBus, 0b1010000, 32768, 2, testStr, 8);

 

Printing the Retrieved Data

Last, this print statement displays the data in the testStr array with the SimpleIDE Terminal. If testStr contains "abcdefg" then mission accomplished!

  print("testStr = %s \n", testStr);          // Display result