14 Jun 2014

IO Expander PCF8574 with Raspberry Pi

With the Raspberry Pi, you can easily run out of IO pins. A good way to solve that problem is to use an IO expander to give your Pi a couple more IO. On Tayda Electronics website, you have two different chips, the 8-bits PCF8574 and the 16-bits MCP23017 from Microchip. These two devices can communicate with the PI using the I2C protocol, it only uses two pins : SDA (data) and SCL (clock). The MCP23017 is a bit more complicated than the other one because you have access to a lot of registers on the device. The PCF8574 does not use register address : you simply read or write from it.

First, you will need to activate the I2C port on your Raspberry Pi. There are several tutorials showing how to do this and I covered a couple links in my article about Adafruit's I2C LED matrix. You also have to find the address of your device. If it's a PCF8574A the address will be different.


The three least significant bits of the 7-bits address are A2, A1, and A0. These three bits are set on the pins of the chip that have the same name. That means that you could have a maximum of 8 PCF8574 connected to your Pi without having address conflict. This means you can have up to 64 more IO. If you use 8 PCF8574 and 8 PCF8574A you can double this number :)

On my breadboard, I wired 8 orange leds to the PCF8574 using 220 Ohms resistors. I plugged to chip on the 5V line to have a little bit more current on the LEDs than on the 3.3V line. A2, A1, and A0 are shorted to ground. The address of the device is then 0x38 (0111000). At first I did not understand it was this address because of the picture above, where we see a 0 next to A0. But A0 is really the least significant bit. An I2C address is always 7-bit.

On the rev2 Raspberry Pi, the I2C port is #1 and on rev1 it is #0. This little detail is important to know when we initialize the I2C device. Once everything is wired up and I2C is enabled, we can issue a detect command :


In the command i2cdetect -y 1 we specify that we are using port #1 for the rev2 Pi. There is also a really useful command we can use :
i2cset -y 1 0xADDRESS 0xDATA

For example, if I want to turn on all the LEDs directly on the command line, I can issue this command :
i2cset -y 1 0x38 0xFF

It's easy as that!!!

Python example

This demo would not be complete without some LED chasing and a demo Python program. In the sample below, LED1 through LED8 are the bit position of each LED in hexadecimal. I used the cycle function from the itertools library to create an infinite array that cycle from LED1 to LED8, and back to LED1, etc... Without pausing between each iteration, the Raspberry Pi would do that sequence really fast, so I'm adding a 100ms delay so we can see what is happening.

from smbus import SMBus
from itertools import cycle
from time import sleep

LED1 = 0x01
LED2 = 0x02
LED3 = 0x04
LED4 = 0x08
LED5 = 0x10
LED6 = 0x20
LED7 = 0x40
LED8 = 0x80

PATTERN = (LED1, LED2, LED3, LED4,
           LED5, LED6, LED7, LED8,
           LED7, LED6, LED5, LED4,
           LED3, LED2)

bus = SMBus(1) # Port 1 used on REV2 

for LED in cycle(PATTERN):
    bus.write_byte(0x38, LED)
    sleep(0.1)

Demo video

7 comments:

  1. Interesting post. You might want to connect your LEDs between the PCF8574A's output ports and Vdd. Sourcing current is internally limited to 100uA while sinking current in the port allows up to 25mA.

    ReplyDelete
    Replies
    1. Thanks for bringing this to my attention, that could explain some poor performance I got with the IC :P

      Delete
  2. I just reproduced this project. Very instructive.

    I wired up the led's as Bruno suggested. This inverts things, so to get the same result the values of LED1 through LED8 must be redefined:
    LED1 = 0xFF - 0x01
    LED2 = 0xFF - 0x02
    LED3 = 0xFF - 0x04
    etc.

    The led's can even be wired to 5V pin which can supply much more current than the 3.3V pin which is limited to 50mA. The PCF8574 must be supplied with 3.3V in both cases.

    You did your project with a PCF8574A, which shows up at address 0x38 with A0, A1, and A2 to ground. I did mine with a PCF8574, which wired the same, shows up at 0x20.

    ReplyDelete
  3. how to use these expander pins as input??

    ReplyDelete
  4. How can I get only one led turn on and leave rest at there current state?
    I really need this for my project. I will share my project soon on your forum.

    ReplyDelete
    Replies
    1. To turn on only the LED you want you need to use bit manipulation
      for example to turn on only LED1, LED2 and LED4 : "LED = LED1 | LED2 | LED4"
      This is done with the OR operator "|"
      Now let's say you want to toggle off only LED2 : "LED = LED ^ LED2"
      Toggling is done with the XOR operator "^"

      I suggest that you read a bit more on what is called bit masks and bit manipulation using binary operators

      Delete