SOLVED: I2C Slave Data Request Issue

Hey there!

I'm currently working on emulating the Atmel AT24C01 I2C EEPROM using an Arduino Pro (3.3v, ATMEGA 328 @ 8MHz). The reason for doing so is that I am working with a device that uses proprietary chip, and that chip reads data from the aforementioned EEPROM upon boot. In my application I need to change those values periodically and cannot store them. The proprietary chip acts as an I2C master clocking at 400kHz (according to the docs) and looks for I2C devices upon boot on the 0x50 address. The proprietary chip reads reads data perfectly from the EEPROM, as can be seen in the logic analyzer snapshots following this post. Additionally, I personally programmed the EEPROM.

The activity on the I2C data lines between the chip and the EEPROM is as follows (can be confirmed from the logic analyzer snapshot):

  • Write Bytes {0x50, 0x00} //Detect I2C devices
  • Read Byte {0x50, 0x87, 0x50, data_byte} //Read first byte of data (memory address 0x87, response should be 0x00)
  • Various other read/write requests... //Read various other bytes of data

Now I attempted to replace the EEPROM chip with the Arduino...

To emulate the chip, I've done the following steps:

  • Modified the I2C speed in twi.h to 400kHz: "#define TWI_FREQ 400000L"
  • Added 1.5k pull up resistors on SDA <-> Pin 4 and SCL <-> Pin 5
  • Created slave device in the code on address 0x50
  • The code at the end of this post is what I am using to test replying to the first requested byte.

When the proprietary chip writes data to the I2C line, the onReceive() interrupt gets called fine. The problem lies when the proprietary chip reads data as onRequest() never gets called. The logic analyzer output is also attached below.

I've looked into various causations and stumbled upon the following post by el_supremo on this thread: http://arduino.cc/forum/index.php/topic,109335.0.html

AFAIK, When you specify an address to the I2C library it has to be the 7-bit address. Internally, the library shifts this up one bit which sets the low order bit to zero (which is good for a write). If you have asked for a read, the library sets the low order bit to one.

So, specifying 0x40 to the library means that internally this will be shifted to become 0x80 and when writing it will use 0x80. For reading it will use 0x81. [...]

Could this be the cause of my issue? That the Arduino is waiting for read requests on a different address than 0x50? If so, I'm not sure how to address this (pun intended)!

Any help would be much appreciated! :slight_smile:

Best,
Aakash

Logic analyzer output for I2C connection from AT24C01 <-> Proprietary chip

Logic analyzer output for I2C connection from Arduino <-> Proprietary chip

Logic analyzer output for I2C connection from Arduino <-> Proprietary chip [Zoomed into actual read request]:

Code (currently only attempts to handle first read request):

#include <Wire.h>

volatile byte rQ;
void setup()
{
 Wire.begin(0x50);
 Wire.onRequest(requestEvent);
 Wire.onReceive(receiveEvent);
}

void loop()
{
}

void requestEvent()
{
    if(rQ == 0x87){
      Wire.write(0);
  }
}

void receiveEvent(int iData){
  rQ = Wire.read();
}

How do you know the requestEvent handler is not called but the receiveEvent is? Have you removed the if statement for testing and just always return a value (I wouldn't take 0 as the value but some bit pattern you easily recognize on the analyzer).

pylon,

Thanks for your reply.

I have confirmed that onReceive() is being called and onRequest() isn't in two ways:

I set the LED pin 13 to HIGH in setup() and set it to LOW inside the onReceive() interrupt, and it turned off the LED when data was written by the master. I have done the same thing, except removed the statement from onReceive() to set the LED pin to low, and placed it in onRequest() and the LED remained on throughout the test.

I have also placed a Serial.print(rQ,HEX); statement inside of onReceive() and the bytes that were written to the logic analyzer output were also written to the serial console.

Regarding the 'if' statement: I have also removed it and always returned a value in my tests also. The logic analyzer output did not seem to show that either (I expected that since the above test regarding onRequest() failed)

Best,
Aakash

If you look closely at the analyzer output, the read request is not acknowledged so it's not extraordinary to not call the requestEvent() is not called.

Do you have the possibility to connect another Arduino as the Master to send the same code as the proprietary chip would do. If that works you have to take a closer look at the details of the timing.

Can you post a more zooomed image of just the read request, from the start condition to the stop condition?

The reason it's not working is because the Wire library does not support repeated starts. A repeated start is when you basically skip sending the stop bit after a transaction has completed and just send an additional start bit to start the next transaction. It has the benefit of decreasing total data transfer time and not freeing up the bus in case another master is on the system and wants to use the bus.

I believe they issued a patch to fix the master side of the Wire library to generate repeated starts but I don't recall reading anything about patching the slave side to handle repeated starts. It is possible to modify the Wire library to make it work, as I did it some time ago, but unfortunately I lost that bit of code.

wayneft,

Yep! That was it!

By applying the following patch by Nate Williams I was able to resolve this issue:

Thanks for the help everyone!

Aakash

Thanks guys

I also needed this fix.

I'm a bit surprised that this patch has not been applied to the released library, as this fix is now over 3 years old.

Perhaps it causes some other problems, or the Arduino team just doesn't have time to do regression testing.

Its a bit of a pain, as I'm hoping to publish some code which emulates a I2C to SPI bridge IC, and anyone using my code would also need to apply this fix to their Wire lib :frowning: