GithubHelp home page GithubHelp logo

pcf8574's Introduction

Arduino CI Arduino-lint JSON check GitHub issues

License: MIT GitHub release PlatformIO Registry

PCF8574

Arduino library for PCF8574 - 8 channel I2C IO expander.

Description

Related to the PCF8575 16 channel IO expander library https://github.com/RobTillaart/PCF8575

This library gives easy control over the 8 pins of a PCF8574 and PCF8574A chip. These chips are identical in behaviour although there are two distinct address ranges.

type address-range notes
PCF8574 0x20 to 0x27 same range as PCF8575 !
PCF8574A 0x38 to 0x3F

So you can connect up to 16 PCF8574 on one I2C bus, giving access to 16 x 8 = 128 IO lines. To maximize IO lines combine 8 x PCF8575 + 8 x PCF8574A giving 128 + 64 = 192 IO lines. Be sure to have a well dimensioned power supply.

The library allows to read and write both single pins or 8 pins at once. Furthermore some additional functions are implemented that are playful and useful.

Interrupts intro

The PCF8574 has an interrupt output line (INT) to notify an MCU that one of the input lines has changed. This can be used to prevent active polling of the PCF8574, which can be more efficient.

From the datasheet:

An interrupt is generated by any rising or falling edge of the port inputs in the input mode. After time, (Tiv), INT is valid. Resetting and reactivating the interrupt circuit is achieved when data on the port is changed to the original setting or data is read from, or written to, the port that generated the interrupt. Resetting occurs in the read mode at the acknowledge bit after the rising edge of the SCL signal, or in the write mode at the acknowledge bit after the high-to-low transition of the SCL signal.

So there are three scenarios how the INT is reset.

  1. pins revert to original state (lesser known).
  2. read from the device (well known)
  3. write to the device (well known)

This implies that polling the PCF8574 can miss an INT in scenario 1. (see #48) In practice if you have faster polling than your signals changes this would not be a problem. E.g. tactile switches and a polling frequency > 100 Hz will work.

Interrupts library

The library cannot handle the PCF8574 interrupts as it has no code for it. The user should catch the interrupt in his own code to set a flag and can use the library to see which line has changed.

There are two examples to show how interrupts can be handled:

  • PCF8574_interrupt.ino
  • PCF8574_rotaryEncoder.ino

A more advanced interrupt handler would not set a boolean flag in the interrupt routine but increase a counter (uint8_t or larger). Then it would be possible to see that:

  1. an interrupt occurred. (counter > 0)
  2. if one or more interrupts are not handled (counter > 1)

A minimal example that shows catching missed interrupts:

  • PCF8574_interrupt_advanced.ino

0.4.0 Breaking change

Version 0.4.0 introduced a breaking change. You cannot set the pins in begin() any more. This reduces the dependency of processor dependent Wire implementations. The user has to call Wire.begin() and can optionally set the Wire pins before calling begin().

Related

16 bit port expanders

8 bit port expanders

I2C Clock

Tested on UNO with PCF8574_performance showed that the PCF8574 still works at 500 KHz and failed at 600 KHz. These values are outside the specs of the datasheet so they are not recommended. However when performance is needed you can try to overclock the chip.

clock speed Read Write Notes
100000 236 240 spec datasheet
200000 132 140
300000 104 108
400000 96 96 max advised speed
500000 92 92 not recommended
600000 crash crash

Interface

#include "PCF8574.h"

PCF8574_INITIAL_VALUE is a define 0xFF that can be set compile time or before the include of "pcf8574.h" to overrule the default value used with the begin() call.

Constructor

  • PCF8574(uint8_t deviceAddress = 0x20, TwoWire *wire = &Wire) Constructor with optional address, default 0x20, and the optional Wire interface as parameter.
  • bool begin(uint8_t value = PCF8574_INITIAL_VALUE) set the initial value (default 0xFF) for the pins and masks.
  • bool isConnected() checks if the address set in the constructor or by setAddress() is visible on the I2C bus.
  • bool setAddress(const uint8_t deviceAddress) sets the device address after construction. Can be used to switch between PCF8574 modules runtime. Note this corrupts internal buffered values, so one might need to call read8() and/or write8(). Returns true if address can be found on I2C bus.
  • uint8_t getAddress() Returns the device address.

Read and Write

  • uint8_t read8() reads all 8 pins at once. This one does the actual reading.
  • uint8_t read(uint8_t pin) reads a single pin; pin = 0..7
  • uint8_t value() returns the last read inputs again, as this information is buffered in the class this is faster than reread the pins.
  • void write8(const uint8_t value) writes all 8 pins at once. This one does the actual writing.
  • uint8_t write(const uint8_t pin, const uint8_t value) writes a single pin; pin = 0..7; value is HIGH(1) or LOW (0)
  • uint8_t valueOut() returns the last written data.

Button

The "button" functions are to be used when you mix input and output on one IC. It does not change / affect the pins used for output by masking these. Typical usage is to call setButtonMask() once in setup as pins do not (often) change during program execution.

  • void setButtonMask(const uint8_t mask) sets the (bit) mask which lines are input.
  • uint8_t getButtonMask() returns the set buttonMask.
  • uint8_t readButton8() use the mask set by setButtonMask to select specific input pins.
  • uint8_t readButton8(const uint8_t mask) use a specific mask to select specific input pins. Note this can be a subset of the pins set with setButtonMask() if one wants to process not all.
  • uint8_t readButton(const uint8_t pin) read a singe input pin.

Background - https://github.com/RobTillaart/Arduino/issues/38

Special

  • void toggle(const uint8_t pin) toggles a single pin
  • void toggleMask(const uint8_t mask = 0xFF) toggles a selection of pins, if you want to invert all pins use 0xFF (default value).
  • void shiftRight(const uint8_t n = 1) shifts output channels n pins (default 1) pins right (e.g. LEDs ). Fills the higher lines with zero's.
  • void shiftLeft(const uint8_t n = 1) shifts output channels n pins (default 1) pins left (e.g. LEDs ). Fills the lower lines with zero's.
  • void rotateRight(const uint8_t n = 1) rotates output channels to right, moving lowest line to highest line.
  • void rotateLeft(const uint8_t n = 1) rotates output channels to left, moving highest line to lowest line.
  • void reverse() reverse the "bit pattern" of the lines, swapping pin 7 with 0, 6 with 1, 5 with 2 etc.

Select

Some convenience wrappers.

  • void select(const uint8_t pin) sets a single pin to HIGH, all others are set to LOW. If pin > 7 all pins are set to LOW. Can be used to select one of n devices.
  • void selectN(const uint8_t pin) sets pins 0..pin to HIGH, all others are set to LOW. If pin > 7 all pins are set to LOW. This can typical be used to implement a VU meter.
  • void selectNone() sets all pins to LOW.
  • void selectAll() sets all pins to HIGH.

Miscellaneous

  • int lastError() returns the last error from the lib. (see .h file).

Error codes

name value description
PCF8574_OK 0x00 no error
PCF8574_PIN_ERROR 0x81 pin number out of range
PCF8574_I2C_ERROR 0x82 I2C communication error

Operation

See examples.

It is advised to use pull-up or pull-down resistors so the lines have a defined state at startup.

Future

Must

  • update documentation.
  • keep in sync with PCF8575 (as far as meaningful)

Should

Could

  • move code to .cpp

Wont

Support

If you appreciate my libraries, you can support the development and maintenance. Improve the quality of the libraries by providing issues and Pull Requests, or donate through PayPal or GitHub sponsors.

Thank you,

pcf8574's People

Contributors

ivankravets avatar jakubrybakowski avatar robtillaart avatar thijstriemstra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

pcf8574's Issues

Can't change Wire instance on ESP32

it's possible to pass a Wire instance to the constructor:

PCF8574::PCF8574(const uint8_t deviceAddress, TwoWire *wire)
{
  _address    = deviceAddress;
  _wire       = wire;
  _dataIn     = 0;
  _dataOut    = 0xFF;
  _buttonMask = 0xFF;
  _error      = PCF8574_OK;
}

but this is overwritten later on when using ESP32 or ESP8266:

#if defined (ESP8266) || defined(ESP32)
bool PCF8574::begin(int dataPin, int clockPin, uint8_t value)
{
  _wire = &Wire;
  if ((dataPin < 255) && (clockPin < 255))
  {
    _wire->begin(dataPin, clockPin);
  } else {
    _wire->begin();
  }
  if (! isConnected()) return false;
  PCF8574::write8(value);
  return true;
}
#endif


bool PCF8574::begin(uint8_t value)
{
  _wire->begin();
  if (! isConnected()) return false;
  PCF8574::write8(value);
  return true;
}

I'd like to be able to use Wire1 (which I passed into the constructor) on an ESP32.

Platformio folder structure

Hello,

I'm suddenly have a build error of this library. Platformio tries to buid test folder (at least) and does not find ArduinoUnitTests.h files.

Reading the platformio documentation, it seems to expect the header files in subfolder include and sources in src. Otherwise it will build everything, which is actually doing.

Are you aware of this ? An idea where I could be wrong ?

thanks

PinMode missing

Hi Unable to set pin mode for all the pins in PC8574 using esp8266.
Earlier pins were idetified as P0,P1..... Now it's not there.
Can you please create a sample on how to set pin mode?

Can't create multiple devices

When trying to call the constructor twice, for two PCF8574A devices with different addresses :
image
image

I get the following error from the arduino compiler :

image

I couldn't find an example with two devices, so here I am !

Edit : My bad, I found an example in test1 but it still doesnt explain the error...

ambiguous readButton8 overloads

Hello!
Arduino compiler complains that call to readButton8() is ambiguous, and I think he's right:

From PCF8574.h lines 44 and 45:
inline uint8_t readButton8() { return PCF8574::readButton8(_buttonMask); }
uint8_t readButton8(const uint8_t mask = 0xFF);

How could the compiler knows whether to call readButton8(0xFF) or readButton8(_buttonMask) then?

I would suggest removing "= 0xFF" at line 45.

Fast polling of PCF8574 does not need to hook the interrupt

Thanks for including an example of how to quickly poll the PCF8574 when reading a rotary encoder. This code is really useful if you are building a front panel display board with some rotary encoders and push buttons.

I notice in the PCF8574_rotaryEncoder.ino example that the interrupt handler is just setting a flag variable and the actual I2C polling of the PCF8574 is done from the loop() function. I considered moving the actual I2C reads into the interrupt handler but as I2C is relatively slow this did not seem like the smartest move. My application is also using interrupts to drive some stepper motor controllers and these are highly sensitive to jitter.

The other option I ended up choosing is to just dispense with the PCF8574 interrupt handler altogether and in the loop() function check the GPIO for the INT pin directly. i.e.:

if (digitalRead(2) == 0)

instead of:

if (flag)

Reading from a GPIO port is almost as fast as testing the flag variable so there is not real performance penalty for just checking the value of INT in loop(). The INT signal from the PCF8574 is just an indication that some input has changed and it will by reset to high again when we perform the I2C read from the device.

I though I would add this comment as it might be useful to others wanting to quickly poll a PCF8574 without having to resort to an interrupt handler or continuous I2C reads. I am happy to provide another example program if this is useful.

Empty constructor for setting the device address later

Hi @RobTillaart,

since I'm using your library within one of mine, I needed to be able to set the device address within my class and so not directly while calling the constructor. As I thought this could be helpful for other people as well, here you have my edited files:

  1. Adding a default address (0x20, all address pins are connected to GND)
  2. Adding an constructor with no address parameter
  3. Adding a function setDeviceAddress()

PCF8574_emptyConstructor.zip

PCF8574 Library Reverse() not found

Hello,
PCF8574 library reverse() is giving the following error in Arduino .
error: 'class PCF8574' has no member named 'reverse'
22 | pcf.reverse();

thanks
Souvik

work with tm1637

Hi
I use xreef library and can't work with tm1637 7segment module,
Is there any luck in using this lib ?

Question - constructor in setup() function?

Hi there,

I would like to use an I2C scan to find the address of my module then run the constructor. As far as I can tell this would have to be done inside the setup() function (scan then constructor) and if I do this the constructor is then not visible outside the setup() function as it would not be a global variable. Any suggestions as to how I could do this (i.e. not know the address of the module until the setup() function is run)?

Thanks,

Pete.

need a selectOne(pin) function

Need a convenience functions that set only one output HIGH at a time.
it automatically deselects a previous set one.

//  n = 0..7  other values deselect all
pcf.select(n);
or
pcf.selectOne(n);

pcf.allLOW();  //  wrapper around write8(0);
pcf.selectNone();

pcf.allHIGH(); 
pcf.selectAll();

another idea is the selectN which sets pin 0..n HIGH and n+1..7 LOW (VU meter)

pcf.selectN(n);  // n = 0..7 
or 
pcf.VU(n);  // n = 0..7  

check pcf8575 / mcp23017 / mcp23S17 ...

Need help with switch

Hi,
I have gone through the documentation avalible over internet but I'm unable to get input with PCF8574. I have tried with buttons, but that's not working even the exaple codes. The hardware I'm working with is mentioned below

Wemos D1R1 Mini(ESP8266)
Arduino IDE

Error when using the library with ESP32

Hello!
I can't get the library to work with an ESP32. I get the following error in the serial monitor:

[E][Wire.cpp:513] requestFrom(): i2cRead returned Error 263.

I'm using the example provided for ESP32.
What would be the correct initialization with ESP32?

Thanks in advance.

How to connect multiple PCF8574 devices using address from scanned procedure

Hi Mr. RobTillaart and others,
Great thanks to your valuable contribution to the creation of this library. I am a newbie and currently using ESP8266-01S (MCU) to control a range of several pcf8574 chips via I2C (using GPIO1, GPIO3 on ESP8266), Interrupt pin of pcf8574s are connected together to GPIO0 on the MCU. I have gone thought your documentation and examples but I did not see a way to connect an I2C device based on the address obtained from I2C scanners. All the known examples was to hard-code slave addressé with constructor rather than using a dynamic slave address initialization.

Could you please be so kind to guide me on this part with thanks. Please see my issue as described below:

  1. MCU (ESP8266) will scan the bus every 10-60 minutes or so to detect new plugged in Pcf8574 module;
  2. If new device is found, MCU will store the address of the new device on EEPROM or a config file using LittleFS
  3. MCU will initialize a new connection to the new pcf8574 device with the new address and conduct check on status of its pin if an interrupt signal was detected at GPIO0 (of course a read request will have to be issued to every Pcf8574 devices to find which device has a pin with status changed).
    Thank you very much for your time and attentions. Please advice me and reach me at [email protected] with thanks.
    yours very faithfully,
    Dang Dinh Ngoc

Read interrupt callback

Hi, thank you for the library, just wondering is there a read interrupt callback function that I can set a int pin to read when there's a interrupt on the PCF8574, thanks!

PCF8575 Latched Input change? If the main-program, that work with FLAG or Counter from Interrupt, is to slow

Can I use the PCF8575 to reliably record short push-button commands to GND? Even if the program calls the read16() command more slowly. I use 16 buttons and the Arduino works at limit ;-)
It is not possible with the standard interrupt with flag- or counter-setting, as the current input state is always read out later when reading with read16().
Read16() is always a snapshot when reading.

Is there no latch input function so that the cause of the interrupt can be detected?
I only know the possibility to use read16() in the interrupt routine and to store between, which is unattractive prog-style!?

I once read that the PCF8575 only resets the interrupt trigger when it is read out. But I don't know a SW-solution for this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.