Le registre à décalage SIPO

Le registre à décalage SIPO

Vous vous souvenez, nous avions vu le registre à décalage PISO entrées parallèles et sortie série pour augmenter le nombre d’entrées de l’Arduino (voir ici). Maintenant nous allons voir son demi-frère le registre à décalage SIPO entrée série => sorties parallèles (ou en anglais shift register Serial In Parallel Out) qui lui est un petit circuit intégré très pratique pour augmenter le nombre de sorties d’un Arduino.

 

A quoi ça sert?

Imaginons un tableau de bord rempli de voyants (un cockpit d’avion?) et trop de Leds pour les sorties de l’Arduino. Il existe plusieurs façons de connecter beaucoup de Leds à un Arduino sans utiliser trop de fils (et donc de sorties).

Il y a entre autre la matrice. Pour une matrice, il faut gérer plusieurs ligne et plusieurs colonnes, les activer et les désactiver pour allumer les Leds (les Leds sont allumées très vite une a une et la persistance rétinienne donne l’impression que c’est bien allumé). Cependant la matrice a 2 inconvénients. Le premier c’est que le programme ne doit pas s’arrêter ni faire de pause sinon les Leds s’éteignent (très bon exercice pour faire du code non bloquant). Et le deuxième c’est qu’il faut quand même un certain nombre de sorties, pour une matrice 4×4 de 16 Leds il faut 8 fils donc 8 sorties. (Peut être ferais-je un article sur les matrice, à suivre).

Il y a aussi l’utilisation d’un multiplexeur (une entrée, plusieurs sorties) qui permet de transférer la valeur de l’entrée sur une de ses sorties, sélectionnée par commande.

Et nous avons aussi la possibilité d’utiliser un registre à décalage et c’est le sujet de cet article !

 

Comment ça marche?

Comme son nom l’indique, ce circuit possède une entrée série et plusieurs sorties parallèles.  Bien pratique pour avoir plusieurs sorties séparées, les alimenter avec un octet et les activer au bon moment, tout ça avec 3 pins de l’Arduino. Le principe est simple, le registre est verrouillé (pour éviter les problèmes pendant le changement de valeur), ensuite les valeurs sont présentées une à une au registre qui les poussent devant la bonne porte (grâce à l’horloge) puis les sorties sont validées : les états des pins de sortie prennent physiquement les valeur attribuées.

L’enchainement des étapes pour envoyer un octet

Étape 1 : verrouiller le registre

Étape 2 : écrire 8 fois la valeur sur l’entrée série du registre en envoyant à chaque écriture un coup d’horloge pour faire décaler les données. Ceci permet d’écrire la 8ème valeur de l’octet puis de la 7ème et ainsi de suite.

Étape 3 : déverrouiller le registre pour que les valeurs se retrouvent sur les pins de sortie

 

La mécanique interne d’un registre SIPO:

le partie pris dans cet exemple est de commencer par les LSB (Less Significant Bits), les bit les moins significatifs, donc ceux de droite. On prend les bits de l’octet de droite à gauche.

Verrouillage du registre.

Premier cycle d’horloge, mise à LOW de l’horloge (CLOCK), sélection du premier bit à droite (1 en rouge), mise à l’état (en fonction du bit) de la sortie de l’Arduino reliée à Ds puis envoi du top d’horloge (mise à HIGH sur CLOCK), on voit que le bit (ici 1) est bien rentré dans le registre (Q0).

Les bits du registre sont en gris car les sorties ne sont pas encore validées, c’est bien la phase de transfert.

Deuxième cycle d’horloge, mise à LOW de l’horloge (CLOCK), sélection du deuxième bit à droite (0 en rouge) , mise à l’état (en fonction du bit) de la sortie de l’Arduino reliée à Ds puis envoi du top d’horloge(mise à HIGH sur CLOCK), on voit que le bit (ici 0) est bien rentré dans le registre (Q0) et que le premier bit entré (1) a bien été décalé (Q1).

Il suffit de répéter ces opérations jusqu’à avoir transférer les 8 bits, déverrouiller le registre et les sorties prendront la valeur de l’octet initial.

Des registres à décalage, il en existe quelques uns avec le même principe de base mais avec quelques fonctionnalités différentes. Nous allons voir le 74HC595 (mais vous auriez pu tout a fait en prendre un autre, attention les câblages sont en général différents).

 

Comment brancher un 74HC595?

Le 74HC595 se présente sous la forme d’un composant à 16 broches en boitier DIP (traversant, en photo) ou en version CMS (composant de surface).

Voici la correspondance des pins.

/*-- pin out du 74HC595
         1___o___16    
PO-Q1 <-  |     | <- Vcc (+5v)
PO-Q2 <-  |  C  | -> PO-Q0
PO-Q3 <-  |  D  | <- DS entrée série
PO-Q4 <-  |  4  | <- OE Output Enable (active LOW) maintenir à GND
PO-Q5 <-  |  0  | <- ST-CP verrouillage des sorties (le temps de décaler le registre)
PO-Q6 <-  |  2  | <- SH-CP entrée de l'horloge de décalage
PO-Q7 <-  |  1  | <- MR Master Reset (active LOW) maintenir à +Vcc
  GND ->  |_____| -> Q7'
         8       9
PO-Q0 à Q7 sont les sorties parallèles
Q7' est l'équivalent de Q7 pour connecter à un autre registre
DS est l'entrée série 
*/

L’alimentation se fait avec les pin 8 GND et pin 16 Vcc (+5v).

les pins de contrôles sont :

  • la pin 12 ST-CP LOCK qui permet de verrouiller les sorties le temps de charger des nouvelles valeurs
  • la pin 11 SH-CP CLOCK, chaque front montant décale les valeurs dans le registre interne de l’entrée série vers Q7 (en passant par Q0, Q1 etc)

les pins de sortie sont les pins Q0 à Q7 et la pin de entrée série est la DS (la pin 14).

 

Comment utiliser le 74HC595 avec une carte Arduino?

Le but sur l’Arduino est de gérer un octet (8 bits) qui représente l’état souhaité des sorties du 74HC595. Lorsque l’octet prêt, il faut le “pousser” dans le 74HC595.

Une fois le verrouillage effectué, la première valeur, (celle de la sortie de l’Arduino reliée à l’entrée série DS, soit la pin 14) doit être enregistrée en première position grâce à un coup d’horlge. Puis ensuite il faut mettre sur la sortie de l’Arduino, la valeur du prochain bit à transférer et envoyer un coup d’horloge. Et ainsi de suite pour pousser les bits restants. Enfin ne pas oublier de déverrouiller le registre pour que les sorties prennent bien en compte les nouvelles valeurs.

Attention à la façon dont vous gérer les bits LSB (Less Significant Bit) ou MSB (Most Significant Bit), l’exemple de code permet d’utiliser un sens de lecture de l’octet le MSB en premier ou le LSB en premier.

Vous trouverez 2 exemples de code ci-dessous, le premier est un chenillard simple et le deuxième permet d’afficher un nombre en binaire :

Le montage

Schéma de montage et test fait sur Tinkercad.

 

Voici un exemple de code de chenillard.

/*-- pin out du 74HC595
         1___o___16    
PO-Q1 <-  |     | <- Vcc (+5v)
PO-Q2 <-  |  C  | -> PO-Q0
PO-Q3 <-  |  D  | <- DS entrée série
PO-Q4 <-  |  4  | <- OE Output Enable (active LOW) maintenir à GND
PO-Q5 <-  |  0  | <- ST-CP verrouillage des sorties (le temps de décaler le registre)
PO-Q6 <-  |  2  | <- SH-CP entrée de l'horloge de décalage
PO-Q7 <-  |  1  | <- MR Master Reset (active LOW) maintenir à +Vcc
  GND ->  |_____| -> Q7'
         8       9
PO-Q0 à Q7 sont les sorties parallèles
Q7' est l'équivalent de Q7 pour connecter à un autre registre
DS est l'entrée série 
*/

//Broche connectée au ST_CP du 74HC595
#define LOCK 12 
//Broche connectée au SH_CP du 74HC595
#define CLOCK 13 
//Broche connectée au DS du 74HC595
#define DATA 11 

void setup()
{
    //On met les broches en sortie
    pinMode(LOCK, OUTPUT);
    pinMode(CLOCK, OUTPUT);
    pinMode(DATA, OUTPUT);
  	Serial.begin(9600);
}

void loop()
{
    // un chenillard, c'est une led allumée à la fois et qui se déplace
  	// il faut allumer la première, puis l'éteindre puis la deuxième etc.
  	// l'octet représentent les 8 leds, il faut donc avoir 
  	// 0000 0001
  	// 0000 0010
  	// 0000 0100 et ainsi de suite
  	// on remarque que les valeurs décimales de l'octet sont 1 puis 2 puis 4
  	// pratique, il suffit de faire x2 pour décaler vers la gauche
  	
  	// départ i=1 pour la première led et x 2 à chaque boucle
  	// lorsque i aura atteint 128, le dernier x2 donnera 256 soit 1 0000 0000
  	// on voit que le 1 ne fait plus parti de l'octet donc i = 0, d'ou la condition de sortie
  	// et ensuite on reprend
    for (unsigned char i = 1; i>0; i=i*2)
    {
      Serial.println(i);  
      //On active le verrou le temps de transférer les données
        digitalWrite(LOCK, LOW);
        //on envoi toutes les données grâce à notre belle fonction
        envoi_ordre(DATA, CLOCK, 1, i);
        //et enfin on relâche le verrou
        digitalWrite(LOCK, HIGH);
        //une petite pause pour constater l'affichage 
        delay(250);
    }
}

// cette fonction permet de "pousser" un octet dans le registre à décalage
// la fonction met un front bas sur l'horloge, présente la valeur au registre
// (soit la valeur du bit de l'octet que la fonction est en train de lire
// puis met un front haut pour valider la valeur
void envoi_ordre(int dataPin, int clockPin, boolean sens, char donnee)
{
    //on va parcourir chaque bit de l'octet
    for(int i=0; i<8; i++)
    {
        //on met l'horloge à l'état bas
        digitalWrite(clockPin, LOW);
        //on met le bit de donnée courante en place
        if(sens)
        {
            digitalWrite(dataPin, donnee & 0x01 << i);
        }
        else
        {
            digitalWrite(dataPin, donnee & 0x80 >> i);
        }
        //enfin on remet l'horloge à l'état haut pour faire prendre en compte cette dernière
        digitalWrite(clockPin, HIGH);
    }
}

Voici un exemple de code qui permet d’afficher un nombre de 0 à 255 en binaire à l’aide de 8 Leds et d’un 74HC595.

/*-- pin out du 74HC595
         1___o___16    
PO-Q1 <-  |     | <- Vcc (+5v)
PO-Q2 <-  |  C  | -> PO-Q0
PO-Q3 <-  |  D  | <- DS entrée série
PO-Q4 <-  |  4  | <- OE Output Enable (active LOW) maintenir à GND
PO-Q5 <-  |  0  | <- ST-CP verrouillage des sorties (le temps de décaler le registre)
PO-Q6 <-  |  2  | <- SH-CP entrée de l'horloge de décalage
PO-Q7 <-  |  1  | <- MR Master Reset (active LOW) maintenir à +Vcc
  GND ->  |_____| -> Q7'
         8       9
PO-Q0 à Q7 sont les sorties parallèles
Q7' est l'équivalent de Q7 pour connecter à un autre registre
DS est l'entrée série 
*/

//Broche connectée au ST_CP du 74HC595
#define LOCK 12 
//Broche connectée au SH_CP du 74HC595
#define CLOCK 13 
//Broche connectée au DS du 74HC595
#define DATA 11 

void setup()
{
    //On met les broches en sortie
    pinMode(LOCK, OUTPUT);
    pinMode(CLOCK, OUTPUT);
    pinMode(DATA, OUTPUT);
}

void loop()
{
    // on affiche les nombres de 0 à 255 en binaire
  	// on met dans i un entier de 0 à 255 soit de  00000000 à 11111111
  	// exemple
  	// le 001 c'est 00000001
  	// le 002 c'est 00000010
  	// le 003 c'est 00000011
  	// le 004 c'est 00000100
  	// le 064 c'est 01000000
  	// le but est d'allumer les leds lorsqu'il y a un 1
  
    for (char i = 0; i<256; i++)
    {
        //On active le verrou le temps de transférer les données
        digitalWrite(LOCK, LOW);
        //on envoi toutes les données grâce à notre belle fonction
        envoi_ordre(DATA, CLOCK, 1, i);
        //et enfin on relâche le verrou
        digitalWrite(LOCK, HIGH);
        //une petite pause pour constater l'affichage 
        delay(500);
    }
}

// cette fonction permet de "pousser" un octet dans le registre à décalage
// la fonction met un front bas sur l'horloge, présente la valeur au registre
// (soit la valeur du bit de l'octet que la fonction est en train de lire
// puis met un front haut pour valider la valeur
void envoi_ordre(int dataPin, int clockPin, boolean sens, char donnee)
{
    //on va parcourir chaque bit de l'octet
    for(int i=0; i<8; i++)
    {
        //on met l'horloge à l'état bas
        digitalWrite(clockPin, LOW);
        //on met le bit de donnée courante en place
        if(sens)
        {
            digitalWrite(dataPin, donnee & 0x01 << i);
        }
        else
        {
            digitalWrite(dataPin, donnee & 0x80 >> i);
        }
        //enfin on remet l'horloge à l'état haut pour faire prendre en compte cette dernière
        digitalWrite(clockPin, HIGH);
    }
}

 

Utiliser plusieurs 74HC595

Le principe est assez simple, comme il s’agit d’une liaison série avec un décalage, il suffit de mettre les 74HC595 en série. Pour se faire on raccorde la sortie Q7 (ou plutôt Q7′ qui est la pour ça) du premier registre sur l’entrée série DS (pin 14) du deuxième. Il faut également relier les LOCK et CLOCK de chacun des éléments. Au bout du 8ème top d’horloge, les valeurs vont commencer à être charger dans le deuxième registre.

Ce montages série est très intéressant car il permet toujours de n’utiliser que 3 pins de la carte Arduino quelque soit le nombre de registres en série. En revanche, plus il y a de registres en série plus il faudra de temps pour charger les données qui devront traverser tous les registres. Le temps est proportionnel au nombre de coup d’horloge nécessaire qui lui est proportionnel au nombre de 74HC595 mis dans le circuit.

Vous trouverez des informations complémentaires sur le site Arduino (voir lien en fin d’article).

 

Note

Il existe aussi plus simplement la fonction shiftOut d’Arduino 🙂

https://www.arduino.cc/en/Tutorial/ShiftOut

Un article traitant du sujet (merci à lui) : https://www.zem.fr/decouverte-du-composant-74hc595-8-bit-shift-register/

Posted in arduino, Projet terminé, Technique and tagged , , , , , .

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *