Wednesday, January 22, 2025

Library for PCF8574 I/O expander for AVR

The PCF8574 is an 8-bit I/O expander integrated circuit that provides an additional 8-bit I/O port that can be controlled via I2C. This allows a micro-controller to access and control more I/O pins than it would have available on its own. It can be used in projects that require lots of pins such as LED displays or button matrices. The operating voltage range is: 2.5V to 6.0V.

The advantages over a classical shift register is that it can also read the pins and with the use of the INT (interrupt) pin, the micro-controller can be notified when another device starts sending data.

Since this IC is used in the PCF8574 backpack for the alphanumeric LCD modules, I've decided to make a library for it.

Contents

 

Using the PCF8574 backpack with the alphanumeric LCD modules

PCF8574 backpack for the alphanumeric LCD modules

These modules are designed for alphanumeric LCD modules to lower the pin count that a micro-controller needs to use to control them. Normally this type of LCD requires 4 pins for data and 3 for control but since this module is based on a pin expander such as PCF8574, all those interface pins can be replaced by just to pins: SDA and SCL that are part of the I2C protocol.

The jumper  is used to disable the on-board led (not the LCD backlight led).

The potentiometer is used to adjust the LCD contrast so in case there is nothing displayed on the LCD, try turning it clockwise.

The A0, A1 and A2 pins can be used to change the device I2C address. By default they are pulled to VCC through pull-up resistors so the jumper pads can be used to pull the pins low either by using a 0 ohm resistors or using a solder bridge.

Connecting the PCF8574 adapter to an LCD

Some LCD modules have already mounted an I2C backpack module but in case you have one without you can buy the I2C module separately and connect to the LCD module yourself. The correct mounting position can be seen in this image.

Connecting the PCF8574 backpack module to an LCD module

In case your LCD has pins from a previous use, you need to remove them to be able to connect the I2C module. This can be a bit difficult if you don't have the proper tools. The cheapest and easiest way is to use a hand desoldering pump. Just be sure the soldering iron is hot enough so the solder can stay fluid for 1 or 2 seconds to be sucked by the desoldering pump.

Schematic of the PCF8574 I2C adapter for LCD modules

Schematic of the PCF8574 I2C adapter for LCD modules

The library and the PCF8574 IC

A typical implementation of the PCF8574 can be seen in the LCD adapter schematic shown earlier. There, the INT pin is not used since the LCD controller will only send data on request. Depending on the application, the INT pin can be connected to an interrupt pin on the micro-controller to trigger an interrupt whenever a device starts sending data.

PCF8574 I2C address reference

PCF8574 I2C address reference
From the PCF8574 datasheet

The I2C address of the PCF8574 can be calculated based on the A2, A1 and A0 pins, the first 4 MSB which are 0b0100, and first LCB R/W bit. Example:

Assuming the A2, A1 and A0 are tied to VCC so they are high (H) and the mode is Write (0), the 8-bit address would be:

0b01001110 = 0x4E

Changing the first bit to 1 for read mode will result with the address:

0b01001111 = 0x4F

API

Init function

void PCF_Init(PCF_t* ic, uint8_t addr)

Used to initialize the I2C library at 100kHz which is the maximum I2C frequency specified in the PCF8574 datasheet.

ic

A pointer to a structure object in case there are more than one devices.

addr

The address of the PCF8574 device shifted right by one to ignore the first read/write bit which will be set by the library. For example if the calculated address is 0x4F or 0x4E, shifting it by one 0x4F >> 1 = 0x27. Here is an useful online tool to do this: https://bitwisecmd.com.

Usage:

PCF_t pcf;
PCF_Init(&pcf, 0x27);

Set pin high or as input

void PCF_setPin(PCF_t* ic, uint8_t pin_number) // set pin high
void PCF_clearPin(PCF_t* ic, uint8_t pin_number) // set pin as input

Sets a bit to 1 or 0, using a local 8-bit variable where each bit represents a pin on the 8-bit port of the IC. The actual pin state will not change until the latchPort() is used. Useful when you need to change the state of more pins with one I2C command issued only by the latchPort() function. When the pin is set to 0 it can be used as an input pin.

The 8-bit port can also be set directly by writing to the structure member like so:

pcf.portPins = 0xF0

This sets the pins 7:4 to 1.

pin_number

Pin number from 0 to 7.

Setting the PCF8574 port

void PCF_latchPort(PCF_t* ic)

Sends an 8-bit variable set by the setPin() and clearPin() to set the state of the device port.

Set pin and latch it

void PCF_latchPinHigh(PCF_t* ic, uint8_t pin_number)
void PCF_latchPinLow(PCF_t* ic, uint8_t pin_number)

A combination of setPin/clearPin and latchPort. Used when you need to set just one pin with a single function.

Read PCF8574 port

uint8_t PCF_readPort(PCF_t* ic)

Returns an 8-bit variable representing the state of pins of the PCF8574 port.

Code example in C

#include "PCF8574.h"

int main(void){
    // PCF device 1
    PCF_t pcf;
		
    // Initialization
    PCF_Init(&pcf, PCF8574_ADDRESS);
		
    // Virtually set pin 7 high and pin 2 low (input)
    PCF_setPin(&pcf, 7);
    PCF_clearPin(&pcf, 2);
		
    // Send an I2C command to the device
    // to actually set the pins
    PCF_latchPort(&pcf);
		
    // Set pin 0 high in one go
    // Equivalent to:
    // PCF_setPin(&pcf, 0);
    // PCF_latchPort(&pcf);
    PCF_latchPinHigh(&pcf, 0);
		
    // Read pins
    uint8 pcf1_port = 0;
    pcf1_port = PCF_readPort(&pcf);
	
    while(1){
		
    }
}

v1.0
PCF8574.h
PCF8574.c
I2C (TWI) library - project page
twi.h
twi.c
External links
PCF8574 datasheet
Changelog
v1.0 (22-01-2025)
- Public release under GNU GPL v3 license.

No comments:

Post a Comment