8. Augmentons les possibilités de l'Arduino!


Comme vous avez pu le remarquer depuis les premiers articles de ce dossier et aussi depuis que vous vous êtes mis à bricoler autour de l'Arduino, la première limitation à laquelle nous faisons face est la puissance disponible pour chaque entrée/sortie. En effet, nous ne pouvons pas utiliser plus de 20mA à 40mA par port. Or, avoir plusieurs LEDs par sortie ou même plusieurs mètres de bandeaux de LEDs n'est pas chose rare...

La deuxième limitation tout aussi ennuyante est le nombre d'entrée/sortie. Une douzaine de sorties numériques dont seulement 6 en mode PWM peut rapidement devenir un inconvénient. Compte-tenu de son faible prix, il est possible d'ajouter un second Arduino pour diviser le projet en deux mais ce n'est pas une solution très "élégante"...

Dans les paragraphes suivants, nous allons voir comment pallier à ces deux problèmes. Nous introduirons la notion de contrôle de puissance avec le transistor TIP122 (si vous vous souvenez bien, j'en ai déjà parlé dans un article précédent). Ensuite, nous découvrirons l'utilisation d'autres circuit-intégrés qui nous aideront à augmenter le nombre d'entrée/sorties de l'Arduino.

Certes, je m'éloigne un peu de mon objectif premier qui était l'utilisation minimale de composants électroniques supplémentaires. Cependant, je ne vais pas non plus lister un nombre important de composants. Seuls quelques incontournables seront présentés afin de répondre aux besoins de notre hobby. Toujours dans le même état d'esprit, je me limiterai aux explications essentielles et nécessaires pour ne pas alourdir cette présentation...



8.1. L'ami de l'électronicien: le breadboard ou la "planche à pain"

Non, je ne vous ferai pas un cours de cuisine sur l'utilisation sécuritaire d'un couteau à pain... mais plutôt une présentation rapide de cet accessoire bien utile. Je l'introduis car les schémas et les montages qui sont présentés par la suite sont un peu plus complexes que les exemples élémentaires que j'ai pu présenter dans les chapitres précédents. Celui-ci devient donc indispensable...

Il permet de construire des prototypes de circuits électroniques sans avoir à souder quelques composants que ce soit. Ceux-ci sont "insérer" dans des trous, faisant ainsi contact avec les trous voisins. Cela crée alors un circuit ou une liaison entre les composants à la manière d'une piste cuivrée sur un PCB.

Il s'agit d'une plaque de plastique, munie de trous espacés de 1/10" soit 2.54mm. Cet écartement est l'un des standards utilisé pour fabriquer les circuits intégrés. Par exemple, les pattes de l'Atmega328P constituant un Arduino UNO sont espacées de 1/10". Il serait tout à fait possible d'utiliser le processeur directement sur une telle planche et reconstituer un Arduino UNO avec tous les composants nécessaires. Mais je m'égare...

Voici le schéma d'un breadboard:



Plusieurs caractéristiques importantes sont à noter:
  • tout d'abord les lignes d'alimentation. Tous les trous identifiés par le + sont connectés entre eux (ligne jaune sur le schéma). De la même façon pour la ligne -. Les deux groupes en haut et en bas de la planche sont indépendants l'un de l'autre. Si on alimente la planche avec une source électrique sur le groupe du haut, alors il ne faut pas oublier d'ajouter deux fils qui relieront les deux groupes si on veut alimenter également les lignes du bas...
  • ensuite, les colonnes de connexions: les 5 trous d'une colonne sont reliés. Chaque colonne est indépendante de ses voisines.
  • l'espace (horizontalement) au centre de la plaque est de 3/10", ce qui correspond à l'espacement des broches d'un circuit intégré comme par exemple le Atmega328P.

Je vous conseille fortement l'achat d'un tel breadboard. Il est très utile lorsqu'on désire créer ou tester un montage. Une fois le montage validé, alors on peut passer à la production d'un PCB au besoin.




8.2. Besoin de plus de puissance? Le transistor...

Sur un Arduino, une sortie ne supporte qu'un maximum de 40mA sous 5V. Si on désire contrôler un bandeau de LEDs de 1A en 12V, l'Arduino ne le permet pas seul. Il faut alors alimenter le bandeau avec une source extérieure et contrôler celle-ci à l'aide de l'Arduino. Pour cela, on utilisera un transistor de puissance.


8.2.1. Le transistor Darlington TIP122

Plusieurs types de transistors sont disponibles, avec des caractéristiques adaptées aux besoins de chacun. Le TIP122 supporte un maximum de 5A sous 100V et est donc très polyvalent lorsque une source de puissance extérieure est nécessaire... Sa vitesse de basculement est très rapide et permet une utilisation avec un port PWM. Un transistor agit dans ce cas-là comme un robinet qui laisserait passer un courant entre le collecteur et l'émetteur (identifiés sur le schéma). La base est assimilée au volant du robinet. Voici le montage:



On remarquera:
  • la résistance de 1k sur la base du transistor
  • l'alimentation externe 12V ne sert qu'à fournir la puissance nécessaire pour illuminer le bandeau de LEDs
  • la base du transistor est connectée à une sortie PWM de l'Arduino
  • les pôles - des deux sources de courant sont connectés! (le pôle - de l'alimentation externe et le port GND de l'Arduino)

La logique est très simple: il suffit de faire varier la valeur du port de sortie 10 pour modifier l'illumination des LEDs. Ce programme ne devrait comporter qu'un seul appel à la fonction analogWrite( 10, valeur ). Cependant, maintenant que vous savez utiliser une entrée analogique et un potentiomètre, il devrait être facile d'ajouter du code afin de lire la valeur sur le port A0 et l'envoyer à la variable valeur... Ainsi, vous pourrez modifier manuellement l'illumination!

Vous devriez obtenir quelque chose d'aussi simple que cela:

#define LEDpin 10
#define POTpin A0

void setup()
{
  pinMode( LEDpin, OUTPUT );
  pinMode( POTpin, INPUT );
}

void loop()
{
  analogWrite( POTpin, analogRead( POTpin ) / 4 );
}


8.2.2. Le transistor MOSFET IRF531

Le transistor TIP122 est de type Darlington. Celui que je présente maintenant est un IRF531 de type MOSFET. Sans entrer dans les détails, le Darlington est une combinaison de deux transistors bipolaires. Le gain en courant est le prdouit des deux gains qui le composent. Le second (MOSFET) est un transistor à effet de champ. Je ne vous en parlerai pas plus, car je ne suis pas très familier pour l'instant avec les principes qui les gouvernent.

Par contre, il faut savoir, et c'est pourquoi j'en parle maintenant, que le MOSFET est en général plus rapide et dégage beaucoup moins de chaleur que le Darlington... Dans le contexte du pilotage de bandes de LEDs de plusieurs ampères, il est préférable d'utiliser le MOSFET.

Le IRF531 supporte 80V 14A ce qui est bien suffisant pour le montage "jour-nuit" par exemple. De plus, il s'agit d'un N-channel qui s'utilise comme un NPN. Donc le montage est identique à l'exemple précédent... (L'autre type est le P-channel qui s'apparente au PNP) On remarquera que le nom des pattes est différent: la "base" est remplacé par la "grille", le "collecteur" supportant la charge est le "drain" et enfin l'"émetteur" connecté à la masse devient la "source".


8.2.3. Plusieurs transistors? Le circuit intégré ULN2003 est fait pour vous

Lorsque le courant et la tension nécessaires sont moins importants, le circuit ULN2003 est très pratique puisqu'il comporte 7 transistors intégrés. Chaque sortie peut supporter jusqu'à 500mA sous 30V (il existe aussi le ULN2803 avec 8 transistors). Il ne s'agit donc pas de 7 ou 8 fois un TIP122, mais ils permettent déjà de supporter plus de courant qu'une simple sortie de l'Arduino...

Remarque importante selon ma compréhension: Le maximum par sortie est de 500mA, mais cela ne signifie pas qu'il soit possible de drainer 500mA par sortie en même temps! Considérons la puissance maximale supportée par le chip: 0.76W. Sachant que la chute de tension par canal est de 1.5V, cela correspond à un courant total de 0.76W / 1.5V =~ 500mA... Donc, si on veut utiliser plusieurs sorties en même temps, il faut faire le calcul de la dissipation totale de chaleur, soit pour deux sorties, environ 250mA, pour 8 sorties, environ 60mA, etc...

Ici aussi, il est possible de trouver d'autres C.I. incluant d'autres caractéristiques. Cependant, pour éviter une recherche fastidieuse dans les datasheets ou tableaux de caractéristiques fournis par les fabricants, je devrais être en mesure de faire, dans ce dossier, un petit tour d'horizon des composants les plus couramment utilisés. Ils constitueront une trousse à outils de départ, suffisante pour les montages et les problématiques liés à notre hobby. Ceci-dit, cette liste n'est pas exhaustive, et vous aurez l'occasion de découvrir bons nombres de composants supplémentaires au fur-et-à-mesure des montages que vous réaliserez autour de l'Arduino.

Mais revenons à nos moutons... Voici un montage permettant de commander 3 LEDs 12V alimentées par une source externe:



Un peu à la manière du montage précédent incluant un TIP122, nous voyons ici 3 sorties de l'Arduino connectées à trois des bases du ULN2003. Un signal HIGH envoyé sur une base provoque l'ouverture du lien entre le collecteur (correspondant à la base sélectionnée) et l'émetteur commun en direction de la masse. Cela permet au courant de passer et d'illuminer la LED. Encore une fois, on peut assimiler cette action à l'ouverture d'un robinet... Le programme permettant d'actionner ce robinet est similaire au précédent. Il suffit d'appeler digitalWrite( sortie, HIGH ) sur une sortie pour illuminer la LED. A l'opposée, la valeur LOW viendra tout simplement l'éteindre.

Pour que ce chapitre soit complet, il me reste à décrire l'utilisation de chacun des ports du circuit-intégré. C'est ce genre de document qu'il faudra examiner avant l'utilisation de n'importe quel C.I.:


Photo Wikipedia

Il faut tout d'abord déterminer le "sens" de lecture des ports. On identifiera soit un point sur le dessus du boitier soit une "demie-lune" sur l'un des petits côtés du C.I.. A partir de là, on oriente le boitier en le plaçant verticalement, cette marque en haut. Les numéros de ports commencent par... 1, et on tourne dans le sens anti-horaire.

Dans le cas présent:
  • les ports 1 à 7 représentent les bases B1 à B7
  • les ports 10 à 16 représentent les collecteurs C7 à C1
  • le port 8 représente l'émetteur commun E, qui est normalement le GND ou pôle - de la source de courant
  • et enfin, le port 9 ou COM, doit être connecté au + de la source de courant dans le cas d'une charge importante comme un moteur ou une bobine. Si ce port n'est pas connecté dans le cas de petites charges (une LED par exemple) cela ne devrait pas poser de problème. Dans le doute, vous pouvez le connecter.
Ainsi, dans notre montage, les trois premiers transistors sont utilisés et commandés par les ports 10, 9 et 8 de l'Arduino. Il suffit d'utiliser la commande digitalWrite( port, HIGH ) pour allumer la LED et digitalWrite( port, LOW ) pour l'éteindre.

Par exemple, un petit chenillard de 3 LEDs:

#define LEDpin1 10
#define LEDpin2 9
#define LEDpin3 8

void setup()
{
  pinMode( LEDpin1, OUTPUT );
  pinMode( LEDpin2, OUTPUT );
  pinMode( LEDpin3, OUTPUT );
}

void loop()
{
  digitalWrite( LEDpin1, HIGH );
  digitalWrite( LEDpin2, LOW );
  digitalWrite( LEDpin3, LOW );
  delay( 200 );

  digitalWrite( LEDpin1, LOW );
  digitalWrite( LEDpin2, HIGH );
  digitalWrite( LEDpin3, LOW );
  delay( 200 );

  digitalWrite( LEDpin1, LOW );
  digitalWrite( LEDpin2, LOW );
  digitalWrite( LEDpin3, HIGH );
  delay( 200 );
}




8.3. Besoin de plus de "contrôle"? Les portes logiques

Par opposition au transistor, une porte logique permet de propager... un état logique! Ces C.I. ne sont normalement pas utilisables pour fournir ou drainer du courant... Cependant, on peut trouver des montages où ces composants sont utilisés pour contrôler de simples LEDs. Dans ce cas, le courant est faible et est supportable par le C.I. mais ce n'est pas son utilisation originale. Avant d'aller plus avant dans la descritpion des C.I., commençons par une liste de termes souvent utilisés.


8.3.1. Terminologie

Les termes que j'utiliserai le plus souvent sont:
  • multiplexage/démultiplexage
  • registre à décalage

8.3.1.1. Multiplexage et démultiplexage

Commençons par le multiplexage/démultiplexage. Je vais utiliser l'analogie suivante: vous avez 8 pièces dans votre maison. Ce sont les 8 sorties d'un démultiplexeur. À l'entrée de la maison, vous pouvez installer 8 interrupteurs mutuellement exclusifs. Cela signifie que: si vous actionnez un interrupteur, la pièce correspondante est allumée alors que toutes les autres seront éteintes.

Cependant, vous pouvez éviter d'installer autant d'interrupteurs. Si vous en possédez seulement 3 au lieu de 8, alors vous pourrez agir sur l'éclairage de la même manière, à condition d'ajouter un multiplexeur... En effet, 3 interrupteurs peuvent être positionnés selon 8 combinaisons possibles. Si on définit un triplet de valeurs ON et OFF, on obtient: (OFF, OFF, ON), (OFF, ON, OFF), etc... Si on transpose les ON et OFF en bits, on obtient alors la suite de combinaisons:

000
001
010
011
100
101
110
111

Nous venons de définir les valeurs 0 à 7 en octal c'est à dire à l'aide de 3 bits. Ces bits sont les entrées de contrôle du démultiplexeur. A chacune de ces entrées correspond une combinaison d'états de sorties, définissant l'éclairage exclusivement dans une seule pièce à la fois. De manière équivalente, le multiplexage est l'action de convertir l'état des 8 entrées en une combinaison de 3 bits de sortie. Un multiplexeur permet de lire 8 ports et de déterminer lequel est allumé en ne transmettant que 3 bits... On identifie souvent dans la nomenclature ce type de combinaison: 3-8 ou 8-3. On peut trouver des 4-16, ou par exemple des groupes 4x 1-2 sur un même C.I. Cela signifie: 4 démultiplexeurs 1-2...


8.3.1.2. Le registre à décalage

Le registre à décalage est un outils permettant d'afficher 8 sorties (à la manière d'un démultiplexeur) mais sans quelles soient mutuellement exclusives! En d'autres termes, vous pouvez avoir 2 pièces allumées et 6 éteintes! L'idée est de remplir une à une les 8 sorties. La commande se fait aussi à l'aide de 3 ports de contrôle, mais employés différemment. Il s'agit de:
  • SER: un port permettant de positionner une sortie à ON ou OFF (HIGH ou LOW)
  • SRCLK: un port permettant de décaler l'état des 8 sorties
  • RCLK: un port permettant de valider l'affichage

Essayons sur un petit exemple:
  • Le registre débute avec les 8 sorties à LOW: 00000000 (de droite à gauche: sorties QA à QH).
  • On commence par positionner le port de contrôle RCLK à l'état LOW, ce qui fige l'état des sorties.
  • A chaque fois que le port de contrôle SRCLK passe de l'état LOW à l'état HIGH, l'état des sorties est décalé à gauche d'une position et la valeur de SER est écrite à droite. Par exemple, si SER est à l'état HIGH, alors on obtient: 00000001.
  • On peut ainsi combiner les appels aux contrôles SER et SRCLK pour obtenir par exemple: 01001000.
  • Une fois définis, on affiche les états sur les sorties en définissant l'état du contrôle RCLK à HIGH.

Vous aurez remarqué que cela demande plus d'instructions que le contrôle d'un démultiplexeur. Cependant, il est possible d'afficher plusieurs sorties à l'état HIGH, mais aussi on peut chaîner plusieurs C.I. de façon à contrôler un multiple de 8 sorties avec seulement 3 ports!


8.3.2. Remplissons notre boite à outils

Les premiers C.I. que nous allons ajouter à notre boite à outils sont:
  • 74HC138: démultiplexeur 3-8
  • 74HC148: multiplexeur 8-3
  • 74HC595: registre à décalage
  • n'oublions pas le ULN2003/2803 utilisé plus haut, même s'il ne s'agit pas de portes logiques...
  • TPIC6B595: le meilleur des deux mondes?
Nous allons commencer par décrire l'utilisation du démultiplexeur et du registre à décalage. Ils nous permettront d'augmenter le nombre de sortie de l'Arduino à moindre frais, ce qui correspondait au deuxieme objectif de ce chapitre...


8.3.2.1. Le démultiplexeur 74HC138

Courant maximum entre Vcc et GND: 50mA
Donc: 50mA / 8 sorties = environ 6mA par sortie
Il est donc nécessaire d'adjoindre un transistor de puissance afin de contrôler une led (environ 20mA) par sortie...

Voici la description des ports:

Photo www.wvshare.com

  • les ports A0 à A2 devront être connectés à 3 ports de sortie de l'Arduino. Cette combinaison contrôlera la porte/sortie du démultiplexeur qui sera à l'état HIGH
  • les ports Y0 à Y7 sont les sorties du démultiplexeur
  • les ports E0 à E2 sont utilisés pour le chaînage de plusieurs multiplexeurs
  • enfin, les ports d'alimentation VCC et GND
Un câblage théorique pourrait donner cela, mais compte-tenu des caractéristiques énumérées plus haut, il sera nécessaire de lui adjoindre un transistor par sortie afin d'alimenter correctement une led par exemple.


On assumant que nous voulions éclairer alternativement les LEDs connectées aux ports Y2 et Y7 du démultiplexeur, nous écririons le programme suivant:

#define DEMUX_A0 10
#define DEMUX_A1 9
#define DEMUX_A2 8

void setup()
{
  pinMode( DEMUX_A0, OUTPUT );
  pinMode( DEMUX_A1, OUTPUT );
  pinMode( DEMUX_A2, OUTPUT );
}

void loop()
{
  // Allumage de la LED sur le port Y2.
  digitalWrite( DEMUX_A0, LOW );
  digitalWrite( DEMUX_A1, HIGH );
  digitalWrite( DEMUX_A2, LOW );
  delay( 1000 );
  // Allumage de la LED sur le port Y7.
  digitalWrite( DEMUX_A0, HIGH );
  digitalWrite( DEMUX_A1, HIGH );
  digitalWrite( DEMUX_A2, HIGH );
  delay( 1000 );
}

Exercice: remplissez la table de vérité des 8 états possibles et allumez tour-à-tour les 8 LEDs dans l'ordre. Vous pourrez ainsi vous familiariser avec le concept de démuliplexage.

Une remarque concernant ce montage: en tout temps le courant qui transite au travers d'une seule porte et au travers du C.I. en général est de l'ordre de 15 à 20mA. Cela ne devrait pas endommager le composant. Cependant, il serait préférable d'utiliser un ULN2803 afin d'alimenter plusieurs LEDs en même temps. Cela devrait donner un montage comme celui-ci:



Voilà pour la description du démultiplexeur. Comme vous avez pu le voir, nous venons de commander 8 LEDs à partir de 3 sorties numériques. Ce qui fait un gain de 5 avec un composant de moins de 1$! Cependant, "ces sorties supplémentaires ne peuvent être utilisées qu'en mode exclusif" me direz vous, ce qui n'en font pas des sorties à part entière... Sur ce point, je suis d'accord... C'est pourquoi nous allons voir dans le chapitre suivant l'utilisation du registre à décalage qui, lui, augmente "réellement" le nombre de sorties!


8.3.2.2. Le registre à décalage 74HC595

8.3.2.2.1. Description

Courant maximum entre Vcc et GND: 70mA
Donc: 70mA / 8 sorties = environ 8mA par sortie
Il est donc nécessaire d'adjoindre un transistor de puissance afin de contrôler une led (environ 20mA) par sortie...

Photo www.wvshare.com

Après avoir donné une brève description de ce composant quelques lignes plus haut, voyons ce qu'il en est à l'aide d'un exemple. Pour commencer, le montage. Il s'apparente beaucoup au précédent puisque seul le C.I. a changé. J'ai positionné le C.I. "tête en bas" de manière à pouvoir accéder facilement aux ports de contrôle et orienter toutes les sorties vers le haut... Les connexions aux ports sont elles-aussi différentes. Les ports de sortie de l'Arduino 8, 10 et 11 sont connectés respectivement aux ports SER (14), RCLK (12) et SRCLK (11) du 74HC595. Les ports OE (13) et SRCLR (10) sont respectivement à l'état LOW et HIGH. Nous n'aborderons pas nécessairement le rôle de ces ports pour l'instant. Il suffit juste de les brancher comme précisé.



Maintenant, le programme. Nous allons commencer par allumer la LED 1:

#define SERpin 8
#define RCLKpin 10
#define SRCLKpin 11

void setup()
{
  pinMode( SERpin, OUTPUT );
  pinMode( RCLKpin, OUTPUT );
  pinMode( SRCLKpin, OUTPUT );
}

void loop()
{
  // On commence par positionner le port d'affichage à LOW
  digitalWrite( RCLKpin, LOW );

  // Pour chaque valeur de registre on positionne le port de décalage à LOW
  digitalWrite( SRCLKpin, LOW );
  // On définit la valeur à positionner sur la sortie 1 (QA)
  // Ici on veut l'allumer
  digitalWrite( SERpin, HIGH );
  // Une fois la nouvelle valeur définie, on décale les registres et on insère la nouvelle valeur
  digitalWrite( SRCLKpin, HIGH );

  // Il ne reste plus qu'à afficher les sorties
  digitalWrite( RCLKpin, HIGH );
}

Ces quelques lignes ont pour effet d'allumer la LED 1 alors que les autres LEDs restent éteintes. Examinez bien la séquence d'instructions: les trois instructions du milieu sont encadrées par deux appels à digitalWrite() sur le port RCLKpin tantôt avec la valeur LOW tantôt avec la valeur HIGH. C'est à ce moment-là que le résultat est effectif et visible sur les LEDs, à la fin de la fonction loop().

Les trois instructions centrales définissent la nouvelle valeur ajoutée par la commande digitalWrite() sur le port SERpin avec la valeur de l'état voulu, ici "allumé" ou HIGH. Cette instruction est encadrée par deux appels sur le port SRCLK tantôt avec la valeur LOW tantôt avec la valeur HIGH.

Dans un second exemple, nous allons allumer la LED 3 uniquement (sortie QC). Pour cela, il suffit d'amener la valeur HIGH que nous venons de positionner sur la sortie QA, en direction de la sortie QC. Cela implique donc deux nouveaux décalages vers la gauche. Mais comme à chaque décalage, une nouvelle valeur est introduite à la première position, il ne faut pas oublier de définir cette nouvelle valeur à LOW si nous ne voulons pas allumer d'autres LEDs...

Ainsi la fonction loop() devient:

void loop()
{
  // On commence par positionner le port d'affichage à LOW
  digitalWrite( RCLKpin, LOW );

  // Première étape, on ajoute la première valeur.
  digitalWrite( SRCLKpin, LOW );
  digitalWrite( SERpin, HIGH );
  digitalWrite( SRCLKpin, HIGH );
  // Après les trois instructions suivantes, l'état du registre devient: 00000001.

  // Deuxième étape, on ajoute une nouvelle valeur LOW.
  digitalWrite( SRCLKpin, LOW );
  digitalWrite( SERpin, LOW );
  digitalWrite( SRCLKpin, HIGH );
  // Après les trois instructions suivantes, l'état du registre devient: 00000010.

  // Troisième étape, on ajoute une nouvelle valeur LOW.
  digitalWrite( SRCLKpin, LOW );
  digitalWrite( SERpin, LOW );
  digitalWrite( SRCLKpin, HIGH );
  // Après les trois instructions suivantes, l'état du registre devient: 00000100.

  // Il ne reste plus qu'à afficher les sorties
  digitalWrite( RCLKpin, HIGH );
}

Et voilà: la LED 3 est allumée. Si au contraire nous introduisons 2 fois la valeur digitalWrite( SERpin, HIGH ), alors les LED 1, 2 et 3 sont allumées (00000111). D'où l'importance d'introduire les bonnes valeurs HIGH ou LOW à chaque décalage...

Exercice: essayez de définir une fonction qui permette d'allumer une seule LED à la fois (numéro de la LED en paramètre de la fonction) et qui positionne toutes les valeurs du registre à l'aide d'une boucle for().

Solution: Vous n'avez pas trouvé? Alors voilà...

// La valeur de led varie de 1 à 8 représentant les sorties Q1 à Q8.
// Q1 est la première et la plus à droite des LEDs. Donc elle sera la dernière est être affichée.
// C'est pour cela que l'on teste l'indice dans le sens inverse de la boucle...
void allumeLEDunique( int led )
{
  digitalWrite( RCLKpin, LOW );

  for ( int i = 1; i < 9; ++i )
  {
    digitalWrite( SRCLKpin, LOW );
    // Sens inverse de la boucle...
    if ( 9 - i == led )
    {
      digitalWrite( SERpin, HIGH );
    }
    else
    {
      digitalWrite( SERpin, LOW );
    }
    digitalWrite( SRCLKpin, HIGH );
  }

  digitalWrite( RCLKpin, HIGH );
}


8.3.2.2.2. Une fonction adaptée au décalage

Essayons de définir une fonction qui permette d'allumer plusieurs LEDs. Cependant, cet exemple nécessite un peu plus de logique... En effet, cette fois il faut donner en paramètre, non plus un nombre identifiant une LED, mais un nombre identifiant plusieurs LEDs! Comment faisons-nous cela?

En fait, en informatique, une "valeur 8 bits" représente deux entités: d'une part un nombre compris entre 0 et 255, d'autre part une série de bits correspondant à ce nombre. Par exemple, la valeur 0 est représentée par la suite de 8 bits 00000000, la valeur 1 par 00000001, la valeur 2 par 00000010, etc... C'est une représentation en base 2 alors que dans la vie de tous les jours, nous comptons dans une base 10. Ainsi, un nombre N est représenté par la somme des puissances de 2. Soit par exemple: 4 = 0x2^0 + 0x2^1 + 1x2^2 + 0x2^3 + etc..., ce qui correspond à 4 = 0x1 + 0x2 + 1x4 + 0x8 + etc... Si vous désirez obtenir plus d'informations, Internet est rempli de pages explicatives sur le sujet...

Maintenant, comment utiliser ce mode de calcul dans notre exemple? Il faut écrire la représentation des LEDs allumées et éteintes sous forme de 0 et 1, de droite à gauche, la première sortie QA étant à droite. Une fois fait, il suffit de faire la somme des puissances de 2 qui correspondent aux bits de valeur 1. Par exemple, nous voulons afficher: 01001110. Le calcul donne: 0x2^7 + 1x2^6 + 0x2^5 + 0x2^4 + 1x2^3 + 1x2^2 + 1x2^1 + 0x2^0 = 0x128 + 1x64 + 0x32 + 0x16 + 1x8 + 1x4 + 1x2 + 0x1 = 78.

Généralisons cette méthode. La fonction d'affichage prend en paramètre une valeur de 0 à 255 correspondant à la représentation des LEDs à allumer. Il faut donc extraire de cette valeur la position des bits 1... Pour cela nous allons comparer le paramètre avec toutes les puissances de 2. Soit le pseudo code suivant:

// Commençons par la première puissance de 2 soit 2^0 = 1.
// Nous pouvons aussi l'inscrire en binaire:
byte reg = B00000001;

for ( int i = 0; i < 8; ++i )
{
  // Comparons le paramètre avec la puissance de 2 courante.
  if ( ( r & reg ) == reg )
  {
    // Bit à la position i vaut 1
  }
  else
  {
    // Bit à la position i vaut 0
  }
  // Mutliplions par 2 en décalant d'un bit à gauche
  reg <<= 1;
}

Il nous suffit maintenant d'ajouter les appels aux ports de contrôle du 74HC595, et de bien tenir compte de l'ordre dans lequel les bits sont décalés, en commençant par la dernière position. La variable reg identifie la position:

void writeRegisters( byte r )
{
  // Dernière position (QH à gauche)
  byte reg = B10000000;

  // On passe en revue les 8 bits ou positions
  for ( int i = 0; i < 8; ++i )
  {
    digitalWrite( SRCLKpin, LOW );
    if ( ( r & reg ) == reg )
    {
      digitalWrite( SERpin, HIGH );
    }
    else
    {
      digitalWrite( SERpin, LOW );
    }
    digitalWrite( SRCLKpin, HIGH );

    // On passe à la position précédente, jusqu'à remonter à la première position QA
    reg >>= 1;
  }
}

Maintenant que nous avons vu comment écrire la fonction de décalage, voyons comment l'utiliser dans un petit exemple: le chenillard. C'est une application que l'on retrouve souvent lorsqu'il s'agit de présenter le registre à décalage. Ici, on allumera deux LEDs. La fonction loop() ressemblera tout simplement à ça:

void loop()
{
  // Position de départ
  byte leds = B00010001;

  // Seulement 4 itérations car la position des LEDs est cyclique après 4 décalages.
  // Une fois la boucle for() terminée, la fonction loop() est appelée de nouveau
  // ce qui repositionne les bits dans la position de départ.
  for ( int i = 0; i < 4; ++i )
  {
    digitalWrite( RCLKpin, LOW );
    writeRegisters( leds );
    digitalWrite( RCLKpin, HIGH );
    leds <<= 1;
    delay( 1000 );
  }
}

Dernier point à soulever dans ce paragraphe: pour les besoins d'apprentissage, l'écriture d'une fonction telle que writeRegisters() est intéressante. Cependant, l'environnement de l'Arduino nous propose une fonction similaire: shiftOut(). L'exemple précédent s'écrirait:

void loop()
{
  byte leds = B00010001;
  for ( int i = 0; i < 4; ++i )
  {
    digitalWrite( RCLKpin, LOW );
    shiftOut( SERpin, SRCLKpin, MSBFIRST, leds );
    digitalWrite( RCLKpin, HIGH );
    leds <<= 1;
    delay( 1000 );
  }
}

Le paramètre MSBFIRST signifie Most Significant Bit FIRST soit le bit le plus significatif d'abord soit celui de gauche. De la même manière, il existe aussi LSBFIRST qui signifie Least Significant Bit FIRST donc le bit (le moins significatif) le plus à droite. Cela correspond en quelque sorte à un effet "miroir"... Ecrivez ce petit programme et modifiez le paramètre pour comprendre le fonctionnement.


8.3.2.2.3. Chaînage

Nous allons voir comment contrôler un plus grand nombre de sorties avec seulement 3 ports de l'Arduino en chaînant plusieurs registres à décalage. Concernant le montage il suffit de répliquer le même circuit (un seul 74HC595) en ajoutant un second C.I. câblé de façon identique. La seule connexion à ajouter est un lien entre le port QH' (9) du premier C.I. et le port SER (14) du second C.I. Ainsi, au moment du décalage, le 8ème bit du premier C.I. "sort" par le port QH' et sert alors de donnée pour le second C.I. par le port SER. Les ports SRCLK et RCLK des deux C.I. sont connectés entre eux.

Le programme se résume simplement par deux appels consécutifs à shiftOut(). Il suffit de représenter les 16 LEDs commandées par les 2 registres à décalage à l'aide de valeurs de 8 bits. Sur un exemple, si nous voulons afficher respectivement sur le 2ème C.I. et le 1er C.I.: 01001110 (=78) 00010011 (=19), nous aurons alors les deux instructions:

// 2ème C.I. alimenté par le port QH' du 1er C.I.
shiftOut( SERPin, SRCLKpin, MSBFIRST, 78 );
// 1er C.I.
shiftOut( SERPin, SRCLKpin, MSBFIRST, 19 );

Si par contre, vous désirez appliquer un effet "miroir" sur l'ensemble 11001000 et 01110010, il suffit d'intervertir l'ordre des appels et remplacer le paramètre MSBFIRST par LSBFIRST, soit:

shiftOut( SERPin, SRCLKpin, LSBFIRST, 19 );
shiftOut( SERPin, SRCLKpin, LSBFIRST, 78 );


8.3.2.3. TPIC6B595: le meilleur des deux mondes?

En effet, le meilleur des deux mondes? Peut-être... Si vous manquez un peu de place sur votre breadboard ou votre PCB... Ce composant est un registre à décalage 595 incluant 8 transistors MOSFETs semblables à ceux contenus dans un ULN2803. Cependant, la limitation est de 150mA sous 50V par sortie. Mais vous aurez assez de courant pour alimenter plusieurs LEDs par sortie, et ce, dans un seul C.I. à peine plus grand qu'un simple 74HC595!

Photo www.cunningturtle.com



Je conclurai ce chapitre un peu long sur l'utilisation, entre autre, des registres à décalage en vous conseillant fortement de faire plusieurs tests et d'essayer différents situations de manière à bien comprendre leurs fonctionnements. Vous aurez besoin de faire quelques schémas et de bien poser le problème avant de le programmer. Sinon, cela risque d'être assez difficile à debugguer.

Bonne programmation!



Aucun commentaire:

Publier un commentaire