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
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 |
---|---|
|
Ici, j'utiliserai une carte Arduino/Genuino UNO, mais vous devriez pouvoir utiliser n'importe quelle carte Arduino/Genuino compatible. |
|
Voir la datasheet des composants de la série KWL-R1230 du constructeur Luckylight. |
|
Voir la datasheet sur le site du constructeur Maxim Integrated : MAX7219 |
|
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. |
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).
À l'intérieur du bargraphe sont en fait agencées 24 LED, chaque segment comportant deux LED, une rouge et une verte :
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 :
La courbe caractéristique IF = f(VF) a l'allure suivante :
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) :
Cet autre tableau ci-dessous donne les valeurs typiques de tension de seuil et de courant :
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 :
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.
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. |
Étape 2 : La broche 6 (ou 9) est à l'état bas, les broches 2, 3, 4 et 5 à l'état haut. |
Étape 3 : La broche 7 (ou 8) est à l'état bas, les broches 2 et 3 à l'état haut. |
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).
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▲
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 :
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.
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 |
---|---|
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.
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.
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 Sélectionnez
|
Le premier segment de chaque digit en rouge Sélectionnez
|
Les quatre segments du premier digit en rouge Sélectionnez
|
Les quatre segments du premier digit en vert Sélectionnez
|
Les quatre segments du premier digit en jaune Sélectionnez
|
Comment remplir un digit, segment par segment (scan)Â ? Sélectionnez
|
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.
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é.
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) :
#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) :
Ci-dessous, la fonction writeRegister utilisée pour écrire dans les registres du MAX7219 :
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 :
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 :
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 |
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 :
#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 :
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 :
Ci-dessous, la fonction bargraphDraw qui va calculer et transférer les valeurs aux registres en fonction du tableau passé en paramètre :
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 :
#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()
{
//
}
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.
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 :
Syntaxe d'appel :
levelGauge(50
); /* niveau=50% */
Ci-dessous un programme de démonstration avec un balayage croissant puis décroissant du niveau entre 0 % et 100 % :
#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()
{
//
}
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
Défi
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.
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 !
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.
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 :
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.
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▲
-
Documentation constructeur (datasheet)
-
Le multiplexage
-
MAX7219/7221Â -Â Playground Arduino
-
La liaison SPI