Apprendre à piloter un bargraphe 12 segments bicolores

Image non disponible  ou comment fabriquer un indicateur de niveau à LED

Dans ce tutoriel, vous allez apprendre à piloter un bargraphe à 12 segments bicolores avec une carte Arduino. Certes, ce genre de composants avec le circuit comportant le contrôleur existent déjà dans les enseignes spécialisées, avec en prime la bibliothèque logicielle. Mais dans la philosophie DIY (Do It Yourself), nous allons piloter ce bargraphe from scratch, ou presque, en plongeant dans les documentations constructeur des composants et des protocoles de communication.

Le plus important ici n'est pas le produit fini, mais la compréhension des principes de fonctionnement qui vont vous permettre, par l'expérimentation sur plaque d'essais, de progresser dans l'apprentissage de l'électronique numérique et de la programmation des systèmes à base de microcontrôleur tel l'Arduino.

À noter que pour démarrer dans de bonnes conditions, il est préférable d'avoir un peu d'expérience sur Arduino. Si vous avez déjà fait clignoter une LED montée en série avec une résistance sur une plaque d'essais (le Hello World, façon Arduino), vous êtes bien parti.

Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Présentation rapide du matériel et démonstration

Pour commencer une petite démonstration, d'un objet bien utile…


Cliquez pour lire la vidéo


…qui rappellera les aventures d'un chevalier solitaire des années 80 Image non disponible

Image non disponible

Sur la plaquette de câblage rapide (breadboard), à gauche sur la photo, sont enfichés et chaînés deux bargraphes à LED (2 x 12 segments bicolores) identiques, et côte à côte pour n'en former plus qu'un en apparence. Pourquoi deux ? Parce que la démonstration n'en est que plus tape-à-l'œil ainsi pour seulement quelques euros de plus, et que le contrôleur MAX7219, la puce enfichée sur la plaquette de droite, peut les prendre en charge tous les deux.

Le MAX7219 (la version en boîtier DIP 28 broches) enfichée sur la plaquette de droite fera donc l'interface entre l'Arduino et les bargraphes. Ce seul composant permet de piloter jusqu'à 64 LED à partir d'une liaison série SPI utilisant seulement trois fils, les trois fils verts qui partent de la carte Arduino sur la photo.

Récapitulons le matériel :

Matériels

Remarques

  • Carte Arduino
Image non disponible

Ici, j'utiliserai une carte Arduino/Genuino UNO, mais vous devriez pouvoir utiliser n'importe quelle carte Arduino/Genuino compatible.
Prix constaté : environ 20 € pour la UNO.

  • Bargraphe à 12 segments bicolores (x2)
    Réf : KWL-R1230CDUGB (à cathode commune)
Image non disponible

Voir la datasheet des composants de la série KWL-R1230 du constructeur Luckylight.
C'est le modèle KWL-R1239CDUGB qu'il faut prendre, celui à cathode commune.
Prix constaté : j'ai pu en trouver un lot de deux pour environ 4-5 € (chez Lextronic).

  • Contrôleur d'afficheurs à LED
    Réf. : MAX7219CNG
Image non disponible

Voir la datasheet sur le site du constructeur Maxim Integrated : MAX7219
Penser à commander la version en boîtier DIP (MAX7219CNG), pour un prototype à câbler sans soudure.
Prix constaté : environ 10 €.

  • Plaquette de câblage rapide
  • Fils de câblage mâle-mâle (beaucoup)Image non disponible
  • Résistances électriques (29 kΩ, soit 27 et 2,2 kΩ en série)
Image non disponible

Tous ces petits matériels, qui font partie de l'attirail de l'apprenti maker, peuvent être trouvés facilement chez un revendeur de matériel électronique ou dans des kits d'électronique pour débutant.

La résistance électrique (27 à 30 kΩ environ) est indispensable au fonctionnement.

II. Description des composants et principe de fonctionnement

II-A. Le bargraphe à LED

Le bargraphe comporte 12 segments repérés de A à L. Pour les connexions, il comprend 14 broches numérotées (PIN) 1 à 14 selon la disposition ci-dessous (remarquez le chanfrein en bas à gauche pour s'orienter).

Image non disponible
Doc. Luckylight KWL-R1230CDUGB
Image non disponible
Doc. Luckylight KWL-R1230CDUGB

À l'intérieur du bargraphe sont en fait agencées 24 LED, chaque segment comportant deux LED, une rouge et une verte :

Image non disponible
Doc. Luckylight KWL-R1230CDUGB

Ainsi, chaque segment peut s'illuminer en rouge, vert ou jaune par synthèse additive du rouge et du vert.

Rappel : la LED ( Light-Emitting Diode )

La diode est un dipôle à semi-conducteur (jonction PN). Les deux bornes sont repérées anode A et cathode K. Elle a la propriété d'être conductrice pour un certain sens du courant et non conductrice pour l'autre sens. On peut donc la faire fonctionner en commutation en la faisant passer de l'état passant à l'état bloqué et inversement. Une LED (Light-Emitting Diode, soit diode électroluminescente en français) a la faculté d'émettre une lumière lorsqu'elle est correctement polarisée dans le sens direct (forward), le courant traversant la diode de l'anode vers la cathode comme sur le schéma ci-dessous :

Image non disponible
Image non disponible
LED 5mm verte, jaune et rouge. La patte la plus courte est la cathode.
Image non disponible
LED SMD (Single Mounted Device), composant à monter en surface.

La courbe caractéristique IF = f(VF) a l'allure suivante :

Image non disponible

Dans le sens direct, lorsque la tension est en dessous d'une tension de seuil (Forward voltage), aucun courant ne passe. La LED est bloquée et reste éteinte. Lorsque la tension de seuil est atteinte, le courant passe et la LED s'allume.

Si une tension est appliquée dans le sens indirect, la LED n'est plus passante, mais il ne faut pas dépasser une valeur limite (reverse voltage) sous peine de destruction du composant.

Le courant qui traverse la LED ne doit pas dépasser une valeur limite donnée par le constructeur. Dans le cas des LED rouge et verte de notre bargraphe, la valeur continue d'intensité supportable est de 25 mA (100 mA sous certaines conditions où l'alimentation est modulée en largeur d'impulsion) :

Image non disponible
Doc. Luckylight KWL-R1230CDUGB

Cet autre tableau ci-dessous donne les valeurs typiques de tension de seuil et de courant :

Image non disponible
Doc. Luckylight KWL-R1230CDUGB

Les caractéristiques typiques pour une polarisation correcte des LED rouge et verte sont :

  • LED rouge : VF = 1,8 V ; IF = 20 mA
  • LED verte : VF = 2,2 V ; IF = 20 mA

Un exercice classique (mais indispensable) pour correctement polariser une LED consiste à déterminer la valeur de la résistance à mettre en série dans le montage ci-dessous :

Image non disponible

La loi des mailles donne : Ve = VF + Rp.IF

Si on prend les caractéristiques de la LED rouge du bargraphe alimentée en continu sous Ve = 5 V, on obtient :

Rp = (5 - 1,8) / 20.10-3 = 160 Ω

II-B. Affichage multiplexé

La problématique est fort simple. Soit 24 LED à contrôler individuellement : doit-on passer par 24 sorties de l'Arduino, avec 24 résistances électriques et certainement de quoi amplifier le courant (24 x 20 = 480 mA consommés lorsque toutes les LED sont allumées) ?

Fort heureusement, non, et il existe depuis longtemps un mécanisme de multiplexage(*) permettant d'économiser à la fois l'énergie consommée et le nombre de sorties nécessaires au niveau de la carte Arduino. Nous allons voir également que la constitution de notre bargraphe est prévue pour ce mode de fonctionnement.

(*) Vous trouverez une description accessible du principe de fonctionnement d'un affichage multiplexé sur le site de sonelec-musique.

Image non disponible

On remarque les trois cathodes, chacune étant commune à un paquet de huit LED. On remarque aussi que par paquets de trois LED, les anodes sont aussi communes. Par exemple, l'anode de la broche 2 est commune aux trois LED rouges des segments A, E et I, et ainsi de suite. Cet agencement particulier est propice au fonctionnement multiplexé que nous allons décrire sur un exemple appliqué à notre bargraphe.

Par exemple, comment allumer les segments A à J en rouge en mode multiplexé, K et L restant éteints ?

Dans les schémas ci-dessous, les carrés bleus représentent les broches à l'état bas et les carrés rouges, les broches à l'état haut.

Étape 1 : La broche 1 (ou 14) est à l'état bas, les broches 2, 3, 4 et 5 à l'état haut.
Les quatre segments A, B, C et D s'allument en rouge. Les autres segments restent éteints, car les LED sont en configuration non passante.

Image non disponible

Étape 2 : La broche 6 (ou 9) est à l'état bas, les broches 2, 3, 4 et 5 à l'état haut.
Les quatre segments E, F, G et H s'allument en rouge. Les autres segments restent éteints, car les LED sont en configuration non passante.

Image non disponible

Étape 3 : La broche 7 (ou 8) est à l'état bas, les broches 2 et 3 à l'état haut.
Les deux segments I et J s'allument en rouge. Les autres segments restent éteints, car les LED sont en configuration non passante.

Image non disponible

Pour jouer avec la persistance rétinienne et créer l'illusion des segments A à J allumés, il suffit de répéter les étapes 1 à 3 en boucle à une fréquence suffisamment élevée (25 fois par seconde au moins).

Image non disponible
Et on fait tourner… Si tous les digits sont parcourus au moins 25 fois par seconde, la persistance rétinienne crée l'illusion des segments A à J allumés.

Si dans le cas d'un montage non multiplexé il fallait 24 sorties de l'Arduino, dans le cas d'un montage multiplexé il ne faut plus que 11 sorties pour piloter les 8 anodes et 3 cathodes. Une autre conséquence intéressante du multiplexage sur la consommation énergétique est que seulement huit LED au maximum seront allumées simultanément.

11 sorties, cela devient jouable même pour l'Arduino UNO. Par contre, au niveau logiciel, il faut prendre en charge toute la gestion du multiplexage.

C'est jouable, mais il y a encore mieux à faire avec un composant spécialisé dans le pilotage de LED (un contrôleur ou driver d'affichage à LED). C'est là qu'intervient le MAX7219 du fabricant de semi-conducteurs Maxim Integrated.

II-C. Le contrôleur d'affichage à LED MAX7219

Image non disponible
Doc. Maxim Integrated - MAX7219/MAX7221

Une application typique du MAX7219 est le pilotage d'afficheur à LED, comme celui d'un radio-réveil qui affiche l'heure par exemple.

L'unique résistance électrique entre ISET et V+ permet de régler le courant qui va traverser chaque LED. Les LED rouge et verte de notre bargraphe n'ayant pas les mêmes caractéristiques, le choix doit se porter sur la valeur de la résistance qui minimise le courant, au risque d'abaisser l'intensité lumineuse pour une couleur. Ici, les caractéristiques sont suffisamment proches, et on ne constate aucune dégradation.

Sur le montage ci-dessus, issu de la documentation de Maxim Integrated, on gère l'affichage de 8 chiffres (8 digits), chaque digit étant constitué de 7 segments (SEG A, SEG B, SEG C, … SEG G), plus un point décimal (SEG DP). Soit 64 LED au maximum, et notre bargraphe n'en comporte que 24.

Le MAX7219 est un composant spécialisé qui prend en charge le multiplexage avec une fréquence de 800 Hz environ, largement suffisante pour jouer avec la persistance rétinienne. Chaque sortie DIG 0, DIG 1… du MAX7219 doit être reliée à la cathode commune de chaque digit. Les sorties SEG A, SEG B, … SEG DP sont reliées aux 8 anodes des segments.

Notre bargraphe ne dispose pas de segments agencés géométriquement pour former des chiffres, mais son câblage interne fonctionne sur le même principe qu'un afficheur 7 segments à cathode commune :

Image non disponible
Observez la structure interne d'un afficheur standard 7 segments avec 3 digits à cathode commune. Elle est identique à celle de notre bargraphe.

Pour envoyer des informations au MAX7219 et le configurer, l'Arduino et celui-ci doivent s'entendre sur un protocole d'échange de données. Le MAX7219 ne nous laisse pas le choix, c'est le protocole de communication série SPI (Serial Protocol Interface) qui sera employé ici pour écrire dans les registres du MAX7219Les registres du MAX7219. Ce protocole standardisé (à peu près) qui permet d'établir une communication de type maître-esclave entre composants est simple à mettre en œuvre, et l'EDI standard Arduino offre une bibliothèque SPI nous évitant les affres du protocole.

III. Préparation du montage

III-A. Schéma de câblage

Voici une vue générale des constituants du système et du câblage.

Image non disponible

Attention de bien faire la différence entre la disposition logique des pattes des composants, qui est là pour faciliter la lecture du schéma, et la disposition physique.

Schéma logique

Schéma physique

Image non disponible
Image non disponible
Image non disponible
Image non disponible

III-B. Liaison MAX7219 - bargraphe

III-B-1. Câblage

Les trois broches DIG0, DIG1 et DIG2 du MAX7219 sont reliées aux trois cathodes du bargraphe. Le MAX7219 gère de son côté l'affichage multiplexé en activant à tour de rôle et de façon cyclique chaque digit à une fréquence d'environ 800 Hz.

Pour relier les broches SEGA, SEGB, … SEGDP, nous avons fait un choix.

Image non disponible

L'extrait de la documentation du MAX7219 ci-dessous montre la correspondance entre l'état du registre de données 8 bits et celui des LED d'un digit standard 7 segments. En mode No-Decode(1), on peut choisir de mettre un bit Dx à 1 ou à 0 pour allumer ou éteindre individuellement chaque LED du segment correspondant, tout simplement.

Image non disponible
Doc. Maxim Integrated - MAX7219/MAX7221

Le câblage choisi devrait faciliter le codage de l'état des LED du bargraphe :

  • les bits de poids faible D0 à D3 géreront l'affichage des 4 LED rouges du digit ;
  • les bits de poids fort D4 à D7 géreront l'affichage des 4 LED vertes du digit.

III-B-2. Conséquences sur le codage

Parmi les nombreux registres de configuration du MAX7219, les registres Digit 0, Digit 1, Digit 2… (respectivement aux adresses 0x01, 0x02, 0x03…) contiendront chacun un octet correspondant à l'état des segments du digit considéré.

On déduit, du câblage effectué, quelques exemples sur la façon d'écrire dans les registres du MAX7219, grâce à une fonction writeRegister(adresse, valeur) que nous décrirons au paragraphe suivant :

Premier segment en rouge

Pour le premier digit, bit D0=1 :

Image non disponible
 
Sélectionnez
writeRegister(DIGIT0, 0x01);
writeRegister(DIGIT1, 0x00);
writeRegister(DIGIT2, 0x00);

Le premier segment de chaque digit en rouge

Image non disponible
 
Sélectionnez
writeRegister(DIGIT0, 0x01);
writeRegister(DIGIT1, 0x01);
writeRegister(DIGIT2, 0x01);

Les quatre segments du premier digit en rouge

Pour le premier digit, bits D0 à D3 = 1 :

Image non disponible
 
Sélectionnez
byte allRed = 0x0F;

writeRegister(DIGIT0, allRed);
writeRegister(DIGIT1, 0x00);
writeRegister(DIGIT2, 0x00);

Les quatre segments du premier digit en vert

Pour le premier digit, bits D4 à D7 = 1 :

Image non disponible
 
Sélectionnez
byte allRed = 0x0F;
byte allGreen = allRed << 4;

writeRegister(DIGIT0, allGreen);
writeRegister(DIGIT1, 0x00);
writeRegister(DIGIT2, 0x00);

Les quatre segments du premier digit en jaune

Pour le premier digit, il faut allumer les LED rouges et vertes, bits D0 à D7 = 1 :

Image non disponible
 
Sélectionnez
byte allRed = 0x0F;
byte allGreen = allRed << 4;
byte allYellow = allRed | allGreen; // synthèse additive rouge+vert

writeRegister(DIGIT0, allYellow);
writeRegister(DIGIT1, 0x00);
writeRegister(DIGIT2, 0x00);

Comment remplir un digit, segment par segment (scan) ?

Image non disponible
Scan trois couleurs
 
Sélectionnez
byte red = 0x00,
     green = 0x00,
     yellow = 0x00;

for (int i = 0; i < 4; i++){
  red |= (1 << i);
  green = red << 4;
  yellow = red | green;
  writeRegister(DIGIT0, red); // scan du premier digit en rouge
  writeRegister(DIGIT1, green); // scan du deuxième digit en vert
  writeRegister(DIGIT2, yellow); // scan du troisième digit en jaune
  delay(1000);
}

Référence du langage Arduino

On pourra voir ou revoir les opérateurs bit à bit, en particulier les opérateurs | (OU logique) et << (décalage de bits à gauche).

III-C. Liaison SPI Arduino - MAX7219

SPI est un acronyme pour Serial Peripheral Interface. Il s'agit d'un bus de transmission de données série synchrone, où les données peuvent circuler simultanément dans les deux sens (Full-duplex), contrairement, par exemple, au bus I2C (Inter-Integrated Circuit). Pour permettre la transmission synchrone de données, le bus SPI utilise normalement quatre fils qui sont :

  • MOSI - Master out, Slave In : cette ligne transporte les données de l'Arduino vers le ou les périphériques SPI ;
  • MISO - Master in, Slave out : cette ligne transporte les données du ou des périphériques SPI vers l'Arduino ;
  • SS - Slave select : cette ligne permet de signifier au périphérique connecté sur le bus que nous souhaitons communiquer avec lui. Chaque périphérique SPI sur le bus dispose de sa ligne SS connectée à l'Arduino ;
  • SCLK - Serial clock : l'horloge, un signal rectangulaire périodique généré par le périphérique maître qui, justement, cadence les échanges sur les lignes MOSI et MISO.
Image non disponible
Liaison SPI : un maître et un esclave - image Wikipédia

Dans ce tutoriel, on considère que la carte Arduino est le périphérique maître (master) et le périphérique SPI tel le MAX7219 est l'esclave (slave). Sur notre carte Arduino/Genuino UNO et cartes compatibles, les connecteurs utilisés sont :

  • SS, connecteur D10 : vous pouvez utiliser une autre sortie numérique, mais le connecteur D10 est celui que l'on prend généralement par défaut, comme il est voisin des autres connecteurs utilisés pour la liaison SPI ;
  • MOSI, connecteur D11 ;
  • MISO, connecteur D12 ;
  • SCLK, connecteur D13.

Pour les utilisateurs de l'Arduino Mega, MISO est sur le connecteur D50, MOSI le D51, SCLK le D52 et SS est d'habitude le D53.

  • Le connecteur MOSI de l'Arduino doit être relié à la broche IN du MAX7219.
  • Le connecteur SCLK de l'Arduino doit être relié à la broche CLK du MAX7219.
  • Le connecteur SS de l'Arduino doit être relié à la broche LOAD du MAX7219.

Comme il n'y a pas de retour d'informations de l'esclave (le MAX7219) vers le maître (l'Arduino), le connecteur MISO de l'Arduino n'est pas utilisé.

Image non disponible
Liaison SPI entre l'Arduino UNO et le MAX7219 avec 3 fils

Pour plus de détails sur les connecteurs, référez-vous au document SPI library.

Ci-dessous, les déclarations minimales d'usage d'un programme écrit dans l'EDI d'Arduino lorsqu'il s'agit de communiquer via la liaison SPI (voir SPI settings) :

 
Sélectionnez
#include <SPI.h>

#define LOAD          10   /* Entrée LOAD du MAX72219 */

void setup() {
  pinMode(LOAD, OUTPUT);
  digitalWrite(LOAD, HIGH);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); /* bits de poids fort en premier /*
}

Les commandes à envoyer au MAX7219 sont des paires d'octets (adresse de registre, valeur) :

Image non disponible
Doc. Maxim Integrated - MAX7219/MAX7221

Ci-dessous, la fonction writeRegister utilisée pour écrire dans les registres du MAX7219 :

 
Sélectionnez
void writeRegister(byte thisRegister, byte thisValue) // Écrire dans un registre du MAX7219
{
  // Mettre l'entrée LOAD à l'état bas
  digitalWrite(LOAD, LOW);

  SPI.transfer(thisRegister); // Adresse du registre
  SPI.transfer(thisValue); // Contenu du registre

  // Basculer l'entrée LOAD à l'état haut pour verrouiller et transférer les données
  digitalWrite(LOAD, HIGH);
}

Pour communiquer avec un périphérique SPI tel le MAX7219, on met sa broche LOAD à l'état bas (LOW), on échange avec lui, puis on remet sa broche LOAD à l'état haut (HIGH).

III-D. Alimentation du dispositif

Les alimentations sont masquées dans les schémas vus plus haut :

  • La broche V+ du MAX7219 doit être reliée au connecteur 5 V de l'Arduino.
  • Les deux broches GND (4 et 9) du MAX7219 et un connecteur GND de l'Arduino doivent être reliés (masse commune).

    Bien entendu, vous attendrez le dernier moment, quand tout sera préparé et vérifié, avant d'alimenter votre dispositif.

  • Pour limiter le courant qui traverse chaque LED, une résistance externe RSET doit être câblée entre les broches V+ et ISET du MAX7219 :
Image non disponible

La valeur de cette résistance ne doit rien au hasard, il y va de la protection de votre MAX7219 et du bargraphe. Le constructeur fournit un tableau pour cela :

Image non disponible
Doc. Maxim Integrated - MAX7219/MAX7221

On rappelle les caractéristiques des LED rouge et verte du bargraphe :

  • LED rouge : VF = 1,8 V ; IF = 20 mA
  • LED verte : VF = 2,2 V ; IF = 20 mA

Ce qui nous donne un RSET entre 27 et 29 kΩ. Par sécurité, il faut prendre la plus grande valeur et on obtient de bons résultats en termes de luminosité avec RSET = 29 kΩ (la résistance normalisée supérieure la plus proche étant de 33 kΩ, on associera deux résistances en série de 27 et 2,2 kΩ).

Faisons un bilan du courant total consommé, en se plaçant dans le cas le plus défavorable.

Le courant maximal traversant chaque LED étant fixé grâce à la résistance RSET, et sachant que dans le fonctionnement multiplexé seules huit LED peuvent être allumées simultanément : 8 x 20 = 160 mA, auxquels on rajoute environ 10 mA consommés par le MAX7219, soit environ 170 mA. Si on prend en compte la consommation propre de la carte Arduino, environ 40 mA, l'alimentation générale de votre dispositif doit pouvoir fournir jusqu'à 210 mA pour fonctionner correctement.

En d'autres termes, vous ne risquez rien si vous alimentez votre dispositif avec le port USB de votre PC ou votre portable (courant maxi = 500 mA). Attention toutefois, certains notebooks ont des stratégies de limitation de courant plus sévères sur leur port USB, et il vous faudra passer par un hub USB externe ou par un autre mode d'alimentation.

IV. Les registres du MAX7219

Passons en revue les registres principaux du MAX7219. Pour plus de détails, référez-vous à la datasheet.

Lors de la mise en alimentation, les LED sont toutes éteintes et les registres sont effacés. Le MAX7219 est en mode shutdown.

Adresse

Registre

Description

0x0C

Shutdown

0x00 : shutdown mode
0x01 : normal operation

0x0F

Display-test

Mode Test par défaut avec toutes les LED allumées (0x01). Mettre à 0x00 (normal operation) pour quitter le mode Test.

0x09

Decode-Mode

Comme nous n'avons pas affaire à un afficheur 7 segments (pas de transcodage DCB vers 7 segments), il faut passer en mode no-decode (0x00).

0x0B

Scan limit

Le nombre de digits à prendre en compte pour le multiplexage. Pour notre bargraphe, fixer la valeur à 0x02 (DIG0, DIG1 et DIG2).

0x0A

Intensity

Limiter la luminosité des LED par modulation en largeur d'impulsions. 0x00 à 0x0F pour la luminosité maximale.

0x01

Digit 0

Valeur du premier digit.

 

0x08

Digit 7

Valeur du huitième digit.

V. Premiers tests

Un premier programme de test, sans prétention, qui allume trois fois tous les segments selon la séquence : rouge, vert et jaune :

test.ino
Sélectionnez
#include <SPI.h>

#define LOAD          10   /* Entrée LOAD du MAX72219 */

#define DISPLAY_TEST  0x0F /* Registre Display-Test */
#define SHUTDOWN      0x0C /* Registre Shutdown */
#define DECODE_MODE   0x09 /* Registre Decode-mode */
#define SCAN_LIMIT    0x0B /* Registre Scan-limit */
#define INTENSITY     0x0A /* Registre Intensity */
#define DIGIT0        0x01 /* Registre Digit 0 */
#define DIGIT1        0x02 /* Registre Digit 1 */
#define DIGIT2        0x03 /* Registre Digit 2 */

void setup() {
  pinMode(LOAD, OUTPUT);
  digitalWrite(LOAD, HIGH);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); /* bits de poids fort en premier */

  writeRegister(DISPLAY_TEST, 0x00); /* Normal operation */
  writeRegister(DECODE_MODE, 0x00);  /* No decode */
  writeRegister(SCAN_LIMIT, 0x02);   /* Display digits 0, 1, 2 */
  writeRegister(INTENSITY, 0x08);    /* LED brightness, max=0x0F */
  writeRegister(SHUTDOWN, 0x01);     /* Normal operation */

  test(); /* Lancer le test */

  writeRegister(SHUTDOWN, 0x00);  /* Shutdown mode */
}

void test() {
  byte allRed = 0x0F;
  byte allGreen = allRed << 4;
  byte allYellow = allRed | allGreen; /* synthèse additive rouge+vert */

  for (int i = 0 ; i < 3 ; i++) {
    writeRegister(DIGIT0, allRed);
    writeRegister(DIGIT1, allRed);
    writeRegister(DIGIT2, allRed);

    delay(1000);

    writeRegister(DIGIT0, allGreen);
    writeRegister(DIGIT1, allGreen);
    writeRegister(DIGIT2, allGreen);

    delay(1000);

    writeRegister(DIGIT0, allYellow);
    writeRegister(DIGIT1, allYellow);
    writeRegister(DIGIT2, allYellow);

    delay(1000);
  }
}

void writeRegister(byte thisRegister, byte thisValue) // Écrire dans un registre du MAX7219
{
  // Mettre l'entrée LOAD à l'état bas
  digitalWrite(LOAD, LOW);

  SPI.transfer(thisRegister); // Adresse du registre
  SPI.transfer(thisValue); // Contenu du registre

  // Basculer l'entrée LOAD à l'état haut pour verrouiller et transférer les données
  digitalWrite(LOAD, HIGH);
}

void loop()
{
  //
}

VI. Codage de l'état des segments avec un tableau

Le but est d'allumer le bargraphe en décrivant l'état des segments dans un tableau :

 
Sélectionnez
enum color_t {NOCOLOR, RED, GREEN, YELLOW};

const byte nb_digits = 3;   /* nombre de digits du bargraph */
const byte nb_segments_digit = 4; /* nombre de segments par digit */

color_t bargraph[nb_digits * nb_segments_digit] = {
    GREEN, YELLOW, RED, RED, YELLOW, GREEN,
    GREEN, YELLOW, RED, RED, YELLOW, GREEN
};

bargraphDraw(bargraph);

Avec ce tableau, le bargraphe sera dans l'état suivant :

Image non disponible
L'état du bargraphe décrit avec un tableau.

Ci-dessous, la fonction bargraphDraw qui va calculer et transférer les valeurs aux registres en fonction du tableau passé en paramètre :

 
Sélectionnez

void bargraphDraw(color_t const *mybargraph) {
  byte c;
  for (byte d = 0; d < nb_digits; d++) { /* pour chaque digit */
    c = 0;
    for (byte s = 0; s < nb_segments_digit; s++) { /* pour chaque segment du digit */
      switch (mybargraph[d * nb_segments_digit + s]) {
        case NOCOLOR : break;
        case RED : c |= (1 << s); break;
        case GREEN : c |= (1 << (s + 4)); break;
        case YELLOW :  c |= ((1 << s) | (1 << (s + 4))); break;
        default: break;
      }
    }
    writeRegister(d + 1, c);
  }
}

Enfin, à titre de démonstration, voici une séquence animée qui fait défiler en boucle une structure en décalant les segments vers la gauche :

demo_tableau.ino
Sélectionnez
#include <SPI.h>

#define LOAD          10   /* Entrée LOAD du MAX72219 */

#define DISPLAY_TEST  0x0F /* Registre Display-Test */
#define SHUTDOWN      0x0C /* Registre Shutdown */
#define DECODE_MODE   0x09 /* Registre Decode-mode */
#define SCAN_LIMIT    0x0B /* Registre Scan-limit */
#define INTENSITY     0x0A /* Registre Intensity */

enum color_t {NOCOLOR, RED, GREEN, YELLOW};
void bargraphDraw(color_t const *mybargraph);

const byte nb_digits = 3;   /* nombre de digits du bargraph */
const byte nb_segments_digit = 4; /* nombre de segments par digit */

void setup() {
  pinMode(LOAD, OUTPUT);
  digitalWrite(LOAD, HIGH);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); /* bits de poids fort en premier */

  writeRegister(DISPLAY_TEST, 0x00); /* Normal operation */
  writeRegister(DECODE_MODE, 0x00); /* No decode */
  writeRegister(SCAN_LIMIT, nb_digits - 1); /* Display digits 0, 1, 2 */
  writeRegister(INTENSITY, 0x08);   /* LED brightness, max=0x0F */
  writeRegister(SHUTDOWN, 0x01);    /* Normal operation */

  demo();  /* Lancer la démo */

  writeRegister(SHUTDOWN, 0x00);  /* Shutdown mode */
}

void demo() {
  const byte nb_segments = nb_digits * nb_segments_digit;
  color_t bargraph[nb_segments] = {
    GREEN, YELLOW, RED, RED, YELLOW, GREEN,
    GREEN, YELLOW, RED, RED, YELLOW, GREEN
  };

  bargraphDraw(bargraph);
  delay(1000);

  for (int i = 0; i < 10 * nb_segments; i++) {
    /* décalage à gauche */
    color_t c = bargraph[0];
    for (int j = 0; j < nb_segments - 1; j++) {
      bargraph[j] = bargraph[j + 1];
    }
    bargraph[nb_segments - 1] = c;
    /* fin décalage à gauche */

    bargraphDraw(bargraph);
    delay(80);
  }

  delay(3000);
}

void bargraphDraw(color_t const *mybargraph) {
  byte c;
  for (byte d = 0; d < nb_digits; d++) { /* pour chaque digit */
    c = 0;
    for (byte s = 0; s < nb_segments_digit; s++) { /* pour chaque segment du digit */
      switch (mybargraph[d * nb_segments_digit + s]) {
        case NOCOLOR : break;
        case RED : c |= (1 << s); break;
        case GREEN : c |= (1 << (s + 4)); break;
        case YELLOW :  c |= ((1 << s) | (1 << (s + 4))); break;
        default: break;
      }
    }
    writeRegister(d + 1, c);
  }
}

void writeRegister(byte thisRegister, byte thisValue) // Écrire dans un registre du MAX7219
{
  // Mettre l'entrée LOAD à l'état bas
  digitalWrite(LOAD, LOW);

  SPI.transfer(thisRegister); // Adresse du registre
  SPI.transfer(thisValue); // Contenu du registre

  // Basculer l'entrée LOAD à l'état haut pour verrouiller et transférer les données
  digitalWrite(LOAD, HIGH);
}

void loop()
{
  //
}
Image non disponible

VII. Exemple d'application : un indicateur de niveau

Les indicateurs de niveau à LED permettent de rendre compte du niveau de charge d'une batterie, du niveau sonore d'un dispositif audio (VU-mètre), du niveau d'eau d'une citerne ou de carburant d'un réservoir, etc.

Image non disponible
Un indicateur de niveau de batterie que l'on trouve dans le commerce.

Pour clore cet article, il ne manquait plus qu'une fonction pour réaliser un indicateur personnalisé. La fonction levelGauge proposée prend en paramètre le niveau à afficher en pourcentage entre 0 % et 100 %. Lorsque le niveau s'approche des 100 %, la couleur tend vers le jaune d'abord, puis le rouge :

Image non disponible
Indicateur à 100 %

Syntaxe d'appel :

 
Sélectionnez
levelGauge(50); /* niveau=50% */

Ci-dessous un programme de démonstration avec un balayage croissant puis décroissant du niveau entre 0 % et 100 % :

demo_niveau.ino
Sélectionnez
#include <SPI.h>

#define LOAD          10   /* Entrée LOAD du MAX72219 */

#define DISPLAY_TEST  0x0F /* Registre Display-Test */
#define SHUTDOWN      0x0C /* Registre Shutdown */
#define DECODE_MODE   0x09 /* Registre Decode-mode */
#define SCAN_LIMIT    0x0B /* Registre Scan-limit */
#define INTENSITY     0x0A /* Registre Intensity */

enum color_t {NOCOLOR, RED, GREEN, YELLOW};
void bargraphDraw(color_t const *mybargraph);
void levelGauge(unsigned int percentage);

const byte nb_digits = 3;   /* nombre de digits du bargraph */
const byte nb_segments_digit = 4; /* nombre de segments par digit */

void setup() {
  pinMode(LOAD, OUTPUT);
  digitalWrite(LOAD, HIGH);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); /* bits de poids fort en premier */

  writeRegister(DISPLAY_TEST, 0x00); /* Normal operation */
  writeRegister(DECODE_MODE, 0x00); /* No decode */
  writeRegister(SCAN_LIMIT, nb_digits - 1); /* Display digits 0, 1, 2 */
  writeRegister(INTENSITY, 0x08);   /* LED brightness, max=0x0F */
  writeRegister(SHUTDOWN, 0x01);    /* Normal operation */

  demo();  /* Lancer la démo */

  writeRegister(SHUTDOWN, 0x00);  /* Shutdown mode */
}

void demo() {
  for (int i = 0; i < 5; i++) {
    for (int percent = 0; percent <= 100; percent += 5) {
      levelGauge(percent);
      delay(20);
    }
    for (int percent = 100; percent >= 0; percent -= 5) {
      levelGauge(percent);
      delay(20);
    }
  }

  delay(3000);
}

void bargraphDraw(color_t const *mybargraph) {
  byte c;
  for (byte d = 0; d < nb_digits; d++) { /* pour chaque digit */
    c = 0;
    for (byte s = 0; s < nb_segments_digit; s++) { /* pour chaque segment du digit */
      switch (mybargraph[d * nb_segments_digit + s]) {
        case NOCOLOR : break;
        case RED : c |= (1 << s); break;
        case GREEN : c |= (1 << (s + 4)); break;
        case YELLOW :  c |= ((1 << s) | (1 << (s + 4))); break;
        default: break;
      }
    }
    writeRegister(d + 1, c);
  }
}

void levelGauge(unsigned int percentage) { /* indicateur de niveau, percentage entre 0 et 100 */
  unsigned int level ,
               levelRed = nb_digits * nb_segments_digit - 2,
               levelYellow = levelRed - 2;
      
  color_t bargraph[nb_digits * nb_segments_digit] = {NOCOLOR}; /* parce que NOCOLOR=0, toutes les autres valeurs non indiquées prendront la valeur 0 */

  if (percentage > 100)
    return;

  if (percentage > 0) {
    level = map(percentage, 0, 100, 0, nb_digits * nb_segments_digit - 1);

    if (level >= levelRed) { /* niveau dans le rouge */
      for (int i = levelRed; i <= level; i++) {
        bargraph[i] = RED;
      }
    }
    if (level >= levelYellow) { /* niveau dans le orange */
      for (int i = levelYellow; i <= min(level, levelRed - 1); i++) {
        bargraph[i] = YELLOW;
      }
    }
    for (int i = 0; i <= min(level, levelYellow - 1); i++) {
      bargraph[i] = GREEN;
    }
  }
  bargraphDraw(bargraph);
}

void writeRegister(byte thisRegister, byte thisValue) // Écrire dans un registre du MAX7219
{
  // Mettre l'entrée LOAD à l'état bas
  digitalWrite(LOAD, LOW);

  SPI.transfer(thisRegister); // Adresse du registre
  SPI.transfer(thisValue); // Contenu du registre

  // Basculer l'entrée LOAD à l'état haut pour verrouiller et transférer les données
  digitalWrite(LOAD, HIGH);
}

void loop()
{
  //
}
Image non disponible
Scan du niveau entre 0 et 100 %

Il ne reste plus qu'à inclure cette fonction dans votre application et de l'adapter à votre convenance.

La fonction map intégrée dans le langage Arduino permet de réétalonner un nombre compris dans une fourchette de valeurs vers une fourchette de destination.

map(value, fromLow, fromHigh, toLow, toHigh)

Ci-dessous une démonstration avec un potentiomètre 10 k sur l'entrée analogique A0 :


Cliquez pour lire la vidéo


Lecture du port analogique A0
CacherSélectionnez

Défi Image non disponible

Le capteur à ultrasons SRF-05 est très répandu en petite robotique. Il fonctionne comme un sonar et permet de mesurer la distance d'un obstacle devant lui. En prime, il s'interface facilement avec l'Arduino.

Image non disponible

Pour plus de détails sur le fonctionnement et la programmation de ce capteur, je vous renvoie à ce tutoriel.

Le défi consiste à reprendre le principe de l'indicateur de niveau vu précédemment pour informer visuellement de la proximité d'un obstacle.

Voici une vidéo du résultat attendu :


Cliquez pour lire la vidéo


Vous avez tous les éléments pour relever le défi, à vous de jouer !

Proposition de code à adapter
CacherSélectionnez

VIII. Et avec DEUX bargraphes !

À dire vrai, un MAX7219 devient intéressant lorsque vous avez beaucoup de LED à piloter. Le MAX7219 peut gérer jusqu'à 8 digits x 8 segments = 64 LED. Notre bargraphe n'en comportant que 24, on peut très bien accoler un deuxième bargraphe et donner l'illusion d'un bargraphe unique à 2 x 12 = 24 segments.

Image non disponible
Deux bargraphes, côte à côte

L'opération n'est pas bien compliquée avec un peu de rigueur, car votre plaque de câblage va commencer à ressembler à un plat de nouilles…

Il suffit de relier les cathodes du deuxième bargraphe aux broches DIG3, DIG4 et DIG5 du MAX7219, et à relier les anodes des segments SEG A, SEG B, … SEG DP entre elles en suivant le schéma ci-dessous :

Image non disponible
Deux bargraphes chaînés

Au niveau de la programmation, il suffit d'étendre la commande aux trois digits supplémentaires DIG3, DIG4 et DIG5. C'est tout…

IX. Conclusion

Dans ce tutoriel, nous avons vu comment piloter un bargraphe à LED grâce au principe du multiplexage et à l'aide d'un composant spécialisé. Ce driver de LED communique avec l'Arduino grâce à une liaison SPI, un protocole de communication série répandu à l'instar du protocole I2C, et qui ne consommera que trois connecteurs de votre carte Arduino. En suivant ces mêmes principes, vous êtes à même de piloter d'autres dispositifs à LED : afficheur 7 segments, matrice ou cube de LED.

Image non disponible
Lumières d'ambiance avec un cube de LED piloté par Arduino et MAX7219, d'après JolliCube - an 8x8x8 LED Cube (SPI) - instructables.com

Il est à noter qu'il existe bien d'autres moyens pour piloter quantité de LED : d'autres drivers spécialisés comme le Holtek HT16K33 qui fonctionne avec une liaison I2C, ou avec des composants plus généralistes comme les très répandus registres à décalage. Le MAX7219 présente des avantages : peu de composants supplémentaires (ici, une seule résistance), et pas de gestion du multiplexage à programmer, puisque le MAX7219 s'en charge tout seul.

Pour aller plus loin, vous pourriez passer à un prototype durable en soudant vos composants (le MAX7219 sera inséré dans un support standard 28 broches, et c'est le support qui sera soudé), fils de câblage, capteurs et connecteurs sur une plaque veroboard et construire un shield personnalisé, que vous pourriez exploiter pour fabriquer un indicateur de niveau fonctionnel et autonome.

Mais l'essentiel reste d'avoir pu progresser dans la pratique de l'électronique numérique et des microcontrôleurs, le tout dans la philosophie Do It Yourself qui a motivé ce tutoriel.

Je remercie Auteur et Winjerome pour leur aide précieuse dans la rédaction de cet article, ainsi que Claude LELOUP pour ses corrections orthographiques.

X. Sitographie

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   


Dans l'autre mode Code B, un décodeur DCB-7 segments est activé. On envoie dans le registre le chiffre à afficher en binaire naturel codé sur 4 bits, et le décodeur fera la correspondance pour activer les segments qui réaliseront le dessin du chiffre.

  

Licence Creative Commons
Le contenu de cet article est rédigé par f-leb et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.