I2C communications conflict.

Hi All!

I'm trying to interface the PCF85263A RTC and CAT24M01 EEPROM on the same I2C bus.

For some reason when I try to get the time from the RTC while the EEPROM is on the bus I get an incorrect response. Take the EEPROM off and the RTC works fine.

They have similar addresses, 0x51 for the RTC and 0x50 for the EEPROM.
I have 2.2K pull-ups on both the SDA and SCL lines.

Would anyone have any idea what might be causing this issue?

Thanks

PeaTear

Hello there!

You mentioned that if you try to talk to the RTC with the EEPROM connected you get an error. What happens if you try to talk to the EEPROM when they are both on the bus?

When you do get the incorrect response, what response do you get, and what response do you expect?

any idea what might be causing this issue?

What issue? Post the code, using code tags, and the output that illustrates the problem.

Have you tried changing the I2C address of the EEPROM? The A0 and A1 pins allow for 4 different addresses.

I have 2.2K pull-ups on both the SDA and SCL lines.

In theory you're over spec with that value. I2C devices have an upper limit of 2mA sinking current.

They have similar addresses, 0x51 for the RTC and 0x50 for the EEPROM.

This is almost never a problem.

If you have both devices connected, what's the total length of the I2C bus in your setup then? Is that a breadboard setup? Did you check the connections? Did you check that the pull-ups exist only once on the bus?

@groundFungus beat me to it but here’s a more verbose answer anyway.

The EEPROM does not respond to only 0x50. The low order bit of 0x50 is the 17th bit of a memory address (a16) and the next two bits select a device. Therefore the 0x51 address recognized by the PCF85263A is also a valid address for the EEPROM.
You can get around this by assigning the EEPROM a non-zero device address using the A1 and A2 pins on the chip. e.g. set A1 high and leave A2 low (they have pull-down resistors on the chip) so that you can address it as 0x52. You can still use the low order bit of the I2C address to be able to access all the memory.

Pete

1. 0x51 is the address of RTC -- it is confirmed in data sheet.

2. Look at the following, which tells a peculiar (not usually seen for other EEPROMs) way of computing the device address (not really clear to me); but, thee is no harm to make a try.

If a16 is the refelection of MS-bit (1) and A2-A1 are left open (they have internal pull down resistors) or grounded, the device address becomes (1010 001 = 0x51). It will conflict with RTC's address.

3. Try connecting A2 and A1 at 5V. The address of the EEPROM (1010111 = 0x57) will, at least, far away from 0x51.

4. If the problem still sustains, remove the RTC, check the EEPROM alone at address 0x57. If the EEPROM does not respond at 0x57, we need to understand the meaning/significance of the a16 bit of the 7-bit address.

The following simple codes could be executed to check the presence of the EEPROM at address 0x57.

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

   do
  {
     Wire.beginTransmission(0x57);
     busStatus = Wire.endTransmission();
  }
  while (busStatus !=0x00);
  Serial.print("EEPROM is Found at 0x57..!");
}

The low order bit of the I2C address is used to address the memory - not a chip. If you set A1 and A2 high, that one chip will respond to 0x56 and 0x57 because 0x56 is addressing the lower half of its memory and 0x57 addresses the upper half.

Pete

Hey All,

Sorry for committing the cardinal sin and not posting my code. I’m using the a slightly modified version of RTClib to control the RTC, running the following test script.

// Date and time functions using a PCF85263 RTC connected via I2C and Wire lib
#include <Wire.h>
#include "RTClib.h"

#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
   #define Serial SerialUSB
#endif

RTC_PCF85263 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
  Serial.begin(115200);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  if (! rtc.initialized()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // March 12, 2017 at 4:29pm you would call:
    // rtc.adjust(DateTime(2017, 3, 12, 16, 29, 0));
  }
  setDualAlarms(47,0,'M');
}

void loop () {
    DateTime now = rtc.now();
    
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    
    Serial.print(" since midnight 1/1/1970 = ");
    Serial.print(now.unixtime());
    Serial.print("s = ");
    Serial.print(now.unixtime() / 86400L);
    Serial.println("d");
    
    // calculate a date which is 7 days and 30 seconds into the future
    DateTime future (now + TimeSpan(7,12,30,6));
    
    Serial.print(" now + 7d + 30s: ");
    Serial.print(future.year(), DEC);
    Serial.print('/');
    Serial.print(future.month(), DEC);
    Serial.print('/');
    Serial.print(future.day(), DEC);
    Serial.print(' ');
    Serial.print(future.hour(), DEC);
    Serial.print(':');
    Serial.print(future.minute(), DEC);
    Serial.print(':');
    Serial.print(future.second(), DEC);
    Serial.println();
  Wire.beginTransmission(0x51);
  Wire.write(0x2B);
  Wire.endTransmission();

  Wire.requestFrom(0x51, (uint8_t)1);
  Serial.print("0x2B: ");
  Serial.println(Wire.read(), BIN);
  Wire.beginTransmission(0x51);
  Wire.write(0x29);
  Wire.endTransmission();

    Wire.requestFrom(0x51, (uint8_t)1);
      Serial.print("0x29: ");
  Serial.println(Wire.read(), BIN);
  Wire.beginTransmission(0x51);
  Wire.write(0x2A);
  Wire.endTransmission();

  Wire.requestFrom(0x51, (uint8_t)1);
    Serial.print("0x2A: ");
  Serial.println(Wire.read(), BIN);
  
      Wire.beginTransmission(0x51);
  Wire.write(0x27);
  Wire.endTransmission();
  Wire.requestFrom(0x51, (uint8_t)1);
    Serial.print("0x27: ");
  Serial.println(Wire.read(), BIN);

        Wire.beginTransmission(0x51);
  Wire.write(0x10);
    Wire.endTransmission();
    Wire.requestFrom(0x51, (uint8_t)1);
    Serial.print("0x10: ");
    Serial.println(Wire.read(), BIN);
    Serial.println();
    delay(3000);
    
}

void setDualAlarms(uint8_t setMinute, uint8_t setSecond, char toggle) 
{
  DateTime now = rtc.now();  
  int resetMinute = setMinute + 5;
  if(resetMinute > 59) resetMinute = resetMinute - 60;
  switch(toggle){
    case 'S':
      rtc.clearAllFlags();
      rtc.setAlarm(2,0,resetMinute,0);
      rtc.enableAlarmINTA(PCF85263_ALARM2_MINUTES_BIT);
      rtc.setAlarm(1,0,0,setSecond);
      rtc.enableAlarmINTB(PCF85263_ALARM1_SECONDS_BIT);
      break;
    case 'M':
      rtc.clearAllFlags();
      rtc.setAlarm(2,0,resetMinute,0);
      rtc.enableAlarmINTA(PCF85263_ALARM2_MINUTES_BIT);
      rtc.setAlarm(1,0,setMinute,0);
      rtc.enableAlarmINTB(PCF85263_ALARM1_MINUTES_BIT);
  }

}

Without the EEPROM connected I get a nice incrementing DateTime.
With the EEPROM connected, The output is not correct, the time does not increment and is not correct to begin with.

Sample output:

2000/5/2 (Tuesday) 4:2:11
since midnight 1/1/1970 = 957240131s = 11079d
now + 7d + 30s: 2000/5/9 16:32:17
0x2B: 0
0x29: 1000
0x2A: 10000
0x27: 10000110
0x10: 100010

2000/5/2 (Tuesday) 4:2:14
since midnight 1/1/1970 = 957240134s = 11079d
now + 7d + 30s: 2000/5/9 16:32:20
0x2B: 0
0x29: 1000
0x2A: 10000
0x27: 10000110
0x10: 100010

2000/5/2 (Tuesday) 4:2:11
since midnight 1/1/1970 = 957240131s = 11079d
now + 7d + 30s: 2000/5/9 16:32:17
0x2B: 0
0x29: 1000
0x2A: 10000
0x27: 10000110

The other printouts are registers I was was working on for setting alarms.

GolamMostafa:
1. 0x51 is the address of RTC – it is confirmed in data sheet.

2. Look at the following, which tells a peculiar (not usually seen for other EEPROMs) way of computing the device address (not really clear to me); but, thee is no harm to make a try.

If a16 is the refelection of MS-bit (1) and A2-A1 are left open (they have internal pull down resistors) or grounded, the device address becomes (1010 001 = 0x51). It will conflict with RTC’s address.

3. Try connecting A2 and A1 at 5V. The address of the EEPROM (1010111 = 0x57) will, at least, far away from 0x51.

4. If the problem still sustains, remove the RTC, check the EEPROM alone at address 0x57. If the EEPROM does not respond at 0x57, we need to understand the meaning/significance of the a16 bit of the 7-bit address.

The following simple codes could be executed to check the presence of the EEPROM at address 0x57.

void setup()

{
  Wire.begin();
  Serial.begin(9600);

do
  {
    Wire.beginTransmission(0x57);
    busStatus = Wire.endTransmission();
  }
  while (busStatus !=0x00);
  Serial.print(“EEPROM is Found at 0x57…!”);
}

yeah I was looking at this, to be honest I couldn’t make sense of the a16 thing.

I was considering pulling up the A1 and A2 pins but I have a pcb already, so I would rather not… but I will change it if I have to.

currently your code sample only works on 0x50 and 0x51. put I’m not entirely sure which device is responding to which request …

but I will change it if I have to

You have to. You could make it slightly easier by pulling up one of the pins.

Pete

pylon:
In theory you're over spec with that value. I2C devices have an upper limit of 2mA sinking current.

This is almost never a problem.

If you have both devices connected, what's the total length of the I2C bus in your setup then? Is that a breadboard setup? Did you check the connections? Did you check that the pull-ups exist only once on the bus?

do you think the resistors could be a problem?

as regards to set up, the EEPROM is on the PCB along with the microcontroller, the RTC in on a breadboard,connected via 4 inch wires.
I used to have a different RTC on the board, a RTC_MCP79401, this worked fine.

I couldn't make sense of the a16 thing

To address 131kB you need 17 address lines. The low order 16 of those address bits are sent as data to the chip. Rather than send another byte of data to specify the one remaining, high-order bit, they use the low order bit of the I2C address as I explained in #7.

P.S. The resistors might cause a problem with the reliability of transmitting to the EEPROM or RTC, but they have nothing to do with the addressing problem which you are only going to fix by changing the I2C address of the EEPROM.

Pete

el_supremo:
You have to. You could make it slightly easier by pulling up one of the pins.

Pete

Cool, I'll give it a try now and see how it goes!

el_supremo:
To address 131kB you need 17 address lines. The low order 16 of those address bits are sent as data to the chip. Rather than send another byte of data to specify the one remaining, high-order bit, they use the low order bit of the I2C address as I explained in #7.

P.S. The resistors might cause a problem with the reliability of transmitting to the EEPROM or RTC, but they have nothing to do with the addressing problem which you are only going to fix by changing the I2C address of the EEPROM.

Pete

Cool thanks for the info I'll need to have a bit of think about this one.

so does that mean to access the entire EEPROM I have to change the address I'm using depending on the write location?

Yes. If you have an EEPROM address in a 32-bit unsigned variable, you have to look at the 0x10000 bit. If that bit is zero, then a16 in the I2C address must be zero, otherwise a16 must be one.

Here's an (untested) example function which will read one byte from a specified address anywhere in the EEPROM.

uint8_t read_one_byte(uint8_t i2c_base_address,uint32_t byte_address)
{
  // Remove the low order bit, just in case
  uint8_t base = i2c_base_address & 0xFE;

  // Change a16 to 1 if the address is in the upper half of the EEPROM
  if(byte_address & 0x10000) base++;

  // Set up the address
  Wire.beginTransmission(base);
  // hi byte of address
  Wire.write((byte_address >> 8) & 0xFF);
  // lo byte of address
  Wire.write(byte_address & 0xFF);
  Wire.endTransmission();
  
  // Now read and return the byte
  Wire.requestFrom(base, 1);
  return(Wire.read());
}

And an example call to read one byte from (the arbitrary) address 0x12345

byte_val = read_one_byte(0x56,0x12345);

Pete