Fads to Obsessions and Beyond...




Free domain for life, exceptional technical support, website transfer

ATtiny85 Port Expansion PCF8574A

Demonstrate interfacing ATtiny85/Digispark with I2C to the PCF8574A 8-bit port expansion chip.

The ATtiny range of microcontrollers are advantageous in terms of small physical size and low cost per GPIO/onboard peripheral available (particularly good for 'small' DIY projects controlling a single sensor and or output for example). However, the small physical size means very limited input/output pins (only six!).

Conveniently, there are I/O port expanders available such as the PCF8574 which can be interfaced using only 2 pins, and provide 8 GPIO's. Further, the PCF8574 can be daisy-chained so that multiple's of additional 8 GPIO's can be achieved. The interface for connecting the PCF8574 to the ATtiny85 is called I2C (I2C is an acronym for “Inter-Integrated Circuit”).

In addition to the benefit of gaining additional GPIO's by using PCF8574 port expanders is learning how to use the I2C interface. There are literally thousands of components that use the I2C interface such a real-time clocks, digital potentiometers, temperature sensors, digital compasses, memory chips etc. So once the PCF8574 interface to the ATtiny85 is successfully achieved, the same process can then be used with additional components as required.

The "TinyWireM" and "TinyWireS" are available via the Arduino IDE that provide support for I2C with the ATtiny85 (note that ATtiny85 SDA is pin P0, and CLK is pin P2). There are also libraries that utilize TinyWireM and conveniently provide functions for controlling the PCF8574 (1),(2),(3) ("bare bones" through to more "bells and whistle's").

First some background about I2C and then some specifics about the PCF8574.

Details on how to setup the Arduino IDE and environment to programme ATtiny85 microcontrollers are given in the ATtiny85 introduction.

I2C “Inter-Integrated Circuit”

I2C orginated from Philips Semiconductor (now NXP) as a way of standardising the data lines that travel between various integrated circuits, which only requires two wires (SDA – data, and SCL – clock). In terms of the hardware and communications protocol, the software library abstracts the majority of the details i.e., you don't really need to know - but if interested, the sparkfun page gives an easy introduction (4). However, some knowledge about the hardware/signal levels is necessary so the importance of having pull-up resistors on the SDA/SCL lines is appreciated.

Each I2C bus consists of two signals: SCL and SDA. SCL is the clock signal, and SDA is the data signal. The clock signal is always generated by the current bus master. The I2C bus drivers are “open drain”, meaning that they can pull the corresponding signal line low, but cannot drive it high. This eliminates possibility of data bus contention where one device is trying to drive the line high while another tries to pull it low. However, this means it is mandatory that each signal line has a pull-up resistor on it, to restore the signal to high when no device is asserting it low.

The usual value for these pull-up resistors is 4K7 (but sometimes if multiple devices on the bus 10K may be appropriate). Also, the I2C bus is meant for short distances (generally on the same PCB). The maximum length of an I2C bus is approximately 1 meter.

Devices with I2C can be connected in any physical order, there are 'address pins' on each device which can be phyiscally set to determine unique addresses.

PCF8574 Port Expander

The datasheet (see Bill of Materials Section) gives all the necessary details. Note there are alternatives to the PCF8574 (I just have a bunch of these available) such as the MCP23008 (which has 25mA sink/source per I/O pin) and 16-bit varieties (e.g. MCP23018 which gives 16 I/O lines on a single chip).

Important to realise is that the PCF8574 is a current sink device rather than current source. This means that external components need to be connected to Vcc via pull-up resistors.


The Digispark board has a surface mount ATtiny85 with headers for all pins. Additionally, the board provides a 5V regulator for external DC power input and PCB copper traces that enable insertion into standard USB connector which can then be used for DC power source. There is an onboard LED to indicate power-on and an additional LED/resistor connected to pin1 for user control.

For convenience for this simple test, the USB power supply is used.

Again for convenience for this simple test, the ATtiny85 onboard oscillator is used rather than an external crystal source.

ISP programming is used as detailed in the ATtiny85 introduction.

Connection between the ATtiny85/Digispark and USB to TTL converter cables is given in the Schematics Section (also see the Photographs Section for physical demonstation).

PCF8574 Port Expander

Pin 8 is connected to ground and pin 16 to Vcc (i.e., 5V). Pins 1 through 3 (A0, A1, A2 respectively) determine the device address. The PCF8574 has a device address of 0100 + A2,A1,A0 whereas the PCF8574A has a device address of 0111 + A2, A1, A0. Therefore, with pins 1 through 3 on the PCF8574A connected to ground, the device address will be 0111000 which is 0x38.

Pin 15 is the I2C data line, which is connected pin P0 on the ATtiny85/Digispark. Whereas, pin 14 is the I2C clock line, and is connected to pin P2 on the ATtiny85/Digispark.

As discussed in the Background Section, pull-up 4K7 resistors are mandatory on pin 14 and 15.

To provide a simple demonstration of using the port expander (to enable concentrating on any software difficulties with the ATtiny85) LED's are connected to the pins 4 through 7 of the PCF8574A (i.e., I/O pins 0 through 3). Because the PCF8574 is a current-sink device, the LEDs are connected with pull-up resistors (330ohm to limit the current to ~15mA per LED). The datasheet shows each pin has a maximum of ±20mA with the chip overall maximum of ±100mA.


Note: Image loading can be slow depending on server load.

  • ATtiny85 SchematicATtiny85 Schematic

    Silver Membership registration gives access to full resolution schematic diagrams.

    ATtiny85 Schematic

    ATtiny85 with PCF8574A Schematic

This project did not require a PCB.

The construction was done using prototyping board. See the photographs and schematic diagram sections.

Qty Schematic Part-Reference Value Notes
Resistors
2R10,R114K71/4W, 10% 
4R6 to R93301/4W, 10% 
Diodes
4D6 to D9LED 
Integrated Circuits
1U1Digispark/ATtiny85microcontroller datasheet
1U3PCF8574A8-bit port expander datasheet
Miscellaeous
3J1wiresDupont pin jumper wires
Description Downloads
Bill of Materials Text File Download

Before connecting the Digispark to the USB port, check for any shorts (direct positive voltage to ground connection) as this could damage both the Digispark board and the USB/PC.

Initial testing of a I2C interfaced device (and if the I2C interface library is working on the ATtiny85/Digispark) is to use the I2C scanner sketch (Code Snippet 1 below).

Once it is determined that I2C communication is working with the ATtiny85 and the PCF8574, the next steps are using the various libraries to set the quasi bidirectional I/O pins on the PCF8574 as both input and output. The simple example of turning LED's on/off connected to the PCF8574 pins is used to determine if correct PCF8574 pins are I/O compared to the transmitted sequences.

I2C Device Address Scanner

The I2C device (the PCF8574 in this case) needs to be connected via the SDA and SCK pins (with pullup resistors, and obviously connect Gnd and Vcc) to the ATtiny85/Digispark. The SoftSerial library is used so output can be directed via USB-TTL converter to a PC Com serial port.

Using a PCF8574A and PCF8574 pins 1 through 3 (ie A0 to A2) tied to ground produces an I2C device address of 0+0111+0+0+0 = 0x38, so the code below should report a single device found with address of 0x38 hexadecimal. Altering which of the PCF8574 pins 1 through 3 are tied to ground or Vcc will change the reported address (the datasheet gives the table showing what the resultant addresses should be).

Code Snippet 1: I2C Device Address Scanner


#include 
#include                  
#define TX_RX_PIN  3 
SoftSerial mySerial(TX_RX_PIN, TX_RX_PIN); 

int runOnce=1;

void setup() {
  mySerial.begin(115200);
  delay(3000); //give a delay so can connect the port in the receiving software
  mySerial.txMode();    //before sending a message, switch to txMode
  mySerial.println("I2C Address Scanner. Scanning ...");
  TinyWireM.begin();    // initialize I2C lib

}

void loop() {
  while (runOnce) {
    runOnce=0;
    scanI2Cdevices();
  }
}

void scanI2Cdevices(){
    byte count = 0;
    
    for (byte i = 8; i < 120; i++){
      TinyWireM.beginTransmission (i);
      if (TinyWireM.endTransmission () == 0){
        mySerial.print ("Found i2c Device Address: ");
        mySerial.print (i, DEC);
        mySerial.print (" (0x");
        mySerial.print (i, HEX);
        mySerial.println (")");
        count++;
        delay (10);  
      } 
    } 
    
    mySerial.println ("Finished.");
    mySerial.print ("Found ");
    mySerial.print (count, DEC);
    mySerial.println (" device(s).");
}
   				

"Bare Bones" PCF8574 Library

Once it is determined that I2C is working between the ATtiny85 and the PCF8574, the relevant library can be used to read and write to the PCF8574 to either set output pins and or read the state of input pins. The simplest library is by 'debsahu' on github (1). This library allows reading/writing to individual PCF8574 port pins and or reading/writing to all the PCF8574 port pins as a single byte. The following code snippet demonstrates setting/resetting PCF8574 pins as output, which control the state of attached LEDs (as per the circuit in the Schematics Section).

The Photographs Section shows the circuit on breadboard, whereas, the Videos Section shows the circuit in operation.

Code Snippet 2: PCF8574 as output controlling LED's


#include 

#define PCF8574_ADDRESS (0x38) // SDA - P0  CLK - P2

A85_PCF8574 myPCF8574;

void setup(){
  myPCF8574.begin(PCF8574_ADDRESS);
}

void loop()
{
 /*
  myPCF8574.setBit(0,1); 
  delay(500);
  myPCF8574.setBit(1,1); 
  delay(500);
  myPCF8574.setBit(2,1); 
  delay(500);
  myPCF8574.setBit(3,1); 
  delay(500);
  PCD8574A_resetAll();
  delay(500);
*/
  myPCF8574.setByte(B00000001); 
  delay(500);
  myPCF8574.setByte(B00000011);  
  delay(500);
  myPCF8574.setByte(B00000111);  
  delay(500);
  myPCF8574.setByte(B00001111);  
  delay(500);
  PCD8574A_resetAll();
  delay(500);  
}

void PCD8574A_resetAll(){
  myPCF8574.setByte(B00000000);
}    
                

"Bells and Whistle's" PCF8574 Library

The PCF8574 library by 'RobTillaart' on github (2) provides the same basic functionality as that used in code snippet 1 above, however, provides a more 'arduino' style 'syntax'. Also, this library provides a few other wrappers and routines that potentially make utilisation of the PCF8574 for controlling buttons easier (and in particular, for rotating through the PCF8574 pins, e.g. for LED displays etc).

One thing to remember if utilising this library is that it targetting Arduino boards and hence uses the Wire.h library. This needs to be changed in the library files to TinyWireM.h so that the ATtiny85 will work. The following code snippet demonstrates setting/resetting PCF8574 pins as output, which control the state of attached LEDs (as per the circuit in the Schematics Section).

Code Snippet 3: Flashing LED's connect to PCF8574


#include "PCF8574.h"

PCF8574 PCF_01(0x38); // SDA - P0 CLK - P2 


void setup()
{
  PCF_01.begin();
}

void loop() {
  PCF_01.write(0, LOW);
  delay(500);
  PCF_01.write(1, LOW);
  delay(500);
  PCF_01.write(2, LOW);
  delay(500);
  PCF_01.write(3, LOW);
  delay(500);  
  PCF_01.write(3, HIGH);
  delay(500);
  PCF_01.write(2, HIGH);
  delay(500);
  PCF_01.write(1, HIGH);
  delay(500);
  PCF_01.write(0, HIGH);
  delay(500);   
}           
               

The circuit is the basic minimum required to enable the ATtiny85 microcontroller to operate after being programmed with a suitable .hex file. So this circuit was only laid out on a breadboard.

Before connecting the Digispark to the USB port, check for any shorts (direct positive voltage to ground connection) as this could damage both the Digispark board and the USB/PC.

The circuit involving interfacing a toggle button is straight forward and should not present problems. In relation to the ATtiny85/Digispark, refer to the Schematics Section as the Digispark board has additional resistors/diodes etc on some of the ATtiny85 pins that enable onboard LEDs and the USB connectivity. These addtional passive components may interfere with a simple button connection.

Also with the Digispark board, pin 5 is generally reset and is not available as a GPIO. Although this can be changed, but then bootloader/USB programming is not enabled.

Additionally, note that the most rapid stable baud rate was used on the ATtiny85 serial communications. This is because the serial communications library uses interrupts to process the communications stream, and while the interrupt is being processed, the remainder of the code is being 'blocked'. Using the quickest stable baud rate means the maximum number of CPU cycles from the ATtiny85 will be avaiable for the loop() sketch routine.

As discussed in the Background Section, pull-up 4K7 resistors are mandatory on pin 14 and 15 of the PCF8574. This is probably the most common Gotch'ya.

Another common problem is to remember that the I2C address for the PCF8574 (0100+A2,A1,A0) is different to the PCF8574A (0111+A2,A1,A0).


Note: Video loading can be slow depending on server load.


Comments/Questions

No comments yet.

Add Comment/Question

Only Logged-In Members can add comments

"If we could sell our experiences for what they cost us, we'd all be millionaires".

Please donate any amount to help with hosting this web site.

If you subscribe (only $2/annum) you can view the site without advertisements and get emails abouts updates etc.

X

Sorry!

Only logged-in users with Silver Membership and above can download.


Silver Membership is only $2, so join now!