Connecting PCF8575 I/O pins together

I need to connect the I/O pin of one PCF8575 chip to the I/O pin of another PCF8575 chip. Grounds are connected. I can measure 5V on the one pin set as the output, but I'm not seeing the input on the second chip. Did I read that correctly in the datasheet pull-up resistors are not needed? What else could I be missing?

I don't know this chip, but looking at the data sheet, quite a lot could go wrong so that you do not get the expected results when you attempt to read a port on the second chip.

a) chapter 9.2 http://www.ti.com/lit/ds/symlink/pcf8575.pdf indicates 100k pull up resistors on input pins for an example circuit.

b) You've set the jumpers on A0,A1 and A2 such that both chips have a different I2C address and you are using the correct address for reading the port states of chip 2 ?

6v6gt:
a) chapter 9.2 http://www.ti.com/lit/ds/symlink/pcf8575.pdf indicates 100k pull up resistors on input pins for an example circuit.

Figure 21/22 seemed like a special/specific case. Based on 8.1 appears there are internal pull-ups or similar when writing 1 to the ports you intend to read.

6v6gt:
b) You've set the jumpers on A0,A1 and A2 such that both chips have a different I2C address and you are using the correct address for reading the port states of chip 2 ?

Yes. I can read each individually. I2C Scanner detects both.

OK. If you have explicitly set the pin on chip 1 as an output, then an input pin pullup resistor is not the main issue here (although it might make the start up state a bit cleaner). Anyway, for testing you could force the pin HIGH to demonstrate that the problem is with chip 2.

Then you have to say what the states of A0, A1 and A2 are on chip 2 chip and post the code that you are using to read the ports on that chip, indicating where, if relevant, that you got the library.

So you've measured the pin that's set output and high at 5v. Did you measure the input pin on the other chip and confirm that it's also at 5v? That's a good reality check to ensure that the wire is actually making contact and is connected the way you think it is.

I would first double or triple check that you have the pins you think you do connected, ie, that you're not misreading the pinout and reading the wrong pin or something. Also make sure that you don't have the chips mixed up (ie, aren't reading from the wrong chip or something).

I have measured both pins in and out. Forced the input direct. I can tell I am reading/writing the correct chips when not connected together.

Library came from Arduino manager. Here's the code. Chips are at the default addresses.

#include <Wire.h>

#define AUTO 0
#define STEP 1

int TESTER_ADDRESS = 0x20;
int DUT_ADDRESS = 0x25;
int address_found=0;
byte address_count=0;
boolean mode = AUTO;
unsigned int outputs, inputs;
byte bit_number;
byte input_or_output = 0;
boolean bank_1_result, bank_2_result;

void setup()
 {
  Wire.begin();
  
  Serial.begin(115200);
  Serial.println("PCF8575 Test Bed I2C v4");
  Display_Menu();
  
  outputs = 0x0000;
  bit_number = 0;
  bank_1_result = 0;
  bank_2_result = 0;
 }

void Display_Menu()
 {
  switch (mode)
   {
    case (AUTO) :
                Serial.println("Testing in AUTO Mode");
               break;
    case (STEP) :
                Serial.println("Testing in STEP Mode");
               break;
   }
  Serial.println("Select");
  Serial.println("'m' to switch modes");
  Serial.println("'i' for Input test");
  Serial.println("'o' for Output test");
  Serial.println("'s' for I2C_Scanner");
  if (address_count==1)
   {
    Serial.println("'u' for use found address");
   } 
 }
void Set_Board_for_Input(int addr)
 {
  Wire.beginTransmission(addr);
  Wire.write(0xFF);
  Wire.write(0xFF);
  Wire.endTransmission();
 }

void Run_Test(int input_addr, int output_addr)
 {
  byte x;
  boolean output_value;
  byte input, low, high;
  
  Serial.println();
  Serial.print("Outputs:");
  Serial.print(" ");

  for (x=0; x<=15; x++)
   {
    // write test value to screen
    if (x == bit_number)
     {
      output_value = 1;
      Serial.print (1);
     }
     else
      {
       output_value = 0; 
       Serial.print (0);
      }
    if ((x+1)%8 == 0)
     { Serial.print(" "); }

    //write test value to chip
    outputs = 0x0000;
    bitSet(outputs, bit_number);
    Wire.beginTransmission(output_addr);
    Wire.write(lowByte(outputs));
    Wire.write(highByte(outputs));
    Wire.endTransmission();
   }
  Serial.print("  ");
  
  delay(100);

  Serial.print("Inputs:");
  input = Wire.requestFrom(input_addr,2);
  if (input == 2)
   {
    low=Wire.read();
    high=Wire.read();
   }
  inputs = high << 8;
  inputs = inputs ^ low;
//  Serial.print(" ");
//  Serial.print(inputs, HEX);

  // display input readings
  Serial.print (" ");
  for (x=0; x<=15; x++)
   {
    if (bitRead(inputs, x))
     { Serial.print (1); }
     else
      { Serial.print (0); }
    
    if ((x+1)%8 == 0)
     { Serial.print(" "); }
   }

  bit_number++;
  if (bit_number > 15)
   {
    bit_number = 0;
    input_or_output = 0;
    Serial.println();
    Serial.println();
    Display_Menu();
   }
/*
   else
    {
     if (mode == AUTO)
      { Run_Test(input_addr, output_addr); }
    }
*/
 }


void I2C_Scanner()
 {
  int i;

  address_count = 0;
  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  
  Wire.begin();
  for (i = 8; i < 120; i++)
  {
    Wire.beginTransmission (i);
    if (Wire.endTransmission () == 0)
     { 
      address_found = i;  
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);
      Serial.println (")");
      address_count++;
      delay (1);  // maybe unneeded?
     } // end of good response
  } // end of for loop
  Serial.println ("Done.");
  Serial.print ("Found ");
  Serial.print (address_count, DEC);
  Serial.println (" device(s).");

  if (address_count != 1)
   { address_found = 0; }
 }

void loop()
 {
  char incoming_byte;
  
  if (Serial.available())
   {
    incoming_byte=Serial.read();
    
    if (input_or_output == 0)
     {
      switch (incoming_byte)
       {
        case ('m') :
        case ('M') :
                     if (mode == AUTO)
                      { mode = STEP; }
                      else
                       { mode = AUTO; }
                     Display_Menu();
                   break;
        case ('i') :
        case ('I') :
                     input_or_output = 1;
                     bank_1_result = 1;
                     bank_2_result = 1;
                     Serial.println("Input Test Selected");
                     Serial.println("Press any key for the next test");
                   break;
        case ('o') :
        case ('O') :
                     input_or_output = 2;
                     bank_1_result = 1;
                     bank_2_result = 1;
                     Serial.println("Output Test Selected");
                     Serial.println("Press any key for the next test");
                   break;
        case ('s') :
        case ('S') :
                     I2C_Scanner();
                     Display_Menu();
                   break;
        case ('u') :
        case ('U') :
                     if (address_count==1)
                      {
                       DUT_ADDRESS = address_found;
                       address_count = 0;
                       Serial.println("Address Set");
                      }
                     Display_Menu();
                   break;
        default :
                     Display_Menu();
                   break;
       }
      while (Serial.available())
       { incoming_byte=Serial.read(); }
     }
   }
     if (input_or_output == 1)
      {
       Set_Board_for_Input(DUT_ADDRESS);
       Run_Test(DUT_ADDRESS, TESTER_ADDRESS);
      }
      else if (input_or_output == 2)
       {
        Set_Board_for_Input(TESTER_ADDRESS);
        Run_Test(TESTER_ADDRESS, DUT_ADDRESS);
       }

 }

The code is some way from the classic “minimum complete verifiable example” (MCVE) but anyway one thing though does look odd because it is not symmetric.

Serial.print("Inputs:");
  input = Wire.requestFrom(input_addr,2);
  if (input == 2)
   {
    low=Wire.read();
    high=Wire.read();
   }
  inputs = high << 8;
  inputs = inputs ^ low;

I’d have expected to see a normal arithmetic OR and not an Exclusive OR on the last line, but maybe there is a reason why you have done this.
It would also be good to warn if you don’t get 2 bytes back from Wire.requestFrom().

Edit.
I've looked again. In this specific case, there is no difference between an OR and an XOR so that is not the problem.

I've noticed that there is an error on the TI version of the data sheet and it is much clearer on the NXP version.
Fig 7 (in Ch 7) on https://www.nxp.com/docs/en/data-sheet/PCF8575.pdf makes it clear that, although the outputs are push/pull, these can source only 100 micro amps. The equivalent TI diagram shows this as 100mA. The pins can sink 20mA so it is not a symmetric push/pull. So if you are doing your tests by driving leds, for example, to check the status of the pins, these must be connected between Vcc and the pin and not from the pin to ground.
This is similar to the PCF8574 which I have used.

Before you read from a port, you have to set the port immediately before to HIGH. It is not clear from your code that the pin which you are interested in reading has currently a value HIGH. It looks like only one pin (addressed by bit 15 in "outputs") will be HIGH when you attempt the reading. All the others will have had a LOW written to them before the read operation.

Just wondering. Why don't you use a PCF8575 library? Would make life a lot easier.

The PCF8574/8575 can not really source current. When a pin is an output set to high, it's basically pulled up through the internal pull-up resistor. As I understand there's no other facility within the chip for getting Vcc to the output pin.

wvmarle:
As I understand there's no other facility within the chip for getting Vcc to the output pin.

They call it "pseudo-bidirectional". In Arduino terminology the pins may be either INPUT_PULLUP or OUTPUT LOW. No other option. It is a bit confusing at first but quite clever IMHO. If you plan accordingly you rarely need anything else. And you need only one register to read/write, making the communication faster and easier.

This is "open collector" or "open drain" logic.

And the point is that it is completely safe to connect any two or more pins of the same or different such devices together to produce a "wired-AND" function - unlike Arduino pins. Not entirely safe to connect one directly to an Arduino pin however.

Note also the default (start-up) state of the chip pins is HIGH so that none are pulled down. And when written HIGH, it does source a little more - nominal 1 mA - current for the duration of the command itself, to speed up transitions.

6v6gt:
Edit.
I've looked again. In this specific case, there is no difference between an OR and an XOR so that is not the problem.

Oops. Wrong language. I'll change that.

6v6gt:
I've noticed that there is an error on the TI version of the data sheet and it is much clearer on the NXP version.
Fig 7 (in Ch 7) on https://www.nxp.com/docs/en/data-sheet/PCF8575.pdf makes it clear that, although the outputs are push/pull, these can source only 100 micro amps. The equivalent TI diagram shows this as 100mA. The pins can sink 20mA so it is not a symmetric push/pull. So if you are doing your tests by driving leds, for example, to check the status of the pins, these must be connected between Vcc and the pin and not from the pin to ground.
This is similar to the PCF8574 which I have used.

Before you read from a port, you have to set the port immediately before to HIGH. It is not clear from your code that the pin which you are interested in reading has currently a value HIGH. It looks like only one pin (addressed by bit 15 in "outputs") will be HIGH when you attempt the reading. All the others will have had a LOW written to them before the read operation.

void Set_Board_for_Input(int addr)
 {
  Wire.beginTransmission(addr);
  Wire.write(0xFF);
  Wire.write(0xFF);
  Wire.endTransmission();
 }
....
if (input_or_output == 1)
      {
       Set_Board_for_Input(DUT_ADDRESS);
       Run_Test(DUT_ADDRESS, TESTER_ADDRESS);
      }
      else if (input_or_output == 2)
       {
        Set_Board_for_Input(TESTER_ADDRESS);
        Run_Test(TESTER_ADDRESS, DUT_ADDRESS);
       }

Should be clear the entire chip is set for input with outputs set high. I'm not driving any LEDs. I/O pin connected to O/I pin by about 6" of wire currently (will be longer when not on the bench). That's all, nothing fancy here. Yes only one pin on the output chip is high, all others are low. The chip with the one input high is the output chip, the chip with all inputs high is the input chip.

OK. then it looks like your code then meets all the requirements and should work.

The only other things I can suggest to try are:

  1. stronger I2C line pull up resistors say 2.2K
  2. If you are using an AVR (Uno Etc.) look at Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino to see how to change the I2C clock speed and do some experiments.
  3. repeat the suggestion made by wvmarle to attempt to use an existing library. Say pcf8574_arduino_library/PCF8575 at master · skywodd/pcf8574_arduino_library · GitHub and, if it works, reverse engineer it to see why yours failed.

I think I'm missing something. How does changing anything related to the I2C side of the chips effect the ability of the chip's I/O pin seeing another same chips I/O pin?

Maybe you do some mistake reading/writing the chips. You should try to make a minimal (non)working example: try to write some data to a chip and check with a DMM if they are output correctly. Add some pull up/downs to the chip and check if you can read it correctly. If so communication is likely OK. Add functions and on every step check if it does what you want. This way you will likely find the mistake yourself. If not you can preset two nearly identical sketches: one working and one not working. It will be much easier to find the bug.

Post #5.

adwsystems:
I have measured both pins in and out. Forced the input direct. I can tell I am reading/writing the correct chips when not connected together.

Put another way. If I disconnect the chips and drive the inputs from their out power/ground, I can read the correct input values. If I use a voltmeter (not an LED) I can see the correct output (one output) being set high.

Now I make one change and connect the two chips (input-to-output) and then am not able to see anything. How does this indicate an issue with the I2C or the program? I see an issue between the chips, not enough current? But that doesn't sound right as the output sink current is much more than the output source current and the input is already pulled high. Therefore I should be able to pull down the input and read 0 and the pull up combined with the output source current should read as 1.

adwsystems:
I think I'm missing something. How does changing anything related to the I2C side of the chips effect the ability of the chip's I/O pin seeing another same chips I/O pin?

If I have understood your problem correctly, you are attempting to read a pin from what you have referred to in your OP as your "second chip". You have measured the input voltage on the pin and it is 5 Volts (HIGH). However, your attempts to read the value of that input pin do not give the expected results.
If that is all correct, then it is perfectly reasonable to assume that there is an error in attempting to read the value of that pin ( through I2C ) with the sketch you have posted. Hence the suggestions for changing the behaviour of I2C. Or how were you expecting to read the value of the input pin ?

This has just crossed with your last post which I now have to read . . .

When they are connected together what the DMM says? Are logic levels as intended? What does the reading device reports instead?

adwsystems:
Post #5.
Put another way. If I disconnect the chips and drive the inputs from their out power/ground, I can read the correct input values. If I use a voltmeter (not an LED) I can see the correct output (one output) being set high.

Now I make one change and connect the two chips (input-to-output) and then am not able to see anything. How does this indicate an issue with the I2C or the program? I see an issue between the chips, not enough current? But that doesn't sound right as the output sink current is much more than the output source current and the input is already pulled high. Therefore I should be able to pull down the input and read 0 and the pull up combined with the output source current should read as 1.

If you believe that your can successfully handle the 2 chips separately (that is setting the values on output pins and reading the values on input pins through your sketch and verifying the results with a physical measurement) but have problems only when combining the two chips using an output pin to drive an input pin, then I agree it is not an I2C issue. Then I guess that it can only be pin pull up resistor issue and, if you haven't already done so, you should try with a 100k resistor as in the example application circuits in the data sheet.

6v6gt:
If you believe that your can successfully handle the 2 chips separately (that is setting the values on output pins and reading the values on input pins through your sketch and verifying the results with a physical measurement) but have problems only when combining the two chips using an output pin to drive an input pin, then I agree it is not an I2C issue. Then I guess that it can only be pin pull up resistor issue and, if you haven't already done so, you should try with a 100k resistor as in the example application circuits in the data sheet.

I was trying to avoid this as it won't be very pretty (PCF8575 is surface mounted and no good place to connect the external pull-up). I will give it a shot.

I'm curious to know what practical application this has. Since the MPU must set an output pin, what is going to read the input pin which is driven by that output pin ? The MPU again or something else ?