Nous avons vu la technique du Charlieplexing dans « Témoins de positions : le Charlieplexing », nous allons maintenant l’appliquer à un système à 5 broches de pilotage et 16 LED puis l’intégrer dans notre application.
Charlieplexing des 16 LED témoins [1]
Le schéma pour allumer 16 LED est le suivant.
- Charlieplexing avec 5 broches et 16 LED
On peut noter que le schéma est incomplet. En effet, avec 5 broches, il est possible de piloter 20 LED. Ici les couples de broches 0-3 et 1-4 sont manquants et nous n’allons donc pas les placer dans notre table d’état des broches.
Voici donc la table d’état des broches pour chaque LED
broche 0 | broche 1 | broche 2 | broche 3 | broche 4 | |
D0 | 0 | 1 | Z | Z | Z |
D1 | 1 | 0 | Z | Z | Z |
D2 | Z | 0 | 1 | Z | Z |
D3 | Z | 1 | 0 | Z | Z |
D4 | Z | Z | 0 | 1 | Z |
D5 | Z | Z | 1 | 0 | Z |
D6 | Z | Z | Z | 0 | 1 |
D7 | Z | Z | Z | 1 | 0 |
D8 | 0 | Z | 1 | Z | Z |
D9 | 1 | Z | 0 | Z | Z |
D10 | Z | Z | 0 | Z | 1 |
D11 | Z | Z | 1 | Z | 0 |
D12 | Z | 0 | Z | 1 | Z |
D13 | Z | 1 | Z | 0 | Z |
D14 | 0 | Z | Z | Z | 1 |
D15 | 1 | Z | Z | Z | 0 |
Nous allons donc construire cette table en C comme nous l’avons fait pour la Charlieplexing avec 3 broches de commande.
const byte etatSortiePourLED[16][5] = { { OUT0, OUT1, INZ, INZ, INZ }, /* D0 */ { OUT1, OUT0, INZ, INZ, INZ }, /* D1 */ { INZ, OUT0, OUT1, INZ, INZ }, /* D2 */ { INZ, OUT1, OUT0, INZ, INZ }, /* D3 */ { INZ, INZ, OUT0, OUT1, INZ }, /* D4 */ { INZ, INZ, OUT1, OUT0, INZ }, /* D5 */ { INZ, INZ, INZ, OUT0, OUT1 }, /* D6 */ { INZ, INZ, INZ, OUT1, OUT0 }, /* D7 */ { OUT0, INZ, OUT1, INZ, INZ }, /* D8 */ { OUT1, INZ, OUT0, INZ, INZ }, /* D9 */ { INZ, INZ, OUT0, INZ, OUT1 }, /* D10 */ { INZ, INZ, OUT1, INZ, OUT0 }, /* D11 */ { INZ, OUT0, INZ, OUT1, INZ }, /* D12 */ { INZ, OUT1, INZ, OUT0, INZ }, /* D13 */ { OUT0, INZ, INZ, INZ, OUT1 }, /* D14 */ { OUT1, INZ, INZ, INZ, OUT0 } /* D15 */ };
La fonction programmeBroche(...)
reste la même par contre la fonction gereLED(...)
doit être modifiée puisque l’on passe de 3 à 5 broches de commande.
void gereLED(int num) { if (etatLED[num] == ALLUME) { programmeBroche(8,etatSortiePourLED[num][0]); programmeBroche(9,etatSortiePourLED[num][1]); programmeBroche(10,etatSortiePourLED[num][2]); programmeBroche(11,etatSortiePourLED[num][3]); programmeBroche(12,etatSortiePourLED[num][4]); } else { programmeBroche(8,INZ); programmeBroche(9,INZ); programmeBroche(10,INZ); programmeBroche(11,INZ); programmeBroche(12,INZ); } }
Et le tableau etatLED
doit être agrandi pour contenir 16 éléments.
Intégration dans l’application
Pour intégrer l’allumage des témoins dans l’application il faut tout d’abord insérer l’appel à gereLED()
dans loop()
. Nous faisons face à un premier problème. Pour que les servomoteurs pivotent suffisamment lentement, l’attente est de 5ms. Cela conduit à un balayage des 16 LED en 80ms ce qui est trop lent pour que la persistance rétinienne opère. Il faudrait balayer au moins 4 fois plus souvent. Par conséquent, le délai dans loop()
va être réduit à 1ms et la gestion des poussoirs et des servomoteurs sera appelée toutes les 5 fois alors que la gestion des LED sera appelée toutes les fois. Nous allons donc ajouter un compteur compteurGestionServos
initialisé à 0. Comme ceci.
void loop() { compteurGestionServos++; if (compteurGestionServos == 5) { compteurGestionServos = 0; ... /* code de gestion des servos et des poussoir à l'identique */ ... } gereLED(numLED); numLED++; if (numLED == 16) numLED = 0; delay(1); }
Il suffit ensuite de changer l’état des LED en ALLUME ou ETEINT dans le tableau etatLED
en fonction des événements. Deux fonctions membre de la classe DescripteurServo
sont concernées : evenementServo(...)
où on va éteindre la LED de la position que l’on quitte et gereServo()
où on va allumer la LED de la position que l’on atteint. Dans le tableau etatLED
, les LED sont alternativement rouges et vertes et par conséquent la paire de LED correspondant à un servomoteur est à la position numéro de servo × 2 (angle minimum) et numéro de servo × 2 + 1 (angle maximum).
void evenementServo() { cptArret = -1; switch (etatServo) { case SERVO_A_ANGLE_MIN: etatLED[numServo * 2] = ETEINT; objetServo.attach(pin); case SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN: vitesse = 1; etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX; break; case SERVO_A_ANGLE_MAX: etatLED[numServo * 2 + 1] = ETEINT; objetServo.attach(pin); case SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX: vitesse = -1; etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN; break; } }
Dans la fonction membre gereServo()
, la partie qui s’occupe du passage de la vitesse à 0 quand les angles minimum ou maximum sont atteint est modifiée comme suit :
if (! reglageEnCours || etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN || etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX) { if (angle > angleMax) { angle = angleMax; arreteServo(); etatServo = SERVO_A_ANGLE_MAX; etatLED[numServo * 2 + 1] = ALLUME; } else if (angle < angleMin) { angle = angleMin; arreteServo(); etatServo = SERVO_A_ANGLE_MIN; etatLED[numServo * 2] = ALLUME; } }
Il ne faut pas oublier d’allumer la LED correspondant à l’angle minimum dans la fonction membre connecte(...)
/* allume la LED correspondante */ etatLED[numServo * 2] = ALLUME;
Nous sommes parvenus au bout de ce développement. Voici le sketch de l’application complète à télécharger.
- Sketch de l’application complète
(clic sur l'image pour agrandir ou télécharger) - mis à jour en version 1.1. La position des servos est désormais mémorisée dans l’EEPROM.
- Dessin du montage complet
(clic sur l'image pour agrandir ou télécharger) - Attention : le montage montre les 8 servomoteurs alimentés par l’Arduino. Ce n’est pas possible en pratique et il est nécessaire d’avoir une alimentation 5V séparée pour les servomoteurs
- Dessin du montage complet à télécharger en PDF
(clic sur l'image pour agrandir ou télécharger) - Attention : le montage montre les 8 servomoteurs alimentés par l’Arduino. Ce n’est pas possible en pratique et il est nécessaire d’avoir une alimentation 5V séparée pour les servomoteurs
Ainsi qu’une vidéo de démonstration.