Circuit intégré 74HC595 (classe IC74hc595)

L'Arduino ne dispose que d'un nombre de pins de sortie limité. Aussi est il intéressant de pouvoir disposer d'un multiplexeur permettant de disposer de sorties supplémentaires. Le circuit intégré 74HC595 semble être dédié à cet usage. Par exemple, comme cela sera présenté dans un prochain article, l'utilisation d'afficheurs LED à segments nécessitent 8 sorties sur l'Arduino. En utilisant une puce 74HC595 en interface entre l'Arduino et l'afficheur, on peut réduire ce nombre à 3. De plus c'est un circuit peu coûteux (quelques centimes d'Euros).
Cet article présente le circuit 74HC595, son utilisation et sa programmation sur l'Arduino en C++.

Le circuit intégré 74HC595

Le circuit intégré 74HC595 est en fait un registre à décalage. C'est à dire qu'il transforme une suite d'impulsions synchronisées sur une horloge pour positionner en sortie des registres à 1 ou à 0 en fonction de la suite d'impulsions transmise.
Ce circuit utilise trois pins en entrées qui sont connectées à trois pins de sortie de l'Arduino, et fournit huit sorties parallèles. On pourrait même aller jusqu'à multiplier les sorties parallèles en couplant plusieurs circuits 74HC597 en connectant leur pin Q7' respective à la pin DS (Data) d'un autre 74HC595 comme cela est présenté dans l'article décrivant cette solution.

Câblage du 74HC595

Le circuit intégré 74HC595 possède 16 broches dont la signification est la suivante :
NomDescription
1Q1Registre de sortie parallèle 1.
2Q2Registre de sortie parallèle 2.
3Q3Registre de sortie parallèle 3.
4Q4Registre de sortie parallèle 4.
5Q5Registre de sortie parallèle 5.
6Q6Registre de sortie parallèle 6.
7Q7Registre de sortie parallèle 8.
8GNDAlimentation électrique du circuit.
Doit être reliée à GND de l'Arduino.
9Q7SSortie série.
10MRReset du circuit.
Est déclenché lorsque la pin passe à 0V.
Doit être reliée +5V (reset désactivé) sur l'Arduino.
11SH_CPPin d'entrée Clock.
12ST_CPPin d'entrée Latch.
13OESortie activée.
Doit être reliée à GND sur l'Arduino. 
14DSPin d'entrée Data.
15Q0Registre de sortie parallèle 1.
16VCCAlimentation électrique du circuit.
Doit être reliée à +5V sur l'Arduino.
Les pins Q0 à Q7 du 74HC595 peuvent être utilisées comme 8 sorties parallèles supplémentaires. Si le train d'impulsion envoyé sur DS (Data) fait plus de 8 bits, la pin Q7' permet de poursuivre le décalage en transmettant les wagons (les impulsions) non traités à un autre 74HC595. Pour cela, il faut relier la broche Q7S (pin 9) du premier circuit à la broche DS (Data) du second. Les pins SH_CP (Clock) et ST_CP (Latch) doivent être reliées entre elle pour partager l'horloge de synchronisation. On bénéficie alors encore de huit sorties parallèles supplémentaires, soit 16 en tout.

Définition de la classe IC74hc595

Afin de faciliter l'utilisation du circuit 74HC595 dans des projets, voici la classe IC74hc595. La définition de cette classe est contenue dans le fichier IC74hc595.h qui doit être inclus par le directive #include dans tous les projets dans lesquels le circuit est utilisé.
/* 1 */
#ifndef IC74hc595_h
#define IC74hc595_h

/* 2 */
#include "Component.h"

/* 3 */
class IC74hc595 : Component {
/* 4 */
  uint8_t __dataPin;
  uint8_t __latchPin;
  uint8_t __clockPin;
/* 5 */
  byte __Qvalue;

/* 6 */
  void __write(byte, uint8_t);
/* 7 */
  static byte reverseValue(byte value);

  public:
/* 8 */
  IC74hc595(uint8_t, uint8_t, uint8_t);
/* 9 */
  void begin();
  void loop() { }
/* 10 */
  void write(byte, uint8_t=MSBFIRST);
  void pinWrite(uint8_t, uint8_t);
/* 11 */
  byte QValue() { return __Qvalue; }
};

/* 1 */
#endif // IC74hc595_h
  1. Application de la bonne pratique consistant à définir les classes d'une bibliothèque entre deux directives #ifndef et #endif en prévention de multiples définitions dans les projets composites.
  2. Comme toutes les classes relatives à des composants électroniques, la classe IC74hc595 implémente l'interface Component. Le fichier Component.h doit donc être inclus par la directive #include.
  3. La classe IC74hc595 implémente l'interface Component. Ce qui l'oblige à implémenter les méthodes membres begin() et loop().
  4. Le circuit 74HC595 mobilise trois pins de l'Arduino. Les numéros de celles-ci doivent être archivées dans des attributs privés.
    • __dataPin est le numéro de la pin de l'Arduino utilisée pour le flux de données. Cette pin doit être reliée à la pin DS (n°14) du 74HC595. 
    • __latchPin est le numéro de la pin de l'Arduino  utilisée pour valider le flux de données. Cette pin doit être reliée à la pin ST_CP (n°12) du 74HC595.
    • __clockPin est le numéro de la pin de l'Arduino  utilisée pour valider le flux de données. Cette pin doit être reliée à la pin SH_CP (n°11) du 74HC595.
  5. L'attribut privé __Qvalue mémorise l'état courant des pins Q0 à Q7 du 74HC595. Un bit à 1 signifie que la tension sur la pin concernée est à +5V.
  6. La méthode privée __write() effectue le transfert du flux de données. Elle est utilisée par les méthode publique write() et pinWrite().
  7. Comme les bits peuvent être transférés dans les deux sens, de Q0 à Q7 ou de Q7 à Q0, en fonction du paramètre bitOrder passé à la méthode write(), il est nécessaire d'inverser cet ordre pour archiver la bonne valeur de l'attribut __QValue. C'est le rôle de la méthode statique reversValue(). De cette manière, le bit de poids le plus faible de __QValue correspond pond toujours à Q0 et celui-de poids le le fort correspond toujours à Q7
  8. Le constructeur de la classe IC74hc595  reçoit trois paramètres correspondant respectivement aux pins Data, Latch et Clock mobilisées sur l'Arduino.
  9. La classe IC74hc595 implémentant l'interface Component, celle-ci doit implémenter les méthodes begin() et loop(). La méthode begin() doit être invoquée soit dans la méthode setup() de l'Arduino, soit dans la méthode begin() du composite utilisant le circuit. La méthode loop() ne fait rien. Elle est implémentée vide dans la définition de la classe IC74hc595.
  10. La classe IC74hc595 implémente deux méthodes publiques pour modifier l'état des pins Q0 à Q7 du circuit :
    • write() permet de modifier l'état de toutes les pins Q0 à Q7 à la fois par le paramètre value. L'ordre des pins modifiées dépend du deuxième paramètre (bitOrder). Si bitOrder vaut MSBFIRST, l'état des pins Q0 à Q7 est celui des bits de value (la pin Q0 correspond au bit de pois le plus faible de value). Si bitOrder vaut LSBFIRST, l'état des pins Q0 à Q7 est inverse à celui des bits de value (la pin Q7 correspond au bit de pois le plus faible de value).
    • pinWrite() fonctionne comme la fonction digitalWrite() de l'Arduino. Elle permet de positionner la pin Qx du 74HC595 dont le numéro est passé en paramètre.
  11. La classe expose l'état courant des pins Q0 à Q7 du 74HC595, enregistré dans l'attribut __Qvalue,  en lecture seule, par la méthode QValue().

Implémentation de la classe IC74hc595

Constructeur de la classe IC74hc595

Le circuit 74HC595 mobilise trois pins de l'Arduino. Les numéros de ces trois pins sont passés en paramètre du constructeur.
  • dataPin est le numéro de la pin de l'Arduino utilisée pour le flux de données. Cette pin doit être reliée à la pin DS (n°14) du 74HC595. 
  • latchPin est le numéro de la pin de l'Arduino  utilisée pour valider le flux de données. Cette pin doit être reliée à la pin ST_CP (n°12) du 74HC595.
  • clockPin est le numéro de la pin de l'Arduino  utilisée pour valider le flux de données. Cette pin doit être reliée à la pin SH_CP (n°11) du 74HC595.
IC74hc595::IC74hc595(uint8_t dataPin, uint8_t latchPin, uint8_t clockPin) {
  this->__dataPin = dataPin;
  this->__latchPin = latchPin;
  this->__clockPin = clockPin;
  this->__Qvalue = 0;
}
  • Le trois numéros de pins sont mémorisées dans les trois attributs privés __dataPin, __latchPin, __clockPin.
  • La valeur de l'attribut __Qvalue est initialisée à 0. Ce qui signifie que toutes les pins Q0 à Q7 du 74HC595 sont positionnées à LOW. 

Méthode begin()

La méthode begin() est héritée de l'interface Component qui définit un polymorphisme sur tous les composants C++ en les obligeant à implémenter les méthodes begin() et loop(). Cette méthode doit être invoquée soit dans la méthode setup() du sketch Arduino, soit dans la méthode begin() des composites qui utilisent ce composant.
void IC74hc595::begin() {
  pinMode(this->__latchPin, OUTPUT);
  pinMode(this->__dataPin, OUTPUT);
  pinMode(this->__clockPin, OUTPUT);
  digitalWrite(this->__latchPin, HIGH);
  this->__write(this->__Qvalue, MSBFIRST);
}
  • Les trois pins mobilisées sur l'Arduino par le circuit 74HC595 sont configurées en sortie (OUTPUT) en utilisant la fonction pinMode().
  • La pin Latch est pré-positionnée à HIGH. En effet, elle doit être positionnée à LOW pendant qu'une transmission d'un flux de bits est en cours.
  • Toutes les pins Q0 à Q7 du 74HC595 sont initialisée par rapport à la valeur de l'attribut __QValue. De fait elles sont toutes positionnées à 0.

Méthode privée __write()

La méthode __write() positionne les pins de sortie Q0 à Q7 du 74HC595 en fonction du paramètre value. L'ordre des bits dépend de la valeur du paramètre bitOrder. Si bitOrder vaut MSBFIRST, Q0 correspond au bit de poids le plus faible (celui le plus à droite) de value. Si bitOrder vaut LSBFIRST, Q0 correspond au bit de poids le plus fort (celui le plus à gauche) de value
void IC74hc595::__write(byte value, uint8_t bitOrder) {
  digitalWrite(this->__latchPin, LOW);
  digitalWrite(this->__clockPin, LOW);
  shiftOut(this->__dataPin, this->__clockPin, bitOrder, value);
  digitalWrite(this->__latchPin, HIGH);
}
  • La première étape consiste à positionner la pin Latch à 0 pour indiquer au 74HC595 qu'un flux de bits va lui être transmis. 
  • Avant la transmission, on s'assure que la pin Clock est à la position 0 pour éviter une interprétation erronée de celle-ci par des circuits électronique synchronisant les changements d'état sur le flanc montant de l'horloge.
  • La fonction shiftOut() est utilisée pour transmettre le train de bits.
  • Une fois la transmission terminée, la pin Latch est repositionnée à 1.

Méthode publique write()

La méthode publique write() (sans le préfixe souligné), fait la même chose que la méthode privée __write(). En plus elle mémorise l'état des pins Q0 à Q7 dans l'attribut __Qvalue.
void IC74hc595::write(byte value, uint8_t bitOrder) {
  this->__write(value, bitOrder);
  this->__Qvalue = (bitOrder == LSBFIRST) ? reverseValue(value) : value;
}
  • La méthode publique write() invoque la méthode privée __write(). C'est normal puisqu'elles font la même chose.
  • Le paramètre value est archivé dans l'attribut __QValue pour que l'on puisse connaitre à tout instant l'état des pins de sortie Q0 à Q7 du 74HC595. Si le paramètre bitOrder vaut LSBFIRST, l'ordre des bits de value est inversé pour que le bit de poids le plus faible de __Qvalue soit toujours relatif à l'état de la pin Q0

Méthode publique pinWrite()

La méthode pinWrite() de la classe IC74hc595, pour les pins de sortie du circuit 74HC595, a le même rôle que la fonction digitalWrite() sur les pins de l'Arduino. Elle positionne la pin Qx du 74HC595 dont le numéro x (de 0 à 7) est passé par le paramètre pin. Comme pour digitalWrite(), le paramètre value peut avoir la valeur HIGH (+5V) ou LOW (0V).
Cette méthode permet de disposer de 8 sorties supplémentaires en ne mobilisant que trois pins de l'Arduino. Elle est un peu plus lente (108 micro-secondes) à l'exécution que digitalWrite() car un train de huit bits est envoyé sur la pin Data à chaque écriture.
void IC74hc595::pinWrite(uint8_t pin, uint8_t value) {
  uint8_t bitRange = 1 << pin;
  switch (value) {
    case HIGH:
      this->__Qvalue |= bitRange;
      break;
    case LOW:
      this->__Qvalue &= ~bitRange;
      break;
  }
  this->__write(this->__Qvalue, MSBFIRST);
}
  • Le bit à modifier est positionné dans la variable locale bitRange par un décalage de pin bits vers la gauche.
  • Si value vaut HIGH, un OU binaire est effectué sur l'attribut __Qvalue pour ne modifier que le bit de rang pin en le passant à 1.
  • Si value vaut LOW, un ET binaire est effectué sur l'attribut __Qvalue pour ne modifier que le bit de rang pin en le passant à 0.
  • Une fois modifié, l'attribut __Qvalue est écrit sur le 74HC595 en invoquant la méthode privée __write(). A chaque invocation de pinWrite(), les 8 bits de __Qvalue sont transmis.

Méthode statique reverseValue()

La méthode reversValue() inverse l'ordre des bits du paramètre value pour les fournir en résultat. C'est une méthode de classe déclarée static, car elle n'affecte en rien l'instance de la classe à laquelle elle est attachée. Cela permet, quelque soit l'ordre des bits passé en paramètre de la méthode write(), d'enregistrer l'état des pins de sortie du 74HC595 toujours dans le même ordre dans l'attribut __Qvalue
byte IC74hc595::reverseValue(byte value) {
  byte result = 0;
  char buffer[64];
  for (int i = 0; i < 8; i++) {
    result <<= 1;
    if (value & 1) {
      result |= 1;
    }
    value >>= 1;
  }
  return result;
}

Utilisation de la classe IC74hc595 dans un sketch Arduino

L'utilisation de la classe IC74hc595 dans un sketch Arduino s'effectue comme pour tous les autres composants présentés sur ce blog. Voici, ci-dessous, un exemple, reprenant l'exemple d'une LED clignotante connectée sur une des pins du circuit 74HC595 au lieu d'être connectée directement sur l'Arduino :
/* 1 */
#define IC_DATA 2
#define IC_LATCH 3
#define IC_CLOCK 4
#define LED 0

/* 2 */
#include "IC74hc595.h"

/* 3 */
IC74hc595 ic(IC_DATA, IC_LATCH, IC_CLOCK);

/* 4 */
void setup() {
  ic.begin();
}

/* 5 */
void loop() {
  ic.pinWrite(LED, HIGH);
  delay(1000);
  ic.pinWrite(LED, LOW);
  delay(1000);
}
  1. Il est d'une bonne pratique que de déclarer en constantes les numéros de pins utilisées tant sur l'Arduino que sur le circuit 74HC595 :
    • IC_DATA est le numéro de pin de l'Arduino sur laquelle est connectée la pin Data du 74HC595.
    • IC_LATCH est le numéro de pin de l'Arduino sur laquelle est connectée la pin Latch du 74HC595.
    • IC_CLOCK est le numéro de pin de l'Arduino sur laquelle est connectée la pin Clock du 74HC595.
    • LED est le numéro de pin du 74HC595  sur laquelle est connectée la LED. Dans cet exemple, on considère que la LED est connectée à la broche Q0 du 74HC595.
  2. Le sketch utilisant la classe IC74hc595, la définition de celle-ci doit être incluse par une directive #include
  3. La classe IC74hc595 est instanciée par la variable ic à laquelle on passe les numéros de pins dans l'ordre exigé par le constructeur.
  4. L'instance ic est initialisée dans la méthode setup() en invoquant sur celle-ci la méthode bégin().
  5. Dans la méthode loop(), on reprend le code d'une LED clignotante. Mais au lieu d'invoquer la fonction digitalWrite() sur une pin de l'Arduino, on invoque la méthode pinWrite() sur l'instance ic de la classe IC74hc595.
    A remarquer que, contrairement à la plupart des composants dérivés de la classe Component, il n'est pas nécessaire d'invoquer la méthode loop() sur l'instance ic. Car, comme vu dans la définition de la classe IC74hc595, celle-ci ne fait rien. 

Conclusion

La classe IC74hc595, présentée dans cet article, permet d'étendre les capacités de base d'un Arduino, en offrant 8 sorties parallèles supplémentaires. C'est très utile pour les circuits gourmand en pins utilisé comme les afficheurs à LED qui, sinon, monopoliseraient à eux seuls 8 pins, oblitérant ainsi la possibilité de connecter d'autre composants.
Un prochain article présentera l'utilisation de cette classe pour un afficheur à LED à 7 segments. Ce qui en est l'utilisation la plus courante.
Mais il est possible d'utiliser le circuit 74HC595 de façon astucieuse pour piloter un écran LCD tel que le module LCD 1602 déjà utilisé dans le projet Horloge. Cela nécessite la programmation d'un nouveau driver à utiliser en  lieu et place du driver LiquidCristal utilisé habituellement avec ce composant.  

Commentaires

Posts les plus consultés de ce blog

Afficheur à LED 7 segments (classe SegmentLedDisplay)

Piloter un clavier matriciel sur le bus I2C avec un PCF8574

Utiliser Visual Studio Code pour développer sur Arduino