In a previous post I signed off with questions about the functionality of the pfc8574. What is all this ‘quasi-bidirectional’ stuff in the data sheet about? It prevented me from implementing a getPin() method in my Android Things things-drivers library. I didn’t know what would happen when I set a pin value making it an output, then read the pin state. Would it be the value I wrote, or the pin state based on the electrical biasing of the pin.

Now that I have the pcf8574 test jig I can see what actually happens when I set pcf8574 pins high or low, and Raspberry Pi pins high or low. I can make a truth table of pin states to see if it would be OK to implement the readPin() method that’s missing from the driver.

The Hardware

The hardware for this experiment is the pcf8574 test jig setup that I used in the test jig experiment.

The PCF8574 Test Jig

The Software

In this experiment I need to write an Android Things application that sets the state of the pcf8574 and some Raspberry Pi GPIO pins. I’m going to use the test application that I wrote in the test jig experiment, extending it to help me construct a truth table of pin states. Previously I had set the Raspberry Pi GPIO pins as inputs, now I want to use some of the Raspberry Pi GPIO pins as outputs. Because I’m using the Raspberry Pi pins as outputs, I’ll need to measure the pin voltage with a multimeter to observe what physically changes.

I added a method to set a Raspberry Pi pin and pcf8574 pin in a given state.

  private void testPin(int pin, boolean gpio, boolean pcf) {
    try {
      gpioMap[pin].setValue(gpio);
      pcf8574.setPin(pin, pcf);
      Log.d(TAG, "testPin: gpio "
              + (gpio ?  " high" : "  low") + ", pcf "
              + (pcf  ?  " high" : "  low") + ", pin: "
              + (pcf8574.getPin(pin) ? " high" : " low"));
    } catch (IOException e) {
      //
    }
  }

I then used this method with all possible input combinations to create the following truth table.

pcf8574 \ Rpi GPIO LOW HIGH
LOW LOW LOW
HIGH LOW HIGH

From this table we can see that when the pcf8574 pin is low, it doesn’t matter what state the Raspberry Pi pin is, it will always read low. This is not what we want for a readPin() method. We want a readPin() method to return the state of the pin biasing, not the state we set it to. When we set the pcf8574 pin high, we read the pin state as set by the Raspberry Pi. This is what we want. This means that we can use the pcf8574 to read the state of the pins, all we need to do is set the pin state high, then the state read will be the actual pin biasing. This confirms the data sheet, which states that to use a pin as an input, first set its state to 1 (high).

Updating the driver

I’ve updated the things-drivers library to have a readPin() method, I’ve also exposed a readByte() method which reads all pins at once. Here’s the updated interface:

/**
 * Interface for an 8 bit IO port see {@link nz.geek.android.things.drivers.i2c.Pcf8574} for implementation
 */
public interface IoPort {

  /**
   * write a 'byte' to the port. This takes an {@link int} the LSB of the int is used.
   * @param mask data does not affect port state when mask bit is 1
   * @param data the data to write to the port
   * @return true when the data is written successfully
   */
  boolean writeByte(int mask, int data);

  /**
   * Read the last value written to the port.
   * @return last value written to the port (LSB is value)
   */
  int readValue();

  /**
   * set the given port pin to the given value
   * @param pin the pin to set [0:7]
   * @param state true to set it, false to clear it
   */
  void setPin(int pin, boolean state);

  /**
   * Get the state of the given port pin
   * @param pin the pin to get [0:7]
   * @return true when set (logic 1), false when clear (logic 0)
   */
  boolean getPin(int pin);

  /**
   * Read the port data, the state of all pins.
   * @return the byte read from the port (as an int LSB is port data)
   */
  int readByte();

  /**
   * Close the port. The port is invalid after being closed.
   */
  void close();
}

Before using getPin() be sure to call setPin(pin, true) to set that pin  high. This means now that readValue() may return a value that is not a representation of actual pin states, but then, the interface documentation does state that readValue() returns the last value written to the port, not the port pin states, so I guess it’s not wrong.

Conclusion

This experiment demonstrates the value of having a test jig to use during driver development. Having a way to validate your work on real hardware removes a lot of the doubt from product development. Although the primary purpose of a test jig may be to validate that hardware has been manufactured correctly, it can also be invaluable to the driver developer. Now that I have a working pcf8574 driver, validated on real hardware, I think I’ll need to use it to control some IO. Maybe drive some relays or LEDs, or perhaps something that goes ‘bing’ or a car eating robot or similar.