dimanche 3 février 2019

Les extensions de GPIOs de l'ARDUINO




Les extensions de GPIOs de l'ARDUINO


Nous allons explorer dans cet article les différentes possibilité d'étendre le nombre d'entrées / sorties digitales d'une carte  ARDUINO.
Des schémas et sketches de base sont proposés.

1. Les GPIOs de l'ADUINO

Comme vous le savez probablement, le nombre d'entrées et de sorties digitales d'une carte ARDUINO ou de l'ESP8266 est limité.

ARDUINO UNO, NANO, MINI, etc. :
  • 14 GPIOs
  • 6 pins analogiques utilisables en pins digitales
Deux de ces GIOs sont réservées à l'émission / réception sur la ligne série.
Cela nous laisse donc 18 GPIOs utilisables, ce qui est parfois insuffisant.

ARDUINO MEGA, DUE :
  • 54 GPIOs
  • 16 pins analogiques utilisables en pins digitales
Ici également deux de ces GIOs sont réservées à l'émission / réception sur la ligne série.
ESP-01 :
  • 2 GPIOs
ESP-12E :
  • 8 GPIOs
Lorsque l'on désire travailler avec un format de carte réduit, comme une NANO par exemple, changer pour une MEGA est fortement pénalisant en terme d'encombrement.
Comme nous allons le voir il existe des solutions bon marché.

2. La démarche

Nous allons examiner d'abord les caractéristiques d'un certain nombre de circuits.

Ensuite pour certains de ces circuits que j'ai en ma possession, je les ai expérimentés.
Je les ai raccordés à un ARDUINO NANO et des LEDs et écrit les sketches correspondants à l'aide de librairies. Les schémas, sketches et la liste des librairies sont fournis.
Ce travail peut servir de base de départ pour démarrer un projet.

Ensuite j'ai mesuré les temps d'écriture. Ceux-ci sont mesurés pour l'écriture d'un mot (8 bits ou 16 bits) sur les sorties.
Les composants I2C sont mesurés à 100KHz et 400KHz.

Enfin, deux petits tableaux récapitulent ensuite les principales caractéristiques et les performances.

3. Les décodeurs

Un décodeur permet de décoder un certain nombre de lignes binaires ou BCD.
  • 3 entrées / 8 sorties
  • 4 entrées / 16 sorties
Une seule sortie peut être activée à la fois. Une ou plusieurs broches "enable" permettent d'activer ou non la sortie.

3.1. Le 74LS138 et le 74LS238

Le 74LS138 et le 74LS238 permettent à partir de 3 entrées binaires d'obtenir 8 sorties.

3.2. Le74HC154

Le 74HC154 permet à partir de 4 entrées binaires d'obtenir 16 sorties.

4. Les registres à décalage

Un registre est un convertisseur série / parallèle, il offre un certain nombre de sorties mais est dépourvu d'entrées.

4.1. Le 74HC595

Le 74HC595 est un registre à décalage. Il peut être utilisé comme expander de GPIOs. Il a les caractéristiques suivantes :
  • 8 sorties
  • courant maximal par sortie 35mA
  • courant maximal pour l'ensemble des sorties 70mA
Quelques lien utiles :
https://learn.adafruit.com/adafruit-arduino-lesson-4-eight-leds/the-74hc595-shift-register
https://eskimon.fr/tuto-arduino-901-ajouter-des-sorties-num%C3%A9riques-%C3%A0-larduino-le-74hc595

Il est possible de chaîner plusieurs 74HC595 pour obtenir un registre à décalage 16bits, 24bits, 32bits ou plus.
Voir plus bas :  7. Les schémas et sketches

Certains afficheurs du commerce en sont équipés :
Le 74HC595 existe en boîtier DIP.
Il est facile de trouver des modules "breakboard" équipés de ce circuit :

4.2. Le 74LS673

Le 74LS673 est aussi un registre à décalage pouvant être utilisé comme expander de GPIOs. Il a les caractéristiques suivantes :
  • 16 sorties
  • courant maximal par sortie 8mA
Malheureusement son prix élevé - une quarantaine d'euros - est dissuasif. Il est préférable de s'orienter vers un expander lorsque l'on désire disposer de 16 bits, ou de chaîner deux 74HC595.

5. Les expanders

Un expander permet de disposer de vraies entrées / sorties. Il offre souvent des fonctionnalités annexes :
  • broche d'interruption
  • résistances PULL-UP configurables ou non
Commençons par les plus connus.

5.1. Le MCP23008 et MCP23S08

Le MCP23008 est un expander de GPIOs sur bus I2C. Le MCP23S08 est un expander de GPIOs sur bus SPI. Ils ont les caractéristiques suivantes :
  • 8 entrées/sorties
  • résistances PULL-UP 100kΩ configurables
  • courant maximal par sortie 25mA
  • courant maximal pour l'ensemble des sorties 125mA
  • MCP23008 : 3 broches de sélection d'adresse
  • MCP23S08 : 2 broches de sélection d'adresse
  • 1 sortie d'interruption
Le MCP23008 et le MCP23S08 existent en boîtier DIP.
On ne trouve pas de modules "breakboard" équipés de ces circuits.

5.2. Le MCP23017 et MCP23S17

Le MCP23017 est un expander de GPIOs sur bus I2C. Le MCP23S17 est un expander de GPIOs sur bus SPI. Ils ont les caractéristiques suivantes :
  • 16 entrées/sorties 
  • résistances PULL-UP 100kΩ configurables
  • courant maximal par sortie 25mA
  • courant maximal pour l'ensemble des sorties 125mA
  • 3 broches de sélection d'adresse
  • 2 sorties d'interruption
Le MCP23017 et le MCP23S17 existent en boîtier DIP.
Il est facile de trouver des modules "breakboard" équipés du MCP23017 :

5.3. Le PCA9537

Le PCA9537 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 4 entrées/sorties 
  • courant maximal par sortie 50mA
  • courant maximal pour l'ensemble des sorties 85mA
  • 1 adresseI2C fixe
  • 1 sortie d'interruption
  • boîtier TSSOP10
On ne trouve pas de "breakboard" équipés de ce circuit.

5.4. Le PCA9555

Le PCA9555 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 16 entrées/sorties 
  • résistances PULL-UP 100kΩ non configurables
  • courant maximal par sortie 8mA (état bas)
  • courant maximal pour l'ensemble des sorties 200mA (état bas)
  • 3 broches de sélection d'adresse
  • 1 sortie d'interruption
On trouve peu de "breakboard" équipés de ce circuit :

5.5. Le PCA9655

Le PCA9655 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 16 entrées/sorties 
  • résistances PULL-UP 100kΩ non configurables
  • courant maximal par sortie 25mA (état bas)
  • courant maximal pour l'ensemble des sorties 400mA (état bas)
  • 3 broches de sélection d'adresse
  • 1 sortie d'interruption
On ne trouve pas de modules "breakboard" équipés de ce circuit.

5.6. Le PCF8574

Le PCF8574 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 8 entrées/sorties en collecteur ouvert
  • courant maximal par sortie 50mA (état bas), 100µA (état haut)
  • courant maximal pour l'ensemble des sorties 100mA
  • 3 broches de sélection d'adresse
  • 1 sortie d'interruption
La majeure partie de convertisseurs I2C pour afficheurs LCD 2x16 ou 4x20 utilisent le PCF8574 :

Attention, le PCF8574 est quasi-bidirectionnel. Il ne possède pas de registre de direction comme un MCP23008. De plus, il est incapable de fournir un courant important à l'état haut.

Il est facile de trouver des modules "breakboard" équipés de ce circuit :

5.7. Le PCF8575

Le PCF8575 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 16 entrées/sorties en collecteur ouvert
  • courant maximal par sortie 50mA (état bas), 100µA (état haut)
  • courant maximal pour l'ensemble des sorties 100mA
  • 3 broches de sélection d'adresse
  • 1 sortie d'interruption
Attention, comme le PCF8574, le PCF8575 est quasi-bidirectionnel. Il ne possède pas de registre de direction comme un MCP23017. De plus, il est incapable de fournir un courant important à l'état haut. 

Il est facile de trouver des modules "breakboard" équipés de ce circuit :

5.8. Le SX1509

Le SX1509 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 16 entrées/sorties en push-pull ou collecteur ouvert
  • résistances PULL-UP PULL-DOWN 100kΩ configurables
  • PWM, BLINK, FADE IN/OUT
  • clavier matriciel
  • courant maximal par sortie 15mA (état bas), 8mA (état haut)
  • 2 broches de sélection d'adresse
  • 1 sortie d'interruption
  • Attention : tension d'alimentation 3.3V
Ce circuit, avec ses possibilités avancées (clavier matriciel, PWM, clignotement, FADE IN/OUT) programmables, permet de décharger le processeur de certaines tâches.

La fréquence PWM est ajustable (voir la datasheet) :

ClkX = fOSC/(2^(RegMisc[6:4]-1))

J'ai fait un essai en réel avec la lib SparkFun :

 io.clock(INTERNAL_CLOCK_2MHZ, 1, OUTPUT, 1);    // F = 8.33KHz
 io.clock(INTERNAL_CLOCK_2MHZ, 7, OUTPUT, 1);    // F = 113Hz


Un lien utile :https://learn.sparkfun.com/tutorials/sx1509-io-expander-breakout-hookup-guide/all

Il est facile de trouver des modules "breakboard" équipés de ce circuit :
Le modèle SparkFun possède deux rangées de pastilles que l'on peut raccorder au 3.3V ou GND par soudure.

5.9. Le MCP23009 et MCP23S09

Le MCP23009 est un expander de GPIOs sur bus I2C. Le MCP23S09 est un expander de GPIOs sur bus SPI. Il a les caractéristiques suivantes :
  • 8 entrées/sorties  en collecteur ouvert
  • résistances PULL-UP 100kΩ configurables
  • courant maximal par sortie 25mA (état bas)
  • courant maximal pour l'ensemble des sorties 125mA (état bas)
  • 1 broche de sélection d'adresse
  • 2 sorties d'interruption
Le MCP23009 et le MCP23S09 existent en boîtier DIP.
On ne trouve pas de modules "breakboard" équipés de ce circuit.

5.10. Le MCP23018 et MCP23S18

Le MCP23018 est un expander de GPIOs sur bus I2C. Le MCP23S18 est un expander de GPIOs sur bus SPI. Il a les caractéristiques suivantes :
  • 16 entrées/sorties  en collecteur ouvert
  • résistances PULL-UP 100kΩ configurables
  • courant maximal par sortie 25mA (état bas)
  • courant maximal pour l'ensemble des sorties 400mA (état bas)
  • 1 broche de sélection d'adresse
  • 2 sorties d'interruption
Le MCP23018 et le MCP23S18 existent en boîtier DIP.
On ne trouve pas de modules "breakboard" équipés de ce circuit.

5.11. Le MAX7301

Le MAX7301 est un expander de GPIOs sur bus 4 fils. Il a les caractéristiques suivantes :
  • 20 (SSOP28) ou 28 (SSOP36) entrées/sorties
  • résistances PULL-UP configurables
  • courant maximal par sortie 10mA (état bas), 4.5mA (état haut)
  • courant maximal pour l'ensemble des sorties 600mA (état bas)
On ne trouve pas de modules "breakboard" équipés de ce circuit.

5.12. Le MAX7311

Le MAX7311 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 16 entrées/sorties
  • résistances PULL-UP 100kΩ configurables
  • courant maximal par sortie 17mA (état bas), 29mA (état haut)
  • courant maximal pour l'ensemble des sorties 250mA
  • 3 broches de sélection d'adresse
  • 1 sortie d'interruption
On ne trouve pas de modules "breakboard" équipés de ce circuit.

5.13. Le MAX7318

Le MAX7318 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 16 entrées/sorties
  • résistances PULL-UP 100kΩ configurables
  • courant maximal par sortie 17mA (état bas), 29mA (état haut)
  • courant maximal pour l'ensemble des sorties 250mA
  • 3 broches de sélection d'adresse
  • 1 sortie d'interruption
On ne trouve pas de modules "breakboard" équipés de ce circuit.

5.14. Le TCA9534

Le TCA9534 est un expander de GPIOs sur bus I2C. Il a les caractéristiques suivantes :
  • 8 entrées/sorties
  • résistances PULL-UP 100kΩ configurables
  • courant maximal par sortie 50mA
  • courant maximal pour l'ensemble des sorties 160mA
  • 3 broches de sélection d'adresse
  • 1 sortie d'interruption
On ne trouve pas de modules "breakboard" équipés de ce circuit.

5.15. Le PCA9685

Le PCA9685 est un driver de LEDs I2C PWM 16 canaux, mais on peut également s'en servir comme d'un expander (en sortie uniquement) :

Il dispose de fonctions avancées :
  • PWM 12 bits : fréquence variable de 24Hz à 1526Hz
  • luminosité des LED programmable
  • allumage / extinction des LED avec un temps programmable
  • etc.

6. Les multiplexeurs I2C

Nous allons terminer par une dernière catégorie de circuits permettant de multiplexer un bus I2C afin d'obtenir plusieurs bus I2C à partir d'un seul.

Ces composants peuvent permettre de résoudre des conflits d'adresse I2C, lorsque l'on utilise plusieurs composants I2C identiques n'ayant pas de broche de sélection d'adresse par exemple.

6.1. Le PCA9540

Le PCA9540 permet de multiplexer 2 bus I2C. Il a les caractéristiques suivantes :
  • 2 bus esclaves
  • alimentation 2.3V - 3.6V
  • pas de broche de sélection d'adresse
On ne trouve pas de modules "breakboard" équipés de ce circuit.

6.2. Le PCA9541

Le PCA9541 permet de multiplexer 2 bus I2C. Il a les caractéristiques suivantes :
  • 2 bus esclaves
  • alimentation 2.3V - 5.5V
  • 4 broches de sélection d'adresse
On ne trouve pas de modules "breakboard" équipés de ce circuit.

6.3. Le PCA9543

Le PCA9543 permet de multiplexer 2 bus I2C. Il a les caractéristiques suivantes :
  • 2 bus esclaves
  • alimentation 2.3V - 3.6V
  • 2 broches de sélection d'adresse
On ne trouve pas de modules "breakboard" équipés de ce circuit.

6.4. Le PCA9545

Le PCA9545 permet de multiplexer 4 bus I2C. Il a les caractéristiques suivantes :
  • 4 bus esclaves
  • alimentation 2.3V - 5.5V
  • 2 broches de sélection d'adresse
On ne trouve pas de modules "breakboard" équipés de ce circuit.

6.4. Le PCA9546

Le PCA9546 permet de multiplexer 4 bus I2C. Il a les caractéristiques suivantes :
  • 4 bus esclaves
  • alimentation 2.3V - 5.5V
  • 3 broches de sélection d'adresse
On ne trouve pas de modules "breakboard" équipés de ce circuit.

6.5. Le PCA9548

Le PCA9548 permet de multiplexer 8 bus I2C. Il a les caractéristiques suivantes :
  • 8 bus esclaves
  • alimentation 2.3V - 5.5V
  • 3 broches de sélection d'adresse
On ne trouve pas de modules "breakboard" équipés de ce circuit.

6.6. Le TCA9543


Le TCA9543 permet de multiplexer 2 bus I2C. Il a les caractéristiques suivantes :
  • 2 bus esclaves
  • alimentation 1.65V - 5.5V
  • 2 broches de sélection d'adresse
On ne trouve pas de modules "breakboard" équipés de ce circuit.

6.7. Le TCA9548

Le TCA9548 permet de multiplexer 8 bus I2C. Il a les caractéristiques suivantes :
  • 8 bus esclaves
  • alimentation 1.65V - 5.5V
  • 3 broches de sélection d'adresse
Il est facile de trouver des modules "breakboard" équipés de ce circuit :

7. Les librairies

Remarque : la librairie PCF8574 de Rob Tillaart est préférable. De plus elle est compatible ESP8266 et ESP32.

8. Les performances

Le tableau suivant regroupe les caractéristiques des différents circuits.
 
Module BUS GPIOS INT Tension Conso
au repos
Boîtier
74HC595 Série 8 0 2.0V - 6.0V 80µA DIP16 SO16
SSOP16
TSSOP16
2 x 74HC595 chaînés Série 16 0 2.0V - 6.0V 160µA
MCP23008 I2C 8 1 1.8V - 5.5V 1µA DIP18 SO18S
SOP20
MCP23S08 SPI 8 1 1.8V - 5.5V 1µA DIP18 SO18
SSOP20
MCP23009 I2C 8 1 1.8V - 5.5V 1µA DIP18 SO18
SSOP20
MCP23S09 SPI 8 1 1.8V - 5.5V 1µA DIP18 SO18
SSOP20
MCP23017 I2C 16 2 1.8V - 5.5V 1µA DIP28 SO28
SSOP28
MCP23S17 SPI 16 2 1.8V - 5.5V 1µA DIP28 SO28
SSOP28
MCP23018 I2C 16 2 1.8V - 5.5V 1µA DIP28 SO28
SSOP28
MCP23S18 SPI 16 2 1.8V - 5.5V 1µA DIP28 SO28
SSOP28
PCA9537 I2C 4 1 2.3V - 5.5V 1µA TSSOP10
TSSOP24
PCA9555 I2C 16 1 2.3V - 5.5V 1µA SO24 SSOP24
TSSOP24
PCA9655 I2C 16 1 1.65V - 5.5V 1µA SO24 TSSOP24
PCF8574 I2C 8 1 2.5V - 5.5V 10µA DIP16 SO16
PCF8575 I2C 16 1 2.5V - 5.5V 10µA SO24 SSOP24
TSSOP24
SX1509 I2C 16 1 1.425V - 3.6V 1µA QFN28
MAX7301 4W 20 ou 28 0 2.5V - 5.5V 11µA SSOP28
SSOP36
MAX7311 I2C 16 1 2.0V - 5.5V 15µA SO24 SSOP24
TSSOP24
MAX7318 I2C 16 1 2.0V - 5.5V 19µA SO24 SSOP24
TSSOP24
TCA9534 I2C 8 1 1.65V - 5.5V 22µA TSSOP16


Le tableau suivant donne leurs performances en écriture.

L'augmentation de la vitesse du bus 12C de l'ARDUINO à 400KHZ permet d'obtenir une plus grande rapidité, grâce à cette simple modification :

void setup() {
  Serial.begin(115200);
  Serial.println("MCP23008");
  mcp.begin();
  Wire.setClock(400000); 

  for (int i = 0 ; i < 8 ; i++) {
    mcp.pinMode(i, OUTPUT);
  }
}


Module Temps
d'écriture
Temps
d'écriture I2C à 100KHz
Temps
d'écriture I2C à 400KHz
74HC595 121µs

2 x 74HC595 chaînés 236µs

MCP23008
325µs 118µs
MCP23S08 20µs

MCP23009 (*)
325µs 118µs
MCP23S09 (*) 20µs

MCP23017
425µs 153µs
MCP23S17 24µs

MCP23018 (*)
425µs 153µs
MCP23S18 (*) 24µs

PCA9537


PCA9555


PCA9655


PCF8574
225µs 79µs
PCF8575


SX1509


MAX7301


MAX7311


MAX7318


TCA9534



(*) : je n'ai pas ces composants sous la main pour les tester mais ils ont des caractéristiques probablement très proches de leurs homologues push-pull en matière de vitesse :
  • MCP23009 : MCP23008
  • MCP23S09 : MCP23S08
  • MCP23018 : MCP23017
  • MCP23S18 : MCP23S17

9. Les schémas et sketches

Les performances des différents circuits sont testées à l'aide des schémas et sketches suivants.

9.1. Le 74HC595 

Le schéma :
Le schéma est réalisé avec KICAD. Les broches d'alimentation du 74HC595  ne sont pas représentées : VCC = 16, GND = 8.

Le sketch :

#define LATCH_PIN 5
#define CLOCK_PIN 6
#define DATA_PIN  4

void setup() {
  Serial.begin(115200);
  Serial.println("74HC595");
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);
}

void loop() {
  unsigned long start = micros();
  for (unsigned cnt = 0; cnt < 256; cnt++) {
    digitalWrite(LATCH_PIN, LOW);
    shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, cnt);
    digitalWrite(LATCH_PIN, HIGH);
  }
  unsigned long stop = micros();
  Serial.print("Elapsed : ");
  Serial.print((stop-start) / 256);
  Serial.println(" µs");
}



Chaîner deux 74HC595
Le schéma :
Le schéma est réalisé avec KICAD. Les broches d'alimentation des 74HC595  ne sont pas représentées : VCC = 16, GND = 8.

Le sketch :

#define LATCH_PIN 5
#define CLOCK_PIN 6
#define DATA_PIN  4

void setup() {
  Serial.begin(115200);
  Serial.println("2x74HC595");
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);
}

void loop() {
  unsigned long start = micros();
  for (unsigned long cnt = 0; cnt < 65536 ; cnt++) {
    digitalWrite(LATCH_PIN, LOW);
    shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, cnt & 0xff);

    shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, cnt >> 8);
    digitalWrite(LATCH_PIN, HIGH);
  }
  unsigned long stop = micros();
  Serial.print("Elapsed : ");
  Serial.print((stop-start) / 65536);
  Serial.println(" µs");
}


9.2. Le MCP23008

Le schéma :
Le schéma est réalisé avec KICAD. Les broches d'alimentation du MCP23008 ne sont pas représentées : VCC = 18, GND = 9.

Les deux résistances de pullup sur les lignes SDA et SCL ne sont pas représentées non plus, mais elles sont nécessaires.

L'adresse I2C est 0x20.

Le sketch :

#include <SPI.h>
#include <Adafruit_MCP23008.h>

Adafruit_MCP23008 mcp;

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

  Serial.println("MCP23008");
  mcp.begin(); 
  Wire.setClock(400000); 
  for (int i = 0 ; i < 8 ; i++) {
     mcp.pinMode(i, OUTPUT);
  }
}

void loop() {
  unsigned long start = micros();
  for (int cnt = 0 ; cnt < 256 ; cnt++) {
    mcp.writeGPIO(cnt);
  }
  unsigned long stop = micros();
  Serial.print("Elapsed : ");
  Serial.print((stop-start) / 256);
  Serial.println(" µs");
}


9.3. Le MCP23S08

Le schéma :
Le schéma est réalisé avec KICAD. Les broches d'alimentation du MCP23S08 ne sont pas représentées : VCC = 18, GND = 9.

Les deux résistances de pullup sur les lignes SDA et SCL ne sont pas représentées non plus, mais elles sont nécessaires.

Le sketch :

#include <SPI.h>
#include <gpio_MCP23SXX.h>

gpio_MCP23SXX mcp(MCP23S08, 10, 0x20);

void setup() {
  Serial.begin(115200);
  Serial.println("MCP23S08");
  mcp.begin();
  for (int i = 0 ; i < 8 ; i++) {
    mcp.gpioPinMode(i, OUTPUT);
  }
}

void loop() {
  unsigned long start = micros();
  for (int cnt = 0; cnt < 256; cnt++) {
    mcp.gpioPort(cnt);
  }
  unsigned long stop = micros();
  Serial.print("Elapsed : ");
  Serial.print((stop-start) / 256);
  Serial.println(" µs");
}


9.4. Le MCP23017

Le schéma :

Le schéma est réalisé avec KICAD. Les broches d'alimentation du MCP23017 ne sont pas représentées : VCC = 9, GND = 10.

Les deux résistances de pullup sur les lignes SDA et SCL ne sont pas représentées non plus, mais elles sont nécessaires.

L'adresse I2C est 0x20.

Le sketch :

#include <SPI.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp;

void setup() {
  Serial.begin(115200);
  Serial.println("MCP23017");
  mcp.begin();

  Wire.setClock(400000); 
  for (int i = 0 ; i < 8 ; i++) {
    mcp.pinMode(i, OUTPUT);
  }
}

void loop() {
  unsigned long start = micros();
  for (unsigned cnt = 0; cnt < 256; cnt++) {
    mcp.writeGPIOAB(cnt);
  }
  unsigned long stop = micros();
  Serial.print("Elapsed : ");
  Serial.print((stop-start) / 256);
  Serial.println(" µs");
}


9.5. Le MCP23S17

Le schéma :

Le schéma est réalisé avec KICAD. Les broches d'alimentation du MCP23S17 ne sont pas représentées : VCC = 9, GND = 10.

Le sketch :

#include <SPI.h>
#include <MCP23S17.h>

MCP mcp(0, 10);

void setup() {
  Serial.begin(115200);
  mcp.begin();
  mcp.pinMode(0x0000);
}

void loop() {
  unsigned long start = micros();
  for (unsigned cnt = 0; cnt < 256; cnt++) {
    mcp.digitalWrite(cnt);
  }
  unsigned long stop = micros();
  Serial.print("Elapsed : ");
  Serial.print((stop-start) / 256);
  Serial.println(" µs");
 
}


9.6. Le PCF8574

Le schéma :

Les deux résistances de pullup sur les lignes SDA et SCL ne sont pas représentées, mais elles sont nécessaires.

Je me suis aperçu récemment que le PCF8574 ne fournit que 100µA à l'état haut. Il serait préférable de connecter les couples résistance+LED entre le +5V et les sorties si l'on veut bénéficier d'un courant supérieur.

Le sketch :

#include <Wire.h>    // Required for I2C communication
#include "PCF8574.h" // Required for PCF8574
 

PCF8574 pcf;

void setup() {
  Serial.begin(115200);
  Serial.println("PCF8574");
  pcf.begin(0x27);
  Wire.setClock(400000);
  for (int i = 0 ; i < 8 ; i++) {
    pcf.pinMode(i, OUTPUT);
  }
}

void loop() {
  unsigned long start = micros();
  for (unsigned cnt = 0; cnt < 256; cnt++) {
    pcf.write(cnt);
  }
  unsigned long stop = micros();
  Serial.print("Elapsed : ");
  Serial.print((stop-start) / 256);
  Serial.println(" µs");
}


10. Gestion d'entrées sous interruption

MCP23017
Ci-dessous un schéma avec un MCP23017 gérant un clavier à 16 touches :

Les deux résistances de pullup sur les lignes SDA et SCL ne sont pas représentées, mais elles sont nécessaires.

La broche d'interruption A  du MCP23017 est reliée à la broche D2 de l'ARDUINO. Ceci va permettre d'endormir celui-ci et de le réveiller uniquement si nécessaire. Un montage de ce genre avec un ARDUINO PRO MINI modifié devrait pouvoir offrir une consommation extrêmement faible, 5 à 6 µA.

Le sketch :

#include <LowPower.h>
#include <SPI.h>
#include <Adafruit_MCP23017.h>

#define INTERRUPT_A_PIN   2

Adafruit_MCP23017 mcp;

volatile boolean awakenByInterrupt = false;

void setup() {
  Serial.begin(115200);
  Serial.println("MCP23017");
  pinMode(INTERRUPT_A_PIN, INPUT);
  mcp.begin();
  mcp.setupInterrupts(true, false, LOW);
  for (int i = 0 ; i < 16 ; i++) {
    mcp.pinMode(i, INPUT);
    mcp.pullUp(i, HIGH);
    mcp.setupInterruptPin(i, FALLING);
  }
}

void intCallBack() {
  awakenByInterrupt = true;
}

void loop() {
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_A_PIN), intCallBack, FALLING);
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
  detachInterrupt(digitalPinToInterrupt(INTERRUPT_A_PIN));
  if (awakenByInterrupt) {
    uint8_t pin = mcp.getLastInterruptPin();
    uint8_t val = mcp.getLastInterruptPinValue();
    Serial.print("Interrupt: pin"); Serial.print(pin); Serial.print(": "); Serial.println(val);
    while (!mcp.digitalRead(pin));
    EIFR=0x01;
    awakenByInterrupt = false;
  }
}


MCP23008
Il est possible de faire exactement la même chose avec 8 touches et un MCP23008. Par contre la gestion des interruptions n'est pas prévue dans la librairie.
Plutôt que de la modifier bêtement, il vaut mieux en hériter dans notre code :

#include <Wire.h>
#include "Adafruit_MCP23008.h"

class MyMCP23008 : public Adafruit_MCP23008
{
  protected:
    void write8(uint8_t addr, uint8_t data);
    uint8_t i2caddr;

  public: 
    void enableInterrupts(uint8_t data);
};

void MyMCP23008::write8(uint8_t addr, uint8_t data)

{
  Wire.beginTransmission(MCP23008_ADDRESS | i2caddr);
#if ARDUINO >= 100
  Wire.write((byte)addr);
  Wire.write((byte)data);
#else
  Wire.send(addr); 
  Wire.send(data);
#endif
  Wire.endTransmission();
}

void MyMCP23008::enableInterrupts(uint8_t data)
{
  this->write8(MCP23008_GPINTEN, data);
}


MyMCP23008 mcp;


void setup()
{
  Serial.begin(115200);
  Serial.println("MCP23008");
  mcp.begin();
  for (int button = 0 ; button < 8 ; button++) {
    mcp.pinMode(button, INPUT);
    mcp.pullUp(button, HIGH);

    mcp.enableInterrupts(0xff);  }
}


La méthode write8() est celle de la librairie AdaFruit. Pourquoi la dupliquer ? parce que dans cette classe elle est "private", donc inutilisable dans les méthodes des classes dérivées, ce qui n'est pas très malin.

11. Les circuits spécialisés

Certains circuits peuvent être utilisés en lieu et place d'expanders ou de registres à décalage pour des tâches spécialisées, en particulier le pilotage d'afficheurs ou de claviers matriciels.
Ces circuits se chargent à la place du processeur du multiplexage de l'afficheur.

Voir cet article :
https://riton-duino.blogspot.com/2018/09/lcd-tft-et-arduino.html

12. Conclusion

On remarque facilement la nette supériorité en vitesse des composants SPI.

Malgré cela les composants I2C n'en restent pas moins intéressants quand la vitesse n'est pas un critère primordial :
  • pilotage de LEDs
  • pilotage d'écrans y compris LCD 7 segments
  • pilotage de relais
  • pilotage de claviers matriciels
  • etc.
Avant de faire un choix l'essentiel est de recenser les besoins. Quand une ou plusieurs entrées sont nécessaires, les expanders sont indispensables. Les registres à décalage en sont dépourvus.

Cordialement
Henri

13. Mises à jour

04/02/2019 : 5.9. Le MCP23009 et MCP23S09.
                     ajout de mesures de vitesse I2C à 400KHz
05/02/2019 : 5.7. Le PCF8574
                     ajout de mesures de vitesse PCF8574
05/02/2019 : 11. Les circuits spécialisés
09/02/2019 : 3. les decodeurs
10/02/2019 : 10. Gestion d'entrées sous interruption
17/02/2020 : 5.3. Le PCA9537
                     5.14. Le TCA9534
                     6. Les multiplexeurs I2C

13 commentaires:

  1. Merci pour cette belle expérience

    RépondreSupprimer
  2. J'ai découvert depuis peu l'I2C et ton article va m'aider grandement dans le choix d'un expander. Merci pour ce partage!

    RépondreSupprimer
  3. Bonjour ! merci pour cette bible de l'I2C : un article de référence !!
    1/ J'ai relevé une erreur sur les MCP23xxx : vous parlez de commande par SPI mais c'est commandé par I2C, d'ailleurs votre schéma des paragraphes 9.2 à 9.5 le montre commandé ainsi

    2/ il y a aussi le MM5451 qui permet de commander 35 leds ou 5 x 7-segments, avec résistance de pull-up intégrée, peut-être utilisable aussi en multiplexeur ?

    RépondreSupprimer
    Réponses
    1. Les deux versions existent. MCP230XX I2C, et MCP23SXX SPI. Les schémas en 9.3 et 9.5 montrent un câblage SPI.

      Supprimer
  4. Top. Je réfléchis à un projet de table d'echecs avec des cellules reed et des pièces aimantées. Il me faut 64 pins, plus deux pour une imprimante et un mini écran. Votre article va m'aider à faire les bons choix, mais je reviendrai peut-être vers vous ! Merci.

    RépondreSupprimer
  5. Bonjour M. Bachetti. Concernant le MCP23017, est-il possible de l'utiliser pour etendre le nombre d'entrée d'un arduino UNO ? Sur tous les schémas que je vois, j'ai l'impression qu'on ne peut qu'étendre que le nombre de le sortie

    RépondreSupprimer
    Réponses
    1. Le MCP23017 est un expander de GPIOs, capable d'étendre le nombre d'entrées ET de sorties d'un microcontrôleur.

      Supprimer
    2. Avez vous un exemple de cablage du MCP23008 en entrées ?
      D'après la datasheet p.20, il faut 0.8Vcc, donc 4V pour un signal haut.
      Faut-il limiter le courant vers les entrées GP ? Ou on considère qu'en mode ENTREE, les GP ont une impédance infinie ?
      Je ne vois pas en fait comment le MCP sait que ses GP doivent être des sorties, ou entrées...

      Aussi, faut-il limiter le courant en entrée VDD ? Page 21 de la datasheet, il est noté courant d'entrée Vdd max =125mA...

      Supprimer
    3. Le schéma du paragraphe 10 montre un MCP23017 et 16 boutons. Avec un MCP23008 le schéma est le même, sauf que le nombre de boutons est limité à 8.
      Aucune limitation du courant d'entrée n'est nécessaire.
      Pour que le MCP23008 considère que ses GPIOs sont des entrées ou des sorties, il faut le configurer. Utilisez une librairie, comme :
      https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library
      La méthode pinMode() permet la configuration.
      Le courant sur VDD n'a pas besoin d'être limité, c'est à vous de limiter le courant débité sur chaque sortie.

      Supprimer