Site icon Renzo Mischianti

PCF8575: un expander i2c I/O digitale a 16 bit

Spread the love

pcf8575 expander digitale I/O I2C a 16 bit

Il PCF8575 è un expander in I/O digitale a 16 bit per il bus bidirezionale a due linee (I2C) è progettato per il funzionamento da 2,5 a 5,5 V.

Il dispositivo è dotato di porte di ingresso/uscita (I/O) quasi bidirezionali a 16 bit (P07-P00, P17-P10), incluse uscite con capacità di azionamento ad alta tensione per il pilotaggio diretto dei LED. Ogni I/O può essere utilizzato come ingresso o uscita senza l’uso di un segnale di controllo della direzione dei dati. All’accensione, gli I/O sono in pull-up.

Connessioni del modulo

La connessione al modulo è diretta.

  1. Va fornita alimentazione da 2,5 fino a 5 V e terra.
  2. Collegare le linee I2C SCL e SDA dall’MCU.
  3. Se utilizzato, collegare la linea INT a un ingresso di interrupt sull’MCU e utilizzare un resistore di pull-up.

Ho scritto una libreria per usare questo IC/modulo sia con Arduino che esp8266 o esp32 all’interno dell’Arduino IDE.

pcf8575 test lettura scrittura led bottone

Così si può leggere e scrivere i valori digitali con solo 2 fili (perfetto per ESP-01).

Breadbord PCF8575

Ho provato a semplificare l’uso di questo IC, riducendo al minimo le 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.

Libreria

Puoi trovare la mia libreria qui.

Download

Fare clic sul pulsante DOWNLOADS nell’angolo in alto a destra, rinominare la cartella non compressa PCF8575.

Verificare che la cartella PCF8575 contenga PCF8575.cpp e PCF8575.h.

Collocare la cartella della libreria PCF8575 come / librerie / cartella.

Potrebbe essere necessario creare la sottocartella librerie se è la tua prima libreria.

Riavvia l’IDE.

IC o modulo

Puoi usare un normale IC ma solo in formato SMD o un modulo.

pcf8575 IC

Puoi trovare il componente SMD su Aliexpress

pcf8575 module

Puoi trovare il module su Aliexpress - Aliexpress

Uso

Come diveco proma ho provato a semplificare l’uso di questo integrato con un set minimo di operazioni.

PCF8575 address map 0x20-0x27

Sul costruttore devi passare l’indirizzo i2c che hai configurato tramite i pin A0, A1, A2 puoi verificare la configurazione qui (per verificare l’indirizzo ti consiglio di usare questa guida I2cScanner)

PCF8575(uint8_t address);

per l’esp8266 se vuoi specificare i pins SDA e SCL usa questo costruttore:

PCF8575(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:

// Instantiate Wire for generic use at 400kHz
TwoWire I2Cone = TwoWire(0);
// Instantiate Wire for generic use at 100kHz
TwoWire I2Ctwo = TwoWire(1);

// Set pcf8575 i2c comunication with second Wire using 21 22 as SDA SCL
PCF8575 pcf8575(&I2Ctwo);
//PCF8575 pcf8575(&I2Ctwo, 21,22);
//PCF8575 pcf8575(&I2Ctwo, 0x5C);
//PCF8575 pcf8575(&I2Ctwo, 21,22,0x5C);

Per ogni pin puoi configurare input o output mode:

	pcf8575.pinMode(P0, OUTPUT);
	pcf8575.pinMode(P1, INPUT);
	pcf8575.pinMode(P2, INPUT);

come puoi vedere dall’immagine l’IC ha 16 I/O digitali:

pcf8575 pinouts

So to read all analog input in one trasmission you can do (even if I use a 10millis debounce time to prevent too much read from i2c):

	PCF8575::DigitalInput di = PCF8575.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);

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 PCF8575_LOW_MEMORY

Abilitando il low memory guadagni circa 14byte di memoria, ed anche l’output del metodo che fa la lettura complessiva sarà modificato in questa maniera:

   byte di = pcf8575.digitalReadAll();
   Serial.print("READ VALUE FROM PCF: ");
   Serial.println(di, BIN);

in di ci sono 2 byte 1110001 1110001 in un uint16_u, e potrai estrarre le stesse informazioni facendo delle operazioni 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;
   p8 = ((di & bit(8)>0)?HIGH:LOW;
   p9 = ((di & bit(9)>0)?HIGH:LOW;
   p10 = ((di & bit(10)>0)?HIGH:LOW;
   p11 = ((di & bit(11)>0)?HIGH:LOW;
   p12 = ((di & bit(12)>0)?HIGH:LOW;
   p13 = ((di & bit(13)>0)?HIGH:LOW;
   p14 = ((di & bit(14)>0)?HIGH:LOW;
   p15 = ((di & bit(15)>0)?HIGH:LOW;

se tu vuoi leggere un input singolo:

int p1Digital = PCF8575.digitalRead(P1); // read P1

se vuoi scrivere un valore digitale:

PCF8575.digitalWrite(P1, HIGH);

o:

PCF8575.digitalWrite(P1, LOW);

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 PCF8575

// Function interrupt
void keyPressedOnPCF8575();

// Set i2c address
PCF8575 pcf8575(0x39, ARDUINO_UNO_INTERRUPT_PIN, keyPressedOnPCF8575);

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 keyPressedOnPCF8575(){
	// Interrupt called (No Serial no read no wire in this function, and DEBUG disabled on PCF library)
	 keyPressed = true;
}

Impostare i nomi dei pin come datasheet invece che sequenziale (Update 27/06/2021)

Come puoi vedere nel pinout dell’IC, P0 è P00, P1 è P01 .. P7 è P07 e quindi non ci sono P8 e P9, invece l’8° pin è P10, il 9° pin è P11, ecc… ho inserito perciò la possibilità di modificare la gestione in questo modo anziché sequenziale, basta decommentare la define qui sotto.

// Define to manage original pinout of pcf8575
// like datasheet but not sequential
// #define NOT_SEQUENTIAL_PINOUT

Qui l’etichette dei pins.

#ifdef NOT_SEQUENTIAL_PINOUT
	#define P00  	0
	#define P01  	1
	#define P02  	2
	#define P03  	3
	#define P04  	4
	#define P05  	5
	#define P06  	6
	#define P07  	7
	#define P10  	8
	#define P11  	9
	#define P12  	10
	#define P13  	11
	#define P14  	12
	#define P15  	13
	#define P16  	14
	#define P17  	15
#else
	#define P0  	0
	#define P1  	1
	#define P2  	2
	#define P3  	3
	#define P4  	4
	#define P5  	5
	#define P6  	6
	#define P7  	7
	#define P8  	8
	#define P9  	9
	#define P10  	10
	#define P11  	11
	#define P12  	12
	#define P13  	13
	#define P14  	14
	#define P15  	15
#endif

Schemi di connessione

Per gli esempi che trovate nella libreria ho usato questo schema di collegamento su una breadboard: 

pcf8575 test lettura scrittura led bottone

Esempi aggiuntivi dal pcf8574 riusabili sul pcf8575

Nel tempo vari contributori mi hanno aiutato a creare nuove features ed esempi. Vado a mostrarveli qui:

Wemos lampeggiamento dei LED

Dal Giappone nopnop ha create un esempio per il Wemos che permette il blink sequenziale di 8 LED.

/*
 * PCF8575 GPIO Port Expand
 * http://nopnop2002.webcrow.jp/WeMos/WeMos-25.html
 *
 * PCF8575    ----- 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 "PCF8575.h"  // https://github.com/xreef/PCF8575_library

// Set i2c address
PCF8575 pcf8575(0x20);

void setup()
{
  Serial.begin(9600);

  // Set pinMode to OUTPUT
  for(int i=0;i<8;i++) {
    pcf8575.pinMode(i, OUTPUT);
  }
  pcf8575.begin();
}

void loop()
{
  static int pin = 0;
  pcf8575.digitalWrite(pin, HIGH);
  delay(1000);
  pcf8575.digitalWrite(pin, LOW);
  delay(1000);
  pin++;
  if (pin > 7) pin = 0;
}
WeMos D1 esp8266 pcf8574 IC schema di connessione 8 leds
Wemos lampeggiamento dei LED invertito

Qui un nuovo esempio che semplicemente posiziona il polo positivo del led sul VCC mentre quello negativo sul pcf8575, così l’alimentazine dei led non è fornita dal pcf ma dall’alimentatore.

/*
 * PCF8575 GPIO Port Expand
 * Inverted led test: all led is connected with anodo to the IC
 *
 * PCF8575    ----- 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
 * P8     ----------------- LED8
 * P9     ----------------- LED9
 * P10     ----------------- LED10
 * P11     ----------------- LED11
 * P12     ----------------- LED12
 * P13     ----------------- LED13
 * P14     ----------------- LED14
 * P15     ----------------- LED15
 *
 */

#include "Arduino.h"
#include "PCF8575.h"  // https://github.com/xreef/PCF8575_library

// Set i2c address
PCF8575 pcf8575(0x20);

void setup()
{
  Serial.begin(9600);

  // Set pinMode to OUTPUT
  for(int i=0;i<16;i++) {
    pcf8575.pinMode(i, OUTPUT);
  }
  for(int i=0;i<16;i++) {
	  pcf8575.digitalWrite(i, HIGH);
  }

  pcf8575.begin();
}

void loop()
{
  static int pin = 0;
  pcf8575.digitalWrite(pin, LOW);
  delay(1000);
  pcf8575.digitalWrite(pin, HIGH);
  delay(1000);
  pin++;
  if (pin > 15) pin = 0;
}

Video dimostrativo
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"
/*
 * 	PCF8575 GPIO Port Expand
 *  Blink all led
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/
 *
 *
 * PCF8575    ----- 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 "PCF8575.h"  // https://github.com/xreef/PCF8575_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
PCF8575 pcf8575(&I2Ctwo, 0x20);
// PCF8575 pcf8575(&I2Ctwo, 0x20, 21, 22);
// PCF8575(TwoWire *pWire, uint8_t address, uint8_t interruptPin,  void (*interruptFunction)() );
// PCF8575(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++) {
    pcf8575.pinMode(i, OUTPUT);
  }
  pcf8575.begin();
}

void loop()
{
  static int pin = 0;
  pcf8575.digitalWrite(pin, HIGH);
  delay(400);
  pcf8575.digitalWrite(pin, LOW);
  delay(400);
  pin++;
  if (pin > 7) pin = 0;
}
esp32 pcf8574 IC schema di connessione 8 leds

Links utili


Spread the love
Exit mobile version