Envie de rajouter tout plein de sorties à votre Arduino, et ce, le plus simplement possible ? Alors rien de tel que le circuit intégré 74 HC 595, pour faire cela ! Car non seulement le 74HC595 permet de rajouter jusqu’à 8 sorties supplémentaires, mais il peut également « s’empiler à l’infini », pour mettre à disposition une « infinité » de sorties ! Et ce, avec 3 fils de commande seulement ! Sympa, non ?
Dans cet article, je vais tout d’abord vous présenter et vous expliquer comment fonctionne la puce 74 HC 595 (qui est ni plus ni moins qu’un registre à décalage 8 bits, le plus souvent utilisé pour faire de la conversion série → parallèle), et vous partager 2 exemples de rajout de sorties à un Arduino Nano (transposables sur tout autre arduino ou autre microcontrôleur, au besoin !).
Le 74xx595 est une puce datant de très nombreuses années. Cela étant dit, elle reste encore très utile de nos jours, preuve en est ! Bien sûr, cette puce n’est pas le seul moyen d’ajouter des sorties à un arduino (ou tout autre microcontrôleur). Je vous avais d’ailleurs présenté le Module PCF8574 à commande I²C pour rajouter des sorties à un arduino, par exemple. Vous avez donc du choix, au final, en fonction de vos besoins 😉
Qu’est-ce que le 74HC595 (registre à décalage 8 bits avec verrou) ?
Avant toute chose, il est important de bien comprendre comment le 74HC595 fonctionne ; sans quoi vous risquez fort de vous perdre, dans le code arduino ! Au passage, n’hésitez pas à faire un saut sur la documentation technique du 74HC595 (datasheet), pour plus de clarté encore 😉
Mais pour faire simple, je dirais que le 74 HC 595 est un circuit intégré qui permet de piloter jusqu’à 8 sorties supplémentaires (et indépendantes), en fonction des données qui lui sont envoyées une à une. Pour ainsi dire, cette puce fait donc de la conversion série (l’entrée de nos données, bit à bit) vers parallèle (nos 8 sorties individuelles).
Mais cette conversion série → parallèle, en interne de la puce, ne se fait pas n’importe comment ; en effet, cela se fait par une série de décalages, et ce, pour chaque nouvelle donnée entrante (d’où le nom de « registre à décalage »). Dit plus clairement, on pourrait dire que chaque nouveau bit entrant sur la ligne de données est « poussé » en sortie, décalant ainsi toutes les autres sorties d’un cran (bien que réellement, ce process est un peu plus riche et complexe que ça !). Au final, comme le 74HC595 possède 8 sorties, ces 8 sorties seront simplement à l’image des 8 derniers bits envoyés sur la ligne d’entrée.
Pour ce faire, en interne, le 74 HC 595 dispose d’une mémoire tampon d’entrée de 8 bits (un « registre à décalage à 8 étages ») et d’une mémoire de sortie à 8 bits (verrouillant l’état des broches de sortie lorsqu’on le souhaite, d’où le terme de « registre avec verrou », quand on parle du 74HC595).
Du reste, au niveau du pinout du 74HC595 et de son schéma interne simplifié, voici ce que ça donne :
Au niveau des broches, on retrouve :
- l’entrée DS (de l’anglais « data serial » input), qui permet l’entrée des bits de données (0 ou 1, donc état bas ou état haut)
- les sorties Q0 à Q7, qui sont à l’image des 8 derniers bits envoyés sur la ligne DS
- la sortie Q7′, qui permet de récupérer le bit perdu à chaque décalage (c’est la dernière valeur de Q7 avant décalage, si vous préférez) ; cette ligne est utile pour chaîner les 74HC595 entre eux, et ainsi, permettre d’avoir un nombre « infini » de sorties supplémentaires
- l’entrée SH_CP (signifiant « shift clock pin »), qui permet d’enregistrer/prendre en compte la donnée présentée sur la ligne DS, en poussant cette valeur sur le registre d’entrée à 8 étages (servant de mémoire tampon), après décalage de toutes les valeurs précédentes
- l’entrée ST_CP (signifiant « store clock pin », mais aussi appelée « latch pin »), qui permet de « transférer » l’état des 8 bits du registre d’entrée vers le registre de sortie ; en sachant qu’on peut activer cette ligne quand on veut (et si on n’y touche pas, les arrivées de bits sur l’entrée sont simplement mémorisées dans la mémoire tampon d’entrée, mais non reportées en sortie ; la puce se dit donc « verrouillée », dans cet état)
- l’entrée /OE (de l’anglais « output enable »), qui permet d’activer ou non les sorties (en connectant ou non le buffer de sortie de la puce) ; si les sorties sont déconnectées, on retrouve un état à haute impédance sur celles-ci
- et l’entrée /MR (de l’anglais « master reset »), qui permet de vider (mettre à zéro / effacer) tous les bits du registre tampon d’entrée
Maintenant, voyons comment transitent les données
Comment faire de la conversion série → parallèle avec cette puce ?
Visuellement parlant, voici un exemple de transmission de données (en série) afin de les retrouver en sortie (en parallèle), utilisant le circuit intégré 74 HC 595 (lien datasheet du 74HC595, au besoin) :
Je pense que cette image se passe de commentaires, enfin … je l’espère ! Car ce n’est pas plus compliqué que de se dire que tout ce qui entre par l’entrée DS se retrouve à la queue leu-leu en sortie 😉
À noter que j’ai simplifié les explications au maximum, afin que vous compreniez bien le principe de base de fonctionnement du 74HC595. Car il y aurait bien d’autres choses à dire ici, comme le fait que :
- la sortie Q7 n’est pas réellement « perdue » à chaque décalage ; en effet, elle se retrouve sur une sortie auxiliaire « Q7’ », qui permet d’attaquer l’entrée DS d’un autre 74xx595, pour faire du chaînage (et ainsi, démultiplier le nombre de sorties)
- l’état des broches de sorties Q0 à Q7 n’est pas systématiquement mis à jour à chaque nouveau bit entrant sur DS ; en effet, comme expliqué au chapitre précédent, c’est la broche ST_CP qui permet de dire au 74HC595 quand est-ce qu’il doit mettre à jour l’état de ses broches de sorties (sur ce schéma, il est donc implicite qu’on active la mise à jour des sorties à chaque nouveau bit entrant sur DS)
Bon, arrêtons-nous là avec la théorie, et passons à la pratique ! Ça sera bien plus intéressant 😉
Câbler un 74HC595 sur un Arduino, avec 3 fils de commande seulement !
Voici un exemple de câblage, pour vous montrer comment raccorder un 74HC595 à un Arduino Uno, basiquement :
Au passage, il faut bien comprendre qu’on peut choisir les broches Arduino de son choix ; en effet, on n’est pas obligé d’utiliser les broches D2/D3/D4 comme je l’ai fait ici (car c’est juste un exemple de raccordement possible).
De plus, vous avez certainement remarqué que 3 fils de commande suffisent pour contrôler le 74 HC 595, basiquement. Mais si vous souhaitez « aller plus loin », et avoir un contrôle plus en profondeur de la puce, vous pouvez également rajouter 2 autres fils de commande (raccordés sur /OE et /MR), permettant :
- l’activation/désactivation des sorties Q0 à Q7 (qui se fait via la ligne /OE)
- la mise à zéro du registre à décalage d’entrée, de la puce (qui se fait via la ligne /MR)
Perso, de mon côté, j’utilise 4 fils de commande, afin d’avoir également la main sur l’état des sorties du 74HC595 au démarrage de l’arduino (via la ligne /OE, donc). C’est d’ailleurs ce que vous pourrez constater, dans les 2 exemples pratiques qui suivent !
Code exemple #1 : ajouter 8 sorties à l’arduino avec 1 x 74HC595 (structure de base)
Premier exemple que je vous propose de découvrir ici : un circuit permettant de piloter 8 leds déportées, branchées sur un 74HC595, le tout piloté par Arduino ! Ainsi, vous verrez comment on peut aisément rajouter 8 sorties supplémentaires à un arduino, avec un seul 74 HC 595 !
Nota : j’ai opté pour un Arduino Nano ici, afin de pouvoir monter l’ensemble du montage-exemple sur breadboard. Mais rien ne vous empêche d’utiliser toute autre carte Arduino ou autre microcontrôleur, bien entendu 😉
Voici ce que ça donne, au niveau schématique :
On retrouve notre Arduino Nano, un 74 HC 595 au format DIP (référence 74HC595N), un bargraphe à 8 LEDs rouges, et un réseau de résistances (ces deux derniers composants étant plus compacts que câbler 8 leds avec 8 résistances séparées !).
Au niveau des broches arduino utilisées, on retrouve les pins :
- 5V/GND pour fournir l’énergie au reste du montage
- D3 pour piloter la ligne /OE (output enable) du 74HC595N
- D4 pour piloter la ligne DS (data serial) du 74HC595N
- D5 pour piloter la ligne SH_CP (shift clock) du 74HC595N
- D6 pour piloter la ligne ST_CP (store clock) du 74HC595N
À noter qu’il n’est pas obligatoire de raccorder la ligne /OE à l’arduino, dans l’absolu. En fait, si je l’ai fait, c’est simplement pour avoir la possibilité d’activer les sorties du 74HC595 qu’une fois l’Arduino « complètement » démarré (ce qui peut prendre une seconde ou deux, le temps de l’exécution du bootloader et de la fonction setup). En clair, si j’ai fait ça, c’est pour que les LEDs connectées au 74 HC 595 ne s’illuminent pas de manière aléatoire, le temps que l’arduino ait pu initialiser toutes les sorties Q0 à Q7. Ceci me semble être une une bonne pratique, d’ailleurs, afin de ne pas avoir des états « indéterminés », à la mise sous tension de l’ensemble 🙂
Liste des composants :
Au final, voici à quoi le montage doit ressembler, une fois câblé sur breadboard :
À présent, voyons le code arduino, qui va donner vie à ce montage !
Au passage, le programme qui suit permettra de :
- allumer les LEDs les unes après les autres
- les éteindre les unes après les autres
- en allumer une sur deux (leds « impaires »), puis les éteindre
- en allumer une sur deux (leds « paires »), puis les éteindre
- et reboucler tout ça à l’infini !
Ça vous dit ? Alors voici le code arduino pour ce faire :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_)
| [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___
| ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \
| | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___|
| |
\_|
Fichier : prgArduino-1-piloter-8-sorties-avec-un-74HC595.ino
Description : Programme permettant de piloter individuellement chacune des 8 sorties
d'un 74 HC 595 (ou équivalent), à partir d'un Arduino Nano
Remarques : Illustration sur bargraphe à 8 leds rouges
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Licence : BY-NC-ND 4.0 CC (https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fr)
Créé le : 21.02.2024
*/
// Définition des broches de raccordement Arduino Nano → circuit intégré 74HC595
// (nota : ici, j'ai utilisé les broches D3/D4/D5/D6, mais on aurait très bien utiliser d'autres pins de l'arduino)
#define brochePourActivationDesSorties 3 // Sortie D3 de l'Arduino Nano vers la broche 13 (/OE) du 74HC595 Autres appellations :
#define brochePourLesDonnees 4 // Sortie D4 de l'Arduino Nano vers la broche 14 (DS) du 74HC595 --> data pin
#define brochePourDecalerLesDonnees 5 // Sortie D5 de l'Arduino Nano vers la broche 11 (SH_CP) du 74HC595 --> clock pin (ou shift clock pin)
#define brochePourVerouillerLesDonnees 6 // Sortie D6 de l'Arduino Nano vers la broche 12 (ST_CP) du 74HC595 --> latch pin (ou store clock pin)
// Définition des nouvelles sorties de notre arduino
#define SORTIE_SUPPLEMENTAIRE_0 0 // Correspond à la broche Q0 du 74 HC 595
#define SORTIE_SUPPLEMENTAIRE_1 1 // Correspond à la broche Q1 du 74 HC 595
#define SORTIE_SUPPLEMENTAIRE_2 2 // Correspond à la broche Q2 du 74 HC 595
#define SORTIE_SUPPLEMENTAIRE_3 3 // Correspond à la broche Q3 du 74 HC 595
#define SORTIE_SUPPLEMENTAIRE_4 4 // Correspond à la broche Q4 du 74 HC 595
#define SORTIE_SUPPLEMENTAIRE_5 5 // Correspond à la broche Q5 du 74 HC 595
#define SORTIE_SUPPLEMENTAIRE_6 6 // Correspond à la broche Q6 du 74 HC 595
#define SORTIE_SUPPLEMENTAIRE_7 7 // Correspond à la broche Q7 du 74 HC 595
// Définition des autres "constantes"
#define delaiEntreChangementsAffichage 200 // Temps de maintien d'un affichage, exprimé en millisecondes, avant de passer au suivant
// Variables
byte REGISTRE_DU_PORT_SUPPLEMENTAIRE;
// ========================
// Initialisation programme
// ========================
void setup() {
// Initialisation des broches
pinMode(brochePourLesDonnees, OUTPUT); digitalWrite(brochePourLesDonnees, LOW);
pinMode(brochePourDecalerLesDonnees, OUTPUT); digitalWrite(brochePourDecalerLesDonnees, LOW);
pinMode(brochePourVerouillerLesDonnees, OUTPUT); digitalWrite(brochePourVerouillerLesDonnees, LOW);
pinMode(brochePourActivationDesSorties, OUTPUT); digitalWrite(brochePourActivationDesSorties, HIGH); // Aucune activation des sorties, pour l'instant
// Initialisation d'une mémoire tampon, qui contiendra l'image de l'état que les 8 sorties du 74HC595 devront avoir
REGISTRE_DU_PORT_SUPPLEMENTAIRE = 0b00000000; // Mise à 0 des 8 bits de ce registre "virtuel"
// Initialisation du 74 HC 595
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_0, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_1, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_2, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_3, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_4, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_5, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_6, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_7, LOW);
digitalWrite(brochePourActivationDesSorties, LOW); // Puis activation des 8 sorties du 74HC595
// Attente d'une seconde, avant de lancer la boucle "loop"
// (pour bien montrer que les sorties sont à l'état bas, avant d'exécuter le programme principal)
delay(1000);
}
// =============================================
// Fonction : extendedDigitalWrite(sortie, état)
// =============================================
void extendedDigitalWrite(byte sortieSupplementaireVisee, bool nouvelEtat) {
// Mise à 1 ou 0 du bit (numéro de sortie) visé
bitWrite(REGISTRE_DU_PORT_SUPPLEMENTAIRE, sortieSupplementaireVisee, nouvelEtat);
// Et mise à jour des sorties du 74HC595
envoyerUnOctetAu74HC595(REGISTRE_DU_PORT_SUPPLEMENTAIRE);
}
// ==================================
// Fonction : envoyerUnOctetAu74HC595
// ==================================
void envoyerUnOctetAu74HC595(byte octetAenvoyer) {
// Mise au niveau bas de la ligne de verrouillage (car ensuite, un front montant sur celle-ci induira un "transfert + verrouillage" des données en sortie)
digitalWrite(brochePourVerouillerLesDonnees, LOW);
// Envoi des données
shiftOut(brochePourLesDonnees, brochePourDecalerLesDonnees, MSBFIRST, octetAenvoyer);
// Mise au niveau haut de la ligne de verrouillage, pour générer un front montant sur cette ligne,
// et ainsi, enclencher un "transfert + verrouillage" des données en sortie du 74HC595
digitalWrite(brochePourVerouillerLesDonnees, HIGH);
}
// =================
// Boucle principale
// =================
void loop() {
// *** ANIMATION #1 ***
// Allumage de toutes les leds, les unes après les autres, de droite à gauche
for(byte indexSortiesSupplementaire = 0 ; indexSortiesSupplementaire < 8 ; indexSortiesSupplementaire++) {
extendedDigitalWrite(indexSortiesSupplementaire, HIGH);
delay(delaiEntreChangementsAffichage);
}
// *** ANIMATION #2 ***
// Extinction de toutes les leds, les unes après les autres, de droite à gauche
for(byte indexSortiesSupplementaire = 0 ; indexSortiesSupplementaire < 8 ; indexSortiesSupplementaire++) {
extendedDigitalWrite(indexSortiesSupplementaire, LOW);
delay(delaiEntreChangementsAffichage);
}
// *** AFFICHAGE #3 ***
// Allumage d'une led sur deux, avec la 1ère allumée (puis la deuxième éteinte, etc), en partant de la droite vers la gauche
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_0, HIGH);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_1, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_2, HIGH);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_3, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_4, HIGH);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_5, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_6, HIGH);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_7, LOW);
delay(delaiEntreChangementsAffichage);
// *** AFFICHAGE #4 ***
// Allumage d'une led sur deux, avec la 1ère éteinte (puis la deuxième allumée, etc), en partant de la droite vers la gauche
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_0, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_1, HIGH);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_2, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_3, HIGH);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_4, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_5, HIGH);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_6, LOW);
extendedDigitalWrite(SORTIE_SUPPLEMENTAIRE_7, HIGH);
delay(delaiEntreChangementsAffichage);
// Effacement de toutes les sorties, puis on reboucle, à l'infini !
for(byte indexSortiesSupplementaire = 0 ; indexSortiesSupplementaire < 8 ; indexSortiesSupplementaire++) {
extendedDigitalWrite(indexSortiesSupplementaire, LOW);
}
}
Si le code ne vous semble pas très clair, ne paniquez pas ! Et focalisez-vous seulement sur la partie loop(), pour commencer. Ici, vous verrez comment sont réalisées les 4 animations led qui suivent (cf. ci-dessous), avec une fonction fait-maison nommée « extendedDigitalWrite » (elle s’utilise comme la fonction native arduino « digitalWrite », sauf que là, on adresse les 8 sorties du 74HC595 et non celles de l’Arduino).
Basiquement, la fonction « extendedDigitalWrite » permet de faire 2 choses successives :
- sauvegarder le nouvel état de la sortie visée (dans une variable tampon de 8 bits, qui contient l’état attendu des 8 sorties du 74HC595N)
- et envoyer les 8 états souhaités sur les 8 sorties du 74 HC 595
En clair : lorsqu’on modifie l’état d’une sortie avec cette fonction, ce n’est pas simplement l’état de la sortie modifiée qui est envoyée au 74HC595, mais l’état attendu sur toutes les sorties de celui-ci. Et c’est bien normal ! Car le 74HC595 est un registre à décalage, il faut donc systématiquement renvoyer les 8 bits correspondants aux 8 sorties à piloter, et ce, à chaque changement d’état d’une sortie. C’est pourquoi on retrouve la fonction « envoyerUnOctetAu74HC595 », dans la fonction « extendedDigitalWrite » !
Et si vous allez plus en profondeur, et examinez de plus près la fonction « envoyerUnOctetAu74HC595 », alors vous verrez qu’elle est principalement constituée de la fonction native arduino « shiftOut », pour faire de l’émission série sur une broche donnée. Avec un déverrouillage/verrouillage du 74 HC 595 avant et après le shiftOut, afin de faire la mise à jour des 8 sorties SEULEMENT APRÈS l’envoi des 8 états de sorties souhaités.
Cela se traduit visuellement comme ceci, pour être plus parlant !
Plutôt sympa, non ?
Mais que diriez-vous de rajouter quelques leds en plus … voire beaucoup plus ! Ça vous dit ? Alors on continue 🙂
Code exemple #2 : ajouter 40 sorties à l’arduino avec 5 x 74HC595 (ou plus encore !)
Pour bien vous montrer qu’on peut rajouter un nombre « incroyable » de sorties sur un Arduino, en branchant tout simplement plusieurs 74HC595 en série (chaînage), voici un exemple pratique permettant d’ajouter 40 sorties à un simple/petit Arduino Nano.
Pour ce faire, voici le schéma de branchement, mettant en œuvre 1 arduino et 5 puces 75HC595N :
Une remarque au passage, histoire d’être bien clair : vous aurez toujours le même nombre de fils de commande côté arduino (3 à 5, selon ce que vous souhaitez faire), et ce, quelque soit le nombre de 74 HC 595 connectés dessus ! Ici je vous montre un exemple avec 5 puces 74HC595N, mais on aurait pu en mettre 20,30, ou 50, que cela n’aurait rien changé au câblage côté arduino !
Pour ma part, comme je voulais vous présenter un montage animé sur breadboard, je me suis ici limité à 5 x 74HC595n. Sans parler du nombre de fils duponts nécessaires, qui va croissant en fonction du nombre de 74 HC 595 que vous mettrez en œuvre (comme visible, juste ci-dessous !).
Au niveau des composants utilisés ici, on retrouve :
Au niveau du programme arduino, on retrouve là aussi quelque chose de semblable à l’exemple précédent à 1 circuit 74HC595, permettant de faire :
- un allumage des leds de droite à gauche
- puis une extinction des leds de droite à gauche
- puis l’allumage/extinction de toutes les leds de rang « paires »
- et l’allumage/extinction de toutes les leds de rang « impaires »
Voici les lignes de code, pour réaliser de telles animations :
/*
______ _ _///_ _ _ _
/ _ \ (_) | ___| | | | (_)
| [_| |__ ___ ___ _ ___ _ __ | |__ | | ___ ___| |_ _ __ ___ _ __ _ ___ _ _ ___
| ___/ _ \| __|| __| |/ _ \| '_ \_____| __|| |/ _ \/ _| _| '__/ \| '_ \| |/ \| | | |/ _ \
| | | ( ) |__ ||__ | | ( ) | | | |____| |__ | | __/| (_| |_| | | (_) | | | | | (_) | |_| | __/
\__| \__,_|___||___|_|\___/|_| [_| \____/|_|\___|\____\__\_| \___/|_| |_|_|\__ |\__,_|\___|
| |
\_|
Fichier : prgArduino-2-piloter-40-sorties-avec-5-ci-74HC595.ino
Description : Programme permettant de piloter individuellement chacune des 40 sorties,
issues de 5 circuits intégrés 74HC595 (ou équivalent), à partir d'un Arduino Nano
Remarques : Illustration sur 5 bargraphes, à 8 leds rouges chacun
Auteur : Jérôme TOMSKI (https://passionelectronique.fr/)
Licence : BY-NC-ND 4.0 CC (https://creativecommons.org/licenses/by-nc-nd/4.0/deed.fr)
Créé le : 21.02.2024
*/
// Définition des broches de raccordement Arduino Nano → circuit intégré 74HC595
// (nota : ici, j'ai utilisé les broches D3/D4/D5/D6, mais on aurait très bien utiliser d'autres pins de l'arduino)
#define brochePourActivationDesSorties 3 // Sortie D3 de l'Arduino Nano vers la broche 13 (/OE) du 74HC595 Autres appellations :
#define brochePourLesDonnees 4 // Sortie D4 de l'Arduino Nano vers la broche 14 (DS) du 74HC595 --> data pin
#define brochePourDecalerLesDonnees 5 // Sortie D5 de l'Arduino Nano vers la broche 11 (SH_CP) du 74HC595 --> clock pin (shift clock pin)
#define brochePourVerouillerLesDonnees 6 // Sortie D6 de l'Arduino Nano vers la broche 12 (ST_CP) du 74HC595 --> latch pin (store clock pin)
// Définition des étiquettes des 40 nouvelles sorties, réparties en 5 ports (PORT_0, PORT_1, PORT_2, PORT_3, PORT_4) de 8 sorties (SORTIE_0 à SORTIE_7)
const byte PORT_0 = 0;
const byte PORT_1 = 1;
const byte PORT_2 = 2;
const byte PORT_3 = 3;
const byte PORT_4 = 4;
const byte SORTIE_0 = 0;
const byte SORTIE_1 = 1;
const byte SORTIE_2 = 2;
const byte SORTIE_3 = 3;
const byte SORTIE_4 = 4;
const byte SORTIE_5 = 5;
const byte SORTIE_6 = 6;
const byte SORTIE_7 = 7;
// Définition des autres "constantes"
#define delaiEntreChangementsAffichage1 50 // Temps de maintien d'un affichage "aller-retour", exprimé en millisecondes, avant de passer d'une led à l'autre
#define delaiEntreChangementsAffichage2 1000 // Temps de maintien d'un affichage "test sorties", exprimé en millisecondes, avant de passer au test suivant
// Mémorisation de l'état des sorties pour chaque port
byte memoire_etat_des_8_sorties_du_port_0;
byte memoire_etat_des_8_sorties_du_port_1;
byte memoire_etat_des_8_sorties_du_port_2;
byte memoire_etat_des_8_sorties_du_port_3;
byte memoire_etat_des_8_sorties_du_port_4;
// ========================
// Initialisation programme
// ========================
void setup() {
// Initialisation des broches
pinMode(brochePourLesDonnees, OUTPUT); digitalWrite(brochePourLesDonnees, LOW);
pinMode(brochePourDecalerLesDonnees, OUTPUT); digitalWrite(brochePourDecalerLesDonnees, LOW);
pinMode(brochePourVerouillerLesDonnees, OUTPUT); digitalWrite(brochePourVerouillerLesDonnees, LOW);
pinMode(brochePourActivationDesSorties, OUTPUT); digitalWrite(brochePourActivationDesSorties, HIGH); // Aucune activation des sorties, pour l'instant
// Initialisation des mémoires tampon, qui contiendront les images de l'état que les 8 sorties de chaque 74HC595 devront avoir
memoire_etat_des_8_sorties_du_port_0 = 0b00000000; // Mise à 0 des 8 bits du registre "virtuel" du port 0
memoire_etat_des_8_sorties_du_port_1 = 0b00000000; // Mise à 0 des 8 bits du registre "virtuel" du port 1
memoire_etat_des_8_sorties_du_port_2 = 0b00000000; // Mise à 0 des 8 bits du registre "virtuel" du port 2
memoire_etat_des_8_sorties_du_port_3 = 0b00000000; // Mise à 0 des 8 bits du registre "virtuel" du port 3
memoire_etat_des_8_sorties_du_port_4 = 0b00000000; // Mise à 0 des 8 bits du registre "virtuel" du port 4
// Initialisation des 74 HC 595
envoyerDesDonneesAu74HC595(); // Transmission de l'état souhaité des 40 sorties (5 octets de 8 bits)
digitalWrite(brochePourActivationDesSorties, LOW); // Puis activation des 8 sorties de chaque 74HC595
// Attente d'une demi-seconde, avant de lancer la boucle "loop"
// (pour bien montrer que les sorties sont à l'état bas, avant d'exécuter le programme principal)
delay(500);
}
// =========================================================
// Fonction : expandedDigitalWrite(port, sortie visée, état)
// =========================================================
void expandedDigitalWrite(byte port, byte sortieVisee, bool nouvelEtat) {
// Mise à 1 ou 0 du bit (numéro de sortie) visé
switch(port) {
case 0:
bitWrite(memoire_etat_des_8_sorties_du_port_0, sortieVisee, nouvelEtat);
break;
case 1:
bitWrite(memoire_etat_des_8_sorties_du_port_1, sortieVisee, nouvelEtat);
break;
case 2:
bitWrite(memoire_etat_des_8_sorties_du_port_2, sortieVisee, nouvelEtat);
break;
case 3:
bitWrite(memoire_etat_des_8_sorties_du_port_3, sortieVisee, nouvelEtat);
break;
case 4:
bitWrite(memoire_etat_des_8_sorties_du_port_4, sortieVisee, nouvelEtat);
break;
default:
return;
}
// Et mise à jour des 40 sorties du 74HC595
envoyerDesDonneesAu74HC595();
}
// =====================================
// Fonction : envoyerDesDonneesAu74HC595
// =====================================
void envoyerDesDonneesAu74HC595() {
// Mise au niveau bas de la ligne de verrouillage (car ensuite, un front montant sur celle-ci induira un "transfert + verrouillage" des données en sortie)
digitalWrite(brochePourVerouillerLesDonnees, LOW);
// Envoi des données (attention à l'ordre d'envoi, suivant votre câblage !
shiftOut(brochePourLesDonnees, brochePourDecalerLesDonnees, MSBFIRST, memoire_etat_des_8_sorties_du_port_4);
shiftOut(brochePourLesDonnees, brochePourDecalerLesDonnees, MSBFIRST, memoire_etat_des_8_sorties_du_port_3);
shiftOut(brochePourLesDonnees, brochePourDecalerLesDonnees, MSBFIRST, memoire_etat_des_8_sorties_du_port_2);
shiftOut(brochePourLesDonnees, brochePourDecalerLesDonnees, MSBFIRST, memoire_etat_des_8_sorties_du_port_1);
shiftOut(brochePourLesDonnees, brochePourDecalerLesDonnees, MSBFIRST, memoire_etat_des_8_sorties_du_port_0);
// Mise au niveau haut de la ligne de verrouillage, pour générer un front montant sur cette ligne,
// et ainsi, enclencher un "transfert + verrouillage" des données en sortie du 74HC595
digitalWrite(brochePourVerouillerLesDonnees, HIGH);
}
// =================
// Boucle principale
// =================
void loop() {
// *** ANIMATION #1 ***
// Allumage de toutes les leds, les unes après les autres, de droite à gauche
for(byte indexPort = 0 ; indexPort < 5 ; indexPort++) {
for(byte indexSortie = 0 ; indexSortie <= 7 ; indexSortie++) {
expandedDigitalWrite(indexPort, indexSortie, HIGH);
delay(delaiEntreChangementsAffichage1);
}
}
// *** ANIMATION #2 ***
// Extinction de toutes les leds, les unes après les autres, de droite à gauche
for(byte indexPort = 0 ; indexPort < 5 ; indexPort++) {
for(byte indexSortie = 0 ; indexSortie <= 7 ; indexSortie++) {
expandedDigitalWrite(indexPort, indexSortie, LOW);
delay(delaiEntreChangementsAffichage1);
}
}
// *** AFFICHAGE #3 ***
// Allumage d'une led sur deux, avec la 1ère éteinte (puis la deuxième allumée, etc), en partant de la droite vers la gauche
expandedDigitalWrite(PORT_0, SORTIE_0, LOW);
expandedDigitalWrite(PORT_0, SORTIE_1, HIGH);
expandedDigitalWrite(PORT_0, SORTIE_2, LOW);
expandedDigitalWrite(PORT_0, SORTIE_3, HIGH);
expandedDigitalWrite(PORT_0, SORTIE_4, LOW);
expandedDigitalWrite(PORT_0, SORTIE_5, HIGH);
expandedDigitalWrite(PORT_0, SORTIE_6, LOW);
expandedDigitalWrite(PORT_0, SORTIE_7, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_0, LOW);
expandedDigitalWrite(PORT_1, SORTIE_1, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_2, LOW);
expandedDigitalWrite(PORT_1, SORTIE_3, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_4, LOW);
expandedDigitalWrite(PORT_1, SORTIE_5, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_6, LOW);
expandedDigitalWrite(PORT_1, SORTIE_7, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_0, LOW);
expandedDigitalWrite(PORT_2, SORTIE_1, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_2, LOW);
expandedDigitalWrite(PORT_2, SORTIE_3, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_4, LOW);
expandedDigitalWrite(PORT_2, SORTIE_5, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_6, LOW);
expandedDigitalWrite(PORT_2, SORTIE_7, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_0, LOW);
expandedDigitalWrite(PORT_3, SORTIE_1, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_2, LOW);
expandedDigitalWrite(PORT_3, SORTIE_3, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_4, LOW);
expandedDigitalWrite(PORT_3, SORTIE_5, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_6, LOW);
expandedDigitalWrite(PORT_3, SORTIE_7, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_0, LOW);
expandedDigitalWrite(PORT_4, SORTIE_1, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_2, LOW);
expandedDigitalWrite(PORT_4, SORTIE_3, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_4, LOW);
expandedDigitalWrite(PORT_4, SORTIE_5, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_6, LOW);
expandedDigitalWrite(PORT_4, SORTIE_7, HIGH);
delay(delaiEntreChangementsAffichage2);
// *** AFFICHAGE #4 ***
// Allumage d'une led sur deux, avec la 1ère allumée (puis la deuxième éteinte, etc), en partant de la droite vers la gauche
expandedDigitalWrite(PORT_0, SORTIE_0, HIGH);
expandedDigitalWrite(PORT_0, SORTIE_1, LOW);
expandedDigitalWrite(PORT_0, SORTIE_2, HIGH);
expandedDigitalWrite(PORT_0, SORTIE_3, LOW);
expandedDigitalWrite(PORT_0, SORTIE_4, HIGH);
expandedDigitalWrite(PORT_0, SORTIE_5, LOW);
expandedDigitalWrite(PORT_0, SORTIE_6, HIGH);
expandedDigitalWrite(PORT_0, SORTIE_7, LOW);
expandedDigitalWrite(PORT_1, SORTIE_0, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_1, LOW);
expandedDigitalWrite(PORT_1, SORTIE_2, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_3, LOW);
expandedDigitalWrite(PORT_1, SORTIE_4, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_5, LOW);
expandedDigitalWrite(PORT_1, SORTIE_6, HIGH);
expandedDigitalWrite(PORT_1, SORTIE_7, LOW);
expandedDigitalWrite(PORT_2, SORTIE_0, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_1, LOW);
expandedDigitalWrite(PORT_2, SORTIE_2, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_3, LOW);
expandedDigitalWrite(PORT_2, SORTIE_4, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_5, LOW);
expandedDigitalWrite(PORT_2, SORTIE_6, HIGH);
expandedDigitalWrite(PORT_2, SORTIE_7, LOW);
expandedDigitalWrite(PORT_3, SORTIE_0, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_1, LOW);
expandedDigitalWrite(PORT_3, SORTIE_2, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_3, LOW);
expandedDigitalWrite(PORT_3, SORTIE_4, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_5, LOW);
expandedDigitalWrite(PORT_3, SORTIE_6, HIGH);
expandedDigitalWrite(PORT_3, SORTIE_7, LOW);
expandedDigitalWrite(PORT_4, SORTIE_0, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_1, LOW);
expandedDigitalWrite(PORT_4, SORTIE_2, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_3, LOW);
expandedDigitalWrite(PORT_4, SORTIE_4, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_5, LOW);
expandedDigitalWrite(PORT_4, SORTIE_6, HIGH);
expandedDigitalWrite(PORT_4, SORTIE_7, LOW);
delay(delaiEntreChangementsAffichage2);
// Effacement de toutes les sorties, puis on reboucle, à l'infini !
for(byte indexPort = 0 ; indexPort < 5 ; indexPort++) {
for(byte indexSortie = 0 ; indexSortie <= 7 ; indexSortie++) {
expandedDigitalWrite(indexPort, indexSortie, LOW);
}
}
delay(delaiEntreChangementsAffichage2);
}
Si tout va bien, vous devriez voir les LEDs s’animer de la manière suivante :
Et, encore une fois : dites-vous bien qu’on n’est pas limité à 5 puces 74HC595, car on aurait pu en brancher une « infinité » ! Imaginez d’ailleurs le nombre de possibilités que cela offre 😉
Mais tout n’étant pas parfait, le 74HC595 a aussi ses inconvénients. Et c’est ce que nous allons voir à présent.
Avantages et inconvénients du 74 HC 595 sur un Arduino
Comme évoqué précédemment; l’avantage indéniable du 74 HC 595 est qu’il peut être chaîné « à l’infini », et donc, permettre d’ajouter une « infinité » de sorties à un Arduino. Mais en pratique certaines limites peuvent apparaître.
En effet, du fait que le 74HC595 repose avant tout sur un registre à décalage à 8 étages, changer l’état de 1 sortie impose de renvoyer l’état des 8 sorties. Et si vous commencez à faire du chaînage, et mettez en œuvre 10 x 74HC595 en série, alors changer l’état de 1 sortie nécessitera de renvoyer l’état de 80 sorties ! En clair, la fréquence de rafraîchissement des sorties sera 80 fois plus faible que la fréquence d’envoi des données de l’Arduino, au premier 74 HC 595 (qui transmettra aux suivants etc).
Cela étant dit, tout est relatif. Car la puce 74HC595N, par exemple, peut être cadencée jusqu’à près de 100 MHz, lorsqu’alimentée en 5 volts. Donc même si on augmente le nombre de sorties (chaînées ou pas), la vitesse de rafraîchissement reste super élevée !
Cela étant dit, le véritable « point faible » du 74 HC 595 est selon moi la puissance qu’il peut fournir en sortie. En effet, voici quelques caractéristiques issues du datasheet du 74HC595 :
- courant max par sortie (entrant ou sortant sur Q0, Q1, Q2, …, ou Q7) : 35 mA
- courant max pouvant traverser la puce (comprenant l’ensemble des sorties) : 70 mA
- puissance max dissipable par la puce : 0,5 W
En clair, si on considère la limite de 70 mA par puce, et qu’on souhaite tirer un courant maximal sur les 8 sorties en même temps (Q0 à Q7); alors la puce ne pourra pas délivrer 35 mA par sortie, mais 70mA/8 soit 8 mA environ par sortie ! Ce qui n’est pas du tout pareil ! Et si vous ne faites pas attention, vous risquez d’échauffer la puce, jusqu’à son endommagement et sa destruction.
Après, tout dépend de l’usage que vous comptez faire de vos sorties ! Comme toujours 😉
Enfin, une chose qui peut être perçue comme un inconvénient, selon vos besoins : on ne peut mettre en œuvre que des sorties ici, et donc, aucune entrée (contrairement au module PCF8574 que je vous avais présenté il y a quelque temps, qui lui, permettait au moins d’ajouter des « pseudo » entrées !).
Circuits intégrés de la famille 74xx595 (L, S, LS, ALS, HC, HCT, …)
Ah oui … une chose que j’aimerai rajouter ici !
Dans cet article, je vous parle du 74 HC 595 ; mais sachez qu’on peut trouver des modèles compatibles, avec des références « approchantes » (avec quelques chiffres ou lettres différentes, j’entends).
Par exemple, tout d’abord, il faut savoir que le préfixe « 74 » désigne une famille de circuits intégrés à usage civil. Mais vous pouvez parfaitement les remplacer par des modèles « 54 », qui eux sont à usage militaire. Car pour ainsi dire, si je simplifie les choses, seule la plage de température d’utilisation diffère entre ces deux modèles (allant de -55°C à +125°C pour les modèles militaires « 54 », au lieu de -40°C à +80°C « seulement » pour les modèles civils « 74 »).
Ensuite, concernant les lettres « HC », vous pourriez là aussi opter pour des lettres différentes, désignant une catégorie différente, mais compatible. Voici d’ailleurs quelques exemples, avec leurs caractéristiques (purement indicatives, attention) :
- aucune lettre (74595 par exemple) désigne la technologie TTL standard (à transistors bipolaires)
- tension d’alim : 5V ±5%
- fréquence max : 25 MHz
- conso : 10 mW
- modèle « L » (74L595 par ex) désigne la technologie TTL « low power » (à faible consommation)
- tension d’alim : 5V ±5%
- fréquence max : 5 MHz
- conso : 1 mW
- modèle « S » (74S595 par ex) désigne la technologie TTL « schottky » (rapide)
- tension d’alim : 5V ±5%
- fréquence max : 75 MHz
- conso : 20 mW
- modèle « LS » (74LS595 par ex) désigne la technologie TTL « low schottky » (combinaison des technologies L et S)
- tension d’alim : 5V ±5%
- fréquence max : 30 MHz
- conso : 2 mW
- modèle « HC » (74HC595 par ex) désigne la technologie CMOS (très économe, et « large » plage d’alimentation possible)
- tension d’alim : 2V à 6V
- fréquence max : 50 MHz
- conso : 0,1 mW
- modèle « HCT » (74HCT595 par ex) désigne la technologie CMOS « compatible TTL »
- tension d’alim : 5V ±10%
- fréquence max : 50 MHz
- conso : 0,1 mW
Et je vous passe toutes les autres familles qu’on peut également trouver (AS, AT, ACT, LV, LVC, LVT, ALVC, … !). Mais selon moi, les modèles qu’on retrouve le plus souvent encore dans le commerce sont les modèles LS et HC.
Du reste, perso, la famille « HC » a ma préférence car les circuits intégrés de cette famille supportent une large plage de tension d’alim (contrairement aux modèles TTL, qui nécessitent du 5V « strict »), et ne consomment vraiment pas grand-chose (moins de pertes d’énergie, donc !).
74HC595 Arduino : conclusion !
Voilà ! Nous voici au terme de cet article sur l’utilisation des 74HC595 avec Arduino, pour rajouter des sorties numériques facilement. J’espère que cela vous aura plu, et vous donnera peut-être des idées pour vos prochains montages 🙂
Sur ce, à très bientôt !
Jérôme.
À découvrir aussi : le module PCF 8574 pour ajouter des entrées/sorties à son Arduino
(*) Mis à jour le 12/07/2024
Très intéressant comme d’habitude. Merci.
A « l’image » du précédent commentaire ! Encore un sujet intéressant et bien « ficelé » !!!
Encore bravo pour ce tuto.
Merci pour tous vos retours, ça fait plaisir ! Encore merci 🙂
Bravo !
C’est magnifiquement présenté 👍
As-tu une idée du temps de temps de transfert total pour l’exemple à 5 circuits ?
Excellente question !
Alors … étant donné que la fonction de transfert (shiftOut) utilise en interne de simples digitalWrite (ce qui est assez « lent »), je pense que la cadence d’envoi est autour des 100 à 150 kHz.
Avec 5 circuits, soit 5×8=40 données à transférer, et 1/100kHz de cadencement soit 0.01 ms, ça nous donnerait un temps de transfert théorique de 0.01*40 = 0,4 ms.
Avec les contrôles supplémentaires et tout, on pourrait certainement tabler sur un temps de transfert d’environ 1 milliseconde, en pratique.
À vérifier tout de même, pour voir si je ne dis pas de bêtises 😉
Jérôme.
C’est bien pour actionner des barres graphes, mais ce n’est pas pratique, ni fait pour ajouter des I/O. Le MCP23008 ou MCP23017 permettent quant à eux de rajouter des I/O paramétrables, de plus ces pavés ont des interruptions qui facilitent l’exploitation de ces I/O. Voir les dataSheet : https://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf et https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
Afin de filtrer au maximum les messages de type "spam" ou "inappropriés", chaque commentaire est soumis à modération, et validé manuellement. Du coup, il se peut que certains commentaires ne soient pas publiés, ou sinon, avec un peu de retard. Par ailleurs, j'ai malheureusement plus de messages à traiter que de temps pour y répondre ; c'est pourquoi je ne pourrais pas répondre à tout le monde. Désolé …