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:
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:
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.