Electronic – Can’t read written data out of 24AA1025

eepromi2cmicrochipmplabpic

I've got a PIC18F with MSSP that I'm interfacing with a 24AA1025. I'm using MPLAB 8 and the functions from C18 to make my life easier. The only problem is that I've (supposedly) written a byte to the 24AA1025, but when I read it back, I get 0xFF instead of the byte I wrote.

Here's how I have the EEPROM wired up:

A0 - GND
A1 - GND
A2 - Vcc
Vss - GND
SDA - pulled up to +5 via 2.2k resistor, and connected to SDA on PIC
SCL - pulled up to +5 via 2.2k resistor, and connected to SCL on PIC
WP - Vss
Vcc - 5V

Here's my write function (now edited with working code):

bool I2CWriteByte( long address, unsigned char data)
{
    unsigned char ret;
    unsigned char control_byte;
    unsigned char high_address_byte;
    unsigned char low_address_byte;

    control_byte = (address >= 65536) ? 0b10101000 : 0b10100000;
    high_address_byte = (char)((address & 0x0000FF00) >> 8);
    low_address_byte = (char)(address & 0x000000FF);

    IdleI2C();

    // perform ack polling around control byte sending every time
    ret = SendControlByte( control_byte);
    if( ret == -1)
        return false;

    ret = WriteI2C( high_address_byte);
    if( ret == -1)
        return false;
    ret = WriteI2C( low_address_byte);
    if( ret == -1)
        return false;
    ret = WriteI2C( data);
    if( ret == -1)
        return false;

    StopI2C();
    return true;
}

Here's my read function (now edited with working code):

bool I2CReadByte( long address, unsigned char* data)
{
    unsigned char ret;
    // to do a read, first do part of a write but don't send the data byte, then send a new control byte with bit 0 set to 1 for read.
    // see 24AA1025 datasheet page 12
    unsigned char control_byte;
    unsigned char high_address_byte;
    unsigned char low_address_byte;

    control_byte = (address >= 65536) ? 0b10101000 : 0b10100000;
    high_address_byte = (char)((address & 0x0000FF00) >> 8);
    low_address_byte = (char)(address & 0x000000FF);

    IdleI2C();
    ret = SendControlByte( control_byte);
    if( ret == -1)
        return false;

    ret = WriteI2C( high_address_byte);
    if( ret == -1)
        return false;
    ret = WriteI2C( low_address_byte);
    if( ret == -1)
        return false;

    control_byte = (address >= 65536) ? 0b10101001 : 0b10100001;
    ret = SendControlByte( control_byte);
    if( ret == -1)
        return false;

    // now return value
    *data = ReadI2C();
    StopI2C();
    return true;
}

EDIT — The all-important SendControlByte() function, which does the requisite ack polling:

bool SendControlByte( unsigned char control_byte)
{
    bool nack;
    bool ret;

    nack = true;

    while( nack) {
        StartI2C();
        ret = WriteI2C( control_byte);
        if( ret == -1)
            return false;
        if( SSPCON2bits.ACKSTAT == 0)
            nack = false;
    }
}

WriteI2C never returns an error, so I assume that it actually worked…

I used my logic sniffer's I2C protocol analysis tool, and it sure looks like all of the data is being sent/received properly:

enter image description here

Can anyone suggest something to do next for debugging? The control byte looks correct, as it is 0b1010 after START, followed by the block identifier, A0, A1, and R/!W. I have tested >64KB addresses and confirmed that B1 is set properly. My EEPROM has A0 and A1 grounded, so that looks correct as well. R/!W is low for writes and high just before the read. The only thing I haven't done yet is added a delay after the write, but I will give that a shot tomorrow.

EDIT — The I2C analysis option does show what you guys have been saying:

enter image description here

Best Answer

My guess is that the problem indeed is that you need to delay after the write.

The device will be busy for roughly 3-5 milliseconds after a write, during which it will not respond to any commands. If you issue a read during this time period, the device will ignore it, and the SDA line will remain high - which would indeed lead to a result of all ones being read on the clock pulses.

First off, try adding a delay after the write, perhaps 10 milliseconds or so.
If that works, check the datasheet chapter on acknowledge polling, to improve the performance. In short, acknowledge polling means sending a write command over and over until the device acknowledges it, at which point you know that the write cycle is complete.