Libreria per l’utilizzo dell’integrato pcf8574 che sfrutta il protocollo i2c usabile sia con Arduino che gli esp8266.
Questo IC puo’ controllare (fino a 8) dispositivi digitali come bottono o led con solamente 2 pins.
Puo’ leggere e scrivere valori digitali con solamente 2 pins perfetto per un esp01 ad esempio.
Ho provato a semplificare l’uso di questo integrato con un set minimo di operazioni.
Come funziona il protocollo I2c
Il protocollo I2C funziona con due fili, l’SDA (dati) e SCL (clock).
Questi pin sono open-drain, e sono in pull-up con delle resistenze.
Di solito c’è un master e uno o più slave sulla linea, anche se possono esserci più master, ma ne parleremo in altra occasione.
Entrambi i master e gli slave possono trasmettere o ricevere dati, pertanto un dispositivo può trovarsi in uno di questi quattro stati: trasmissione master, ricezione master, trasmissione slave, ricezione slave.
IC o modulo
Puoi usare il normale integrato o i vari moduli a disposizione.
Qui trovi l'IC AliExpress
Libreria
Puoi trovare la mia libreria qui .
Download
Fare clic sul pulsante DOWNLOADS nell’angolo in alto a destra, rinominare la cartella non compressa PCF8574.
Verificare che la cartella PCF8574 contenga PCF8574.cpp e PCF8574.h.
Collocare la cartella della libreria PCF8574 come / librerie / cartella.
Potrebbe essere necessario creare la sottocartella librerie se è la tua prima libreria.
Riavvia l’IDE.
Uso
Ho provato a semplificare l’uso di questo integrato con un set minimo di operazioni.
Per il PCF8574P la mappa indirizzi va da 0x20-0x27
per il PCF8574AP la mappa indirizzi va da 0x38-0x3f
Costrutture
Sul costruttora devi passare l’indirizzo i2c che hai configurato tramite i pin A0, A1, A2 (per verificare l’indirizzo ti consiglio di usare questa guida I2cScanner)
PCF8574(uint8_t address);
per l’esp8266 se vuoi specificare i pins SDA e SCL usa questo costruttore:
PCF8574(uint8_t address, uint8_t sda, uint8_t scl);
Per gli esp32 puoi passare direttamente il TwoWire così da poter selezionare il secondo canale i2c: [updated 29/04/2019]
// Instantiate Wire for generic use at 400kHz
TwoWire I2Cone = TwoWire(0);
// Instantiate Wire for generic use at 100kHz
TwoWire I2Ctwo = TwoWire(1);
// Set dht12 i2c comunication with second Wire using 21 22 as SDA SCL
DHT12 dht12(&I2Ctwo);
//DHT12 dht12(&I2Ctwo, 21,22);
//DHT12 dht12(&I2Ctwo, 0x5C);
//DHT12 dht12(&I2Ctwo, 21,22,0x5C);
Impostare se il pin è di input o di output ed il valore iniziale
Per ogni pin puoi configurare input o output mode:
pcf8574.pinMode(P0, OUTPUT);
pcf8574.pinMode(P1, INPUT);
pcf8574.pinMode(P2, INPUT);
Puoi gestire il valore iniziale dei vari pins: [updated 06/03/2020]
Puoi definire e gestire se un input pin e INPUT o INPUT_PULLUP
pcf8574.pinMode(P0, INPUT);
pcf8574.pinMode(P1, INPUT_PULLUP);
Per gli OUTPUT puoi specificare il valore iniziale che viene impostato immediatamente al begin dell’IC: [updated 06/03/2020]
pcf8574.pinMode(P6, OUTPUT, LOW);
pcf8574.pinMode(P6, OUTPUT, HIGH);
per retrocompatibilità il valore dell’OUTPUT di default è HIGH.
come puoi vedere dall’immagine l’IC ha 8 I/O digitali:
Per leggere tutti i pin digitali in un’unica trasmissione puoi usare questo codice (questo non serve a prevenire troppe letture consecutive all’IC, internamente uso 10millis di tempo di debounce per prevenire un sovraccarico di letture):
PCF8574::DigitalInput di = PCF8574.digitalReadAll();
Serial.print("READ VALUE FROM PCF P1: ");
Serial.print(di.p0);
Serial.print(" - ");
Serial.print(di.p1);
Serial.print(" - ");
Serial.print(di.p2);
Serial.print(" - ");
Serial.println(di.p3);
Riduzione uso di memoria
Per assecondare una richiesta specifica di un utente (issue #5) ho creato una define per ridurre l’uso di memoria eliminando la struttura complessa di ritorno alla lettura complessiva, per attivarla devi decommentare la linea nel file .h della libreria:
// #define PCF8574_LOW_MEMORY
Abilitando il low memory guadagni circa 7byte di memoria, ed anche l’output del metodo che fa la lettura complessiva sarà modificato in questa maniera:
byte di = pcf8574.digitalReadAll();
Serial.print("READ VALUE FROM PCF: ");
Serial.println(di, BIN);
dove c’è un byte 1110001, e potrai estrarre le stesse informazioni facendo delle operaizoni binarie sul byte stesso, qui un esempio:
p0 = ((di & bit(0)>0)?HIGH:LOW;
p1 = ((di & bit(1)>0)?HIGH:LOW;
p2 = ((di & bit(2)>0)?HIGH:LOW;
p3 = ((di & bit(3)>0)?HIGH:LOW;
p4 = ((di & bit(4)>0)?HIGH:LOW;
p5 = ((di & bit(5)>0)?HIGH:LOW;
p6 = ((di & bit(6)>0)?HIGH:LOW;
p7 = ((di & bit(7)>0)?HIGH:LOW;
se tu vuoi leggere un input singolo:
int p1Digital = PCF8574.digitalRead(P1); // read P1
[aggiornato 13/03/2020] puoi anche forzare la rilettura dall’integrato bypassando la funzione interna di debounce (rimuovendo la latenza)
int p1Digital = PCF8574.digitalRead(P1, true); // read P1 without debounce
se vuoi scrivere un valore digitale:
PCF8574.digitalWrite(P1, HIGH);
o:
PCF8574.digitalWrite(P1, LOW);
Interrupt
Puoi inoltre usare un interrupt pin: dovrai inizializzare il pin e passare una funzione di callback che sarà chiamata quando l’interrupt sarà attivato dal PCF8574
// Function interrupt
void keyPressedOnPCF8574();
// Set i2c address
PCF8574 pcf8574(0x39, ARDUINO_UNO_INTERRUPT_PIN, keyPressedOnPCF8574);
Ricorda che non puoi usare comandi come Serial o Wire all’interno della funzione di interrupt.
Il sistema migliore sarà settare solamente una variabile da leggere all’interno del loop():
void keyPressedOnPCF8574(){
// Interrupt called (No Serial no read no wire in this function, and DEBUG disabled on PCF library)
keyPressed = true;
}
Bassa latenza
Per le applicationi che richiedono una latenza inferiore ai 10 millisecondi ora ho impostato una define che si può decommentare ed abilitare la latenza ad 1 millisecondo.
Schemi di connessione
Per gli esempi che trovate nella libreria ho usato questo schema di collegamento su una breadboard:
Esempi aggiuntivi
Nel tempo vari contributori mi hanno aiutato a creare nuove features ed esempi. Vado a mostrarveli qui:
Wemos LEDs blink
Dal Giappone nopnop ha create un esempio per il Wemos che permette il blink sequenziale di 8 LED.
/*
* PCF8574 GPIO Port Expand
* http://nopnop2002.webcrow.jp/WeMos/WeMos-25.html
*
* PCF8574 ----- WeMos
* A0 ----- GRD
* A1 ----- GRD
* A2 ----- GRD
* VSS ----- GRD
* VDD ----- 5V/3.3V
* SDA ----- GPIO_4(PullUp)
* SCL ----- GPIO_5(PullUp)
*
* P0 ----------------- LED0
* P1 ----------------- LED1
* P2 ----------------- LED2
* P3 ----------------- LED3
* P4 ----------------- LED4
* P5 ----------------- LED5
* P6 ----------------- LED6
* P7 ----------------- LED7
*
*/
#include "Arduino.h"
#include "PCF8574.h" // https://github.com/xreef/PCF8574_library
// Set i2c address
PCF8574 pcf8574(0x20);
void setup()
{
Serial.begin(9600);
// Set pinMode to OUTPUT
for(int i=0;i<8;i++) {
pcf8574.pinMode(i, OUTPUT);
}
pcf8574.begin();
}
void loop()
{
static int pin = 0;
pcf8574.digitalWrite(pin, HIGH);
delay(1000);
pcf8574.digitalWrite(pin, LOW);
delay(1000);
pin++;
if (pin > 7) pin = 0;
}
Esp32 leds blink usando il canale i2c secondario.
Qui ho creato una variante dell’esempio sopra per mostrare l’utilizzo di un canale secondario i2c presente sugli esp32.
#include "Arduino.h"
/*
* PCF8574 GPIO Port Expand
* Blink all led
* by Mischianti Renzo <https://mischianti.org>
*
* https://mischianti.org/pcf8574-i2c-digital-i-o-expander-fast-easy-usage/
*
*
* PCF8574 ----- Esp32
* A0 ----- GRD
* A1 ----- GRD
* A2 ----- GRD
* VSS ----- GRD
* VDD ----- 5V/3.3V
* SDA ----- 21
* SCL ----- 22
*
* P0 ----------------- LED0
* P1 ----------------- LED1
* P2 ----------------- LED2
* P3 ----------------- LED3
* P4 ----------------- LED4
* P5 ----------------- LED5
* P6 ----------------- LED6
* P7 ----------------- LED7
*
*/
#include "Arduino.h"
#include "PCF8574.h" // https://github.com/xreef/PCF8574_library
// Instantiate Wire for generic use at 400kHz
TwoWire I2Cone = TwoWire(0);
// Instantiate Wire for generic use at 100kHz
TwoWire I2Ctwo = TwoWire(1);
// Set i2c address
PCF8574 pcf8574(&I2Ctwo, 0x20);
// PCF8574 pcf8574(&I2Ctwo, 0x20, 21, 22);
// PCF8574(TwoWire *pWire, uint8_t address, uint8_t interruptPin, void (*interruptFunction)() );
// PCF8574(TwoWire *pWire, uint8_t address, uint8_t sda, uint8_t scl, uint8_t interruptPin, void (*interruptFunction)());
void setup()
{
Serial.begin(112560);
I2Cone.begin(16,17,400000); // SDA pin 16, SCL pin 17, 400kHz frequency
// Set pinMode to OUTPUT
for(int i=0;i<8;i++) {
pcf8574.pinMode(i, OUTPUT);
}
pcf8574.begin();
}
void loop()
{
static int pin = 0;
pcf8574.digitalWrite(pin, HIGH);
delay(400);
pcf8574.digitalWrite(pin, LOW);
delay(400);
pin++;
if (pin > 7) pin = 0;
}
STM32 gestione in contemporanea di 4 leds e 4 bottoni
Usa PA1 come pin di interruzione e segui le istruzioni del prossimo esempio.
Arduino gestione in contemporanea di 4 leds e 4 bottoni
Ecco un esempio di input ed output contemporaneo, se si ha intenzione di gestire la pressione contemporanea dei bottoni va ridotta la latenza fino a 0 o impostata la define specifica. Altro modo è usare il parametro aggiontivo a true sul digitalRead
.
/*
KeyPressed and leds with interrupt
by Mischianti Renzo <https://mischianti.org>
https://www.mischianti.org/pcf8574-i2c-digital-i-o-expander-fast-easy-usage/
*/
#include "Arduino.h"
#include "PCF8574.h"
// For arduino uno only pin 1 and 2 are interrupted
#define ARDUINO_UNO_INTERRUPTED_PIN 2
// Function interrupt
void keyPressedOnPCF8574();
// Set i2c address
PCF8574 pcf8574(0x38, ARDUINO_UNO_INTERRUPTED_PIN, keyPressedOnPCF8574);
unsigned long timeElapsed;
void setup()
{
Serial.begin(115200);
pcf8574.pinMode(P0, INPUT_PULLUP);
pcf8574.pinMode(P1, INPUT_PULLUP);
pcf8574.pinMode(P2, INPUT);
pcf8574.pinMode(P3, INPUT);
pcf8574.pinMode(P7, OUTPUT);
pcf8574.pinMode(P6, OUTPUT, HIGH);
pcf8574.pinMode(P5, OUTPUT);
pcf8574.pinMode(P4, OUTPUT, LOW);
pcf8574.begin();
timeElapsed = millis();
}
unsigned long lastSendTime = 0; // last send time
unsigned long interval = 4000; // interval between sends
bool startVal = HIGH;
bool keyPressed = false;
void loop()
{
if (keyPressed){
uint8_t val0 = pcf8574.digitalRead(P0);
uint8_t val1 = pcf8574.digitalRead(P1);
uint8_t val2 = pcf8574.digitalRead(P2);
uint8_t val3 = pcf8574.digitalRead(P3);
Serial.print("P0 ");
Serial.print(val0);
Serial.print(" P1 ");
Serial.print(val1);
Serial.print(" P2 ");
Serial.print(val2);
Serial.print(" P3 ");
Serial.println(val3);
keyPressed= false;
}
if (millis() - lastSendTime > interval) {
pcf8574.digitalWrite(P7, startVal);
if (startVal==HIGH) {
startVal = LOW;
}else{
startVal = HIGH;
}
lastSendTime = millis();
Serial.print("P7 ");
Serial.println(startVal);
}
}
void keyPressedOnPCF8574(){
// Interrupt called (No Serial no read no wire in this function, and DEBUG disabled on PCF library)
keyPressed = true;
}
Links utili
- PCF8574 un expander i2c I/O digitale: Arduino, esp8266 e esp32, I/O ed interrupt
- PCF8574 un expander i2c I/O digitale: Arduino, esp8266 e esp32, encoder rotativo