I2C -- EEPROM testing

That hollow clonking sound you've been hearing for the last couple of days has been me beating my head against the wall trying to get I2C working with a serial (I2C) EEPROM. I'm running Kubuntu 8.1, a Diecimila with a Lady Ada 328, and IDE v13.

I think I'm writing to it, but bugger-all if I'm getting anything reasonable back out of it; I can get a few (best so far has been 52 of 128) bytes to read back correctly, but that's it.

Here's the Sketch I've been using, in case someone can spot an otherwise obvious mistook I've made:

/******************************************************************************
 *     20090313 -- I2C_DEVICE DEVICE writing
 *     by D.K.Merriman
 *
 *  Arduino analog input 5 - I2C SCL
 *  Arduino analog input 4 - I2C SDA
 *
 ******************************************************************************/

#include "Wire.h"

#define ChipSize 128  // size of EEPROM in bytes
#define PageSize 32  // bytes per page (from datasheet)
#define ChipAddress 0x50  // address of the EEPROM - pretending lowest bit doesn't exist: 1010 000x = 101 0000 = 0x50

void setup()
{
  Wire.begin();
  Serial.begin(9600);
}

void loop()
{
  int x;
  int y;
  int z;
  int PageCount= (ChipSize / PageSize);  // number of pages in the EEPROM

  byte SourceData[ChipSize];  // Source Data for testing
  byte TestData[ChipSize];  // for reading EEPROM data back
  byte t[PageSize];  // array for temp buffer (needed to write to eeprom in page-size chunks)

  delay(2000);  // just to give yourself time to switch over to the serial monitor before things start happening
  
  Serial.println("Loading Source data array");
  for (x = 0; x < ChipSize; x++) {
    SourceData[x] = 0x38;  // ASCII '8'
    TestData[x] = 0x00;  // make sure the test data array is "empty"
  }
  Serial.println();
  Serial.println("Source data array Initialized - Writing to EEPROM");
  
  for (x = 0; x < PageCount; x++) {
    for (y = 0; y < PageSize; y++) {
      t[y] = SourceData[(x * PageSize) + y];
    }
    i2c_eeprom_write_page(ChipAddress, x, t, PageSize);
    delay(1);  // a couple of examples have indicated that a delay is needed here. I've gone from 1-100 with no effect.
  }

  Serial.println();
  Serial.println("EEPROM written");
  
  delay(5000);  // kill some time, just on general principles
  
  Serial.println();
  Serial.println("Reading data back from EEPROM");
  
  for (x = 0; x < ChipSize; x++) {
    TestData[x] = i2c_eeprom_read_byte(ChipAddress, x);
      }
  Serial.println("All EEPROM data read");

  for (x = 0; x < ChipSize; x++) {
    if (TestData[x] != SourceData[x]) {
        Serial.print("Data mismatch at ");
        Serial.print(x);
        Serial.print(" Received: ");
        Serial.print(TestData[x]);
        Serial.print(" Should have been: ");
        Serial.println(SourceData[x]);
    }
  }
}

// I2C Read/Write routines

void i2c_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) {
  int rdata = data;
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.send(rdata);
  Wire.endTransmission();
}

// WARNING: address is a page address, 6-bit end will wrap around
// also, data can be maximum of about 30 bytes, because the Wire library has a buffer of 32 bytes
void i2c_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) {
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddresspage >> 8)); // MSB
  Wire.send((int)(eeaddresspage & 0xFF)); // LSB
  byte c;
  for ( c = 0; c < length; c++)
    Wire.send(data[c]);
  Wire.endTransmission();
}

byte i2c_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) {
  byte rdata = 0xFF;
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,1);
  if (Wire.available()) rdata = Wire.receive();
  return rdata;
}

// maybe let's not read more than 30 or 32 bytes at a time!
void i2c_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) {
  Wire.beginTransmission(deviceaddress);
  Wire.send((int)(eeaddress >> 8)); // MSB
  Wire.send((int)(eeaddress & 0xFF)); // LSB
  Wire.endTransmission();
  Wire.requestFrom(deviceaddress,length);
  int c = 0;
  for ( c = 0; c < length; c++ )
    if (Wire.available()) buffer[c] = Wire.receive();
}

The specific EEPROM I've been using is a CAT24WC33 (32K-bit) from FunGizmos at: http://store.fungizmos.com/index.php?main_page=product_info&cPath=68&products_id=211
where you can also get the datasheet from: http://www.catsemi.com/documents/24WC33.pdf.

IF I'm understanding things I've read elsewhere, there may be some question as to the reliability of the Wire library, as regards I2C -- can anyone even vaguely authoritative confirm or deny that?

Any suggestions on where/why I can't seem to read the EEPROM would be appreciated.

/me

Yep know what you mean .
Im playing with a AT24C256 and thats driving me mad too.

I think it`s all timing !!

I can`t do a Start, Write msb_address , Write lsb_address , write data , write data , stop , so I can store a int value.

I have to insert a small delay if I write .... Stop , read.....

also .. on a differant i/o chip I have to do a :- set to 0 , is it 0 , set to 255 , is it 255 , set to 0 , to make that work.

I have used the wire library with the 24LC256 EEPROM from Microchip with no problems.
Between writing any 2 bytes to the eeprom, a delay of at least 5ms is required, according to the datasheet.
BTW, the link to the datasheet you posted is broken.

Florinc, your comment makes me believe that the wire library is probably good (I figured it was, but...).

I've looked at the datasheet (the problem seems to be at Catalyst' end; it wouldn't download for me, either), and it specs a MAX of 10mS for writes to complete before it'll respond to another request -- which doesn't quite explain why the varying values of delay I put in at the end of the writes didn't work (I started with a delay of 10, then 20, then 50, and finally 100).

Curiouser and curiouser... :-?

Here are some snippets of code for writing to eeprom tested by me (Arduino 011, Atmega 328):

// I2C Bus address of 24LC256 EEPROM;
// if more than one eeprom, they will have different addresses (h/w configured);
#define I2C_ID 0x50


// global address of the last written byte;
unsigned int crtWriteAddress = 0;


void writeByte(int i2cId, unsigned int eeaddress, byte data)
{
  int rdata = data;
  Wire.beginTransmission(i2cId);
  Wire.send((int)(eeaddress >> 8));    // Address High Byte
  Wire.send((int)(eeaddress & 0xFF));  // Address Low Byte
  Wire.send(rdata);
  Wire.endTransmission();
  delay(10);                         // NEED THIS DELAY!
}


void writeNextByte(byte data)
{
  writeByte(I2C_ID, crtWriteAddress, data);
  crtWriteAddress++;
}


void loop()
{
    if (Serial.available() > 0)
    {
      // read next character from COM port;
      byte incomingByte = Serial.read();
      writeNextByte(incomingByte);
    }
}

Interestingly, you use the same ID (0x50) for your eeprom (I would expect that the ID to be different, since it is a different manufacturer, but I may be wrong).

I moved my delay to the end of the EEPROM write routine, as you have it, without effect. :-[

The datasheet for my EEPROM shows the addressing byte as:
1 0 1 0 A2 A1 A0 R/W; ignoring the r/w bit (which Wire is supposed to take care of), that leaves me with 101 0000, or 0x50. I've gone out and checked a few other Googled references, and found that 1010xxx seems to be fairly common for EEPROMs:

As an example, the Microchip 24LC01B Serial EEPROM has A0, A1, and A2 inputs, but the high order address bits are hardwired to 1010 – this allows slave addresses of the form 1010xxx, but no more than 8 of these devices on any I2C bus due to address conflict

http://www.google.com/url?sa=t&source=web&ct=res&cd=5&url=http%3A%2F%2Fcoen.boisestate.edu%2FSsmith%2FBusInt%2Foverheads%2FI2C.doc&ei=ePK-SefhMoikNdL7rKsN&usg=AFQjCNF9FY8mTVWFIqzCwpTZwFRIcJVTOQ&sig2=K2dkY3nsYN2xUlgg1tW6Kw

(shrugs) Beats me. :wink:

Would it be within the realm of possibility that you could try my code using (one of) your chip(s)? Just to eliminate biological errors?

Sure, no problem, but you need to give me a few days. I only have one eeprom chip programmed/written and mounted in a clock and I do not want to disassemble it. I am supposed to receive another batch of PCBs these days, for the same purpose (clock). So, if you bare with me, I will definitely try out your code and will make it working.

One thing I noticed by looking at your code is that you use write_page. I never tried that. I only wrote byte by byte. Not even that (writeByte) works for you?

Can you try something very simple like:

for (int addr=0; addr<128; addr++)
{
i2c_eeprom_write_byte(0x50, addr, addr);
}

And honestly, I am trying to find a reason behind "page write" and I cannot really. The only saving I see is in the transmission of data over I2C lines. But the "engraving" of the data in the eeprom cells should take the same time (5ms per byte), regardless of how many bytes one sends. Or does "write page" write multiple bytes at once, in a parallel fashion (which I highly doubt)?

Since this isn't a matter of national security or anything, I'm not going to fuss if you need/want a couple days to help me :smiley:

I've tried both write_byte and write_page, neither has worked (yet); all I've gotten out of this exercise has been a headache :stuck_out_tongue:

Are you sure this is not a hardware problem (wiring, soldering etc)?
Are you using this setup?

 * SETUP:           _ _                                
 * Arduino GND- A0-|oU |-Vcc  to Arduino Vcc
 * Arduino GND- A1-|   |-WP   to GND for now. Set to Vcc for write protection.
 * Arduino GND- A2-|   |-SCL  to Arduino 5
 * Arduino GND-Vss-|   |-SDA  to Arduino 4
 *                  ---       (A2, A1, A0 to GND for 1010000 (0x50) address.)
 *                            If set to Vcc adds to address (1010,A2,A1,A0)

Do you have your eeprom chip on a socket? Can you try another one?

That's the way I've got it wired -- I've check several times, and that I'm getting data in/out of the first page of the EEPROM makes me believe it isn't my hookup. I've only got the one chip at present; it's stuck in a breadboard, and I've verified that I don't have any bent pins on it.

I'm starting to wonder if I shouldn't call this in as an X-file :o

BBR has working 24XXyyy code at wulfden.org, if you want to compare your code to something that's known to work (I've used his code myself).

-j

Regarding page access, I seem to remember that you could not read a page as big as listed in the data sheet. (A shared buffer or something.) Try decreasing PageSize to say 16 bytes. However, as florinc said, I'm not sure what page access gets you.

KG4 - have you got a link to a page? Just went to the general address (www.wulfden.org), and couldn't figure out where I should go from there...

BroHogan - from the data sheet, the time to actually write a page is the same as for actually writing a single byte, so loading the EEPROM with a page of data before writing would appear to be a little bit of a time saver when dealing with more than page-size chunks of data.

OK, that's good, but still try to reduce your page size to see if that helps.

Actually, I have.

At first, I thought I might be running into the 30-32 byte Wire buffer (which, incidentally, I suspect should probably be increased to a definite 64, since some of the EEPROM sheets I've looked at are that big), and dropped to 24, 16, and even 8 -- all without success.

click through products -> downloads -> code -> arduino

http://www.wulfden.org/downloads/code/arduino/I2C_EEPROM.pde

This code worked for me, and I've modified for 1024 (and 512, IIRC) models of that FLASH family with little effort.

-j

Thanks -- I'll give it a go.

I was able to reproduce your test (luckily I got mt PCBs on Friday).
Firstly, the code did not work, as in your case.
I tried to alter it as little as possible to make it working (just writing byte by byte worked fine; I was more interested in page write this time).

This is what I found:

  1. you had this line:
    i2c_eeprom_write_page(ChipAddress, x, t, PageSize);
    It should be
    i2c_eeprom_write_page(ChipAddress, x * PageSize, t, PageSize);
    since you don't want to write just the first page of 32 bytes over and over again.

  2. Even then, 32 bytes seem to be too long for the eeprom to handle. The last few bytes of the page were never written (or written with 0 actually). So I changed the page size to 16 and it seems fine now.

  3. I used 10ms as delay between writes. I did not bother to test with other values.

My eeprom is 24LC512. I run Arduino IDE 0011, with an atmega 328. (I also have some other I2C devices connected, not that it matters).

So, if you change those 3 things in your sketch, it should work. If it does not, it may be very well a wiring issue.

Let me know how it goes.

THANK YOU!

  1. I wasn't too worried about writing the same 32 bytes over and over; I was more after just being able to read/write - but I still appreciate it.

  2. The EEPROM spec says that 32 bytes is the page size; the comments on the eeprom buffer routine suggest that the Wire buffer might be the problem there. I'll back off to 16 bytes, and see how that goes.

  3. I'll set it to 10mS delay, and work with that, then.

Perhaps you know something that isn't real clear in the datasheets: they specify a page size, but what I'm not sure of is whether or not page boundaries are logical or hardwired -- that is, can a 32-byte page run from, say, memory location 0x17 - 0x37, or would it have to be at 0x00/0x20/0x40 etc ?

Again, thank you for taking the time to double-check me on this.

You are welcome.
I haven't seen the output of your sketch, but when I ran it in my environment, I noticed that some of the values (0x38) are written correctly. That makes me think that the writing works sometimes. So the wiring may be good after all.
But since your eeprom is not made by microchip, it may differ a bit from the original, I assume. So if you are looking at the microchip datasheet, that may only be what the manufacturer aimed for. Again, just a guess.

One more thing: your sketch wrote correctly the first 32 bytes. The reported mismatches start at byte 33, and are caused by the issue #1.