Projet Réveil-Matin (partie XI) - Affichage sur un afficheur à LED

Dans le projet Réveil-matin, l'affichage de l'heure sur un écran LCD avait été présenté dans un article précédent. Voici une variante du projet où l'heure est affiché sur un afficheur à LEDs à quatre digits.
Dans ce projet, les boutons fonctionnent comme dans les versions différentes. Pour signaler les changements d'état de l'horloge, on utilise les points décimaux (la led DP) des digits :

  • L'heure est affichée sur quatre digits au format HH.MN.
  • Le premier point le plus à gauche (au milieu de HH) allumé signale que l'horloge est en mode réglage de l'heure. Lorsque HH clignote, c'est la valeur de l'heure qui est modifiée par les boutons. Lorsque MN clignote, c'est la valeur des minutes  qui est modifiée.
  • Le second point, situé entre HH et MN, clignote avec un battement toutes les secondes pour indiquer que l'horloge fonctionne normalement.
  • Le troisième point (au milieu de MN) allumé signale que l'horloge est en mode réglage de l'alarme.
  • Le quatrième point le plus à droite allumé indique que l'alarme est activée.
  • Lorsque l'alarme se déclenche les quatre digits HH.MN clignotent. 

Câblage du projet

Le câblage du projet Réveil-matin reprend le schéma de l'afficheur LED à quatre digit déjà présenté en y ajourant les boutons de réglage de l'horloge et le buzzer d'alarme.

Schéma électrique

  • L'afficheur 5641AS est piloté par les pins 2 (Data), 3 (Latch) et 4 (Clock) de l'Arduino via deux convertisseurs Série/Parallèle 74HC595 comme déjà évoqué dans l'article consacré à cette technique.
  • Les trois boutons de réglage de l'heure sont respectivement connectés aux pins 7, 8 et 9 de l'Arduino.
  • Un buzzer passif pour émettre le signal d'alarme est connecté sue la pin 10 de l'Arduino.

Montage sur la platine d'expérimentation

Programmation de la classe LedClockDisplay

La programmation du projet Horloge est toujours la même. Elle est toujours basée sur la classe Clock. Seul le système d'affichage est modifié en créant une nouvelle classe LedClockDisplay dérivée du système d'affichage générique ClockDisplay. On constate, une fois encore, que la programmation par objet en C++ assure un découplage parfait entre les différentes fonctions du projet car sans avoir à modifier la logique de l'application, on peut changer de toute l'électronique du projet en ne programmant que la classe relative à ce nouveau système, en dérivant une classe générique ClockDisplay qui embarque l'essentiel de la problématique d'affichage.

Définition de la classe LedClockDisplay

/* 1 */
#ifndef LedClockDisplay_h
#define LedClockDisplay_h

/* 2 */
#include "LedDisplayNDigits.h"
#include "ClockDisplay.h"

/* 3 */
class LedClockDisplay : public ClockDisplay {
/* 4 */
  LedDisplayNDigits __ledDisplay;

/* 5 */
  void __displayByte(uint8_t, uint8_t);
  void __clearByte(uint8_t);
  void __clearStatus();

  protected:
/* 6 */
  virtual void _displayTime();
  virtual void _displayStatus();
  virtual void _displayAlarm();
  virtual void _doBlink(bool);

  public:
/* 7 */
  LedClockDisplay(uint8_t, uint8_t, uint8_t);
/* 8 */
  virtual void begin();
  virtual void loop();
};

/* 1 */
#endif // LedClockDisplay_h
  1. Application de la bonne pratique de définir les classes d'une bibliothèque entre deux directive #ifndef et #endif en prévention de multiples définitions dans les projets composites.
  2. Inclusion des définitions des classes utilisées :
    • LedDisplayNDigits.h contient la définition de la classe LedDisplayNDigits utilisée pour gérer les modules LED avec des 74HC595. 
    • ClockDisplay.h est le fichier de définition présenté dans l'article traitant des dispositifs d'affichage du projet Horloge.
  3. La classe LedClockDisplay dérive la classe générique ClockDisplay. Elle hérite donc des trois méthodes publiques changeTime(), changeStatus() et changeAlarm() invoquées par la classe Clock. En revanche, elle doit implémenter les quatre méthode abstraites déclarées dans la classe de base.
  4. La classe LedClockDisplay utilise trois méthodes privées :
    • __displayByte() affiche la valeur de l'octet passé en paramètre sur les deux digits dont la position est également passée en paramètre. Pour HH, pos vaut 2. Pour MN, pos vaut 0.
    • __clearByte() éteint les deux digits à la position passée en paramètre. Pour HH, pos& vaut 2. Pour MN, pos vaut 0. Cette méthode est utilisée pour le clignotement.
    • Dans la classe LedClockDisplay, pour indiquer l'état dans lequel se trouve l'horloge, on utilise les points (led DP) des digits. __clearStatus() permet de les éteindre tous à la fois.
  5. L'attribut privé __ledDisplay contient une instance de la classe LedDisplayNDigits  qui permet de piloter les modules LED avec des 74HC595..
  6. Les quatre méthodes héritées de la classe  abstraite parente ClockDisplay, doivent être implémentées pour que la classe LedClockDisplay soit instanciable.
  7. L'interface publique de la classe concrète doit implémenter un constructeur. La classe LedClockDisplay utilise un module d'affichage LED à quatre digits via deux 74HC595. Cela nécessite trois pins de l'Arduino. Leurs numéros respectif doivent être passés au constructeur de la classe.
  8. La classe LedDispDisplayNDigits implémente les méthodes begin() et loop(). Pour que l'instance __ledDisplay fonctionne correctement, les méthodes begin() et loop() de la classe LedClockDisplay doivent respectivement invoquer ces méthodes pour tous les composants utilisés, donc sur l'instance __ledDisplay.

Implémentation de la classe SerialClockDisplay

L'implémentation d'une classe concrète pour un dispositif d'affichage de l'horloge consiste à :
  • Créer un constructeur pour la classe concrète. Celui-ci doit permettre de recevoir en paramètres les numéros des pins de l'Arduino utilisées par les composants électroniques qui composent le dispositif d'affichage.
  • Surcharger la méthode begin() pour initialiser les composants électroniques du dispositif d'affichage et notamment définir les pins de l'Arduino en lecture ou en écriture par l'invocation de la  fonction pinMode(). ou éventuellement invoquer la méthode begin() des composants utilisés.
  • Implémenter les quatre méthodes abstraites de la classe ClockDisplay pour définir comment sont afficher les différents éléments de l'horloge.
#include "LedClockDisplay.h"

/* 1 */
LedClockDisplay::LedClockDisplay(uint8_t dataPin, uint8_t latchPin, uint8_t clockPin)
  : ClockDisplay(), __ledDisplay(dataPin, latchPin, clockPin, 4)
{
}

/* 2 */
void LedClockDisplay::begin() {
  ClockDisplay::begin();
  this->__ledDisplay.begin();
}

/* 3 */
void LedClockDisplay::loop() {
  ClockDisplay::loop();
  this->__ledDisplay.loop();
}

/* 4 */
void LedClockDisplay::_doBlink(bool blinkStatus) {
  switch (this->_clockStatus) {
    case HOUR_SETUP:
    case ALARM_HOUR_SETUP:
      if (blinkStatus) {
        this->__displayByte(this->_time.HH(), 2);
      } else {
        this->__clearByte(2);
      }
      break;
    case MINUTE_SETUP:
    case ALARM_MINUTE_SETUP:
      if (blinkStatus) {
        this->__displayByte(this->_time.MN(), 0);
      } else {
        this->__clearByte(0);
      }
      break;
    case ALARM_ACTIVE:
      if (blinkStatus) {
        this->__displayByte(this->_time.MN(), 0);
        this->__displayByte(this->_time.HH(), 2);
      } else {
        this->__clearByte(0);
        this->__clearByte(2);
      }
      break;
    default:
      this->__ledDisplay.setDot(blinkStatus, 2); 
      break;
  }
}

/* 5 */
void LedClockDisplay::_displayTime() {
  this->__displayByte(this->_time.HH(), 2);
  this->__displayByte(this->_time.MN(), 0);
}

/* 6 */
void LedClockDisplay::_displayStatus() {
  this->__clearStatus();
  switch (this->_clockStatus) {
    case CLOCK_SETUP:
    case HOUR_SETUP:
    case MINUTE_SETUP:
      this->__ledDisplay.setDot(true, 3);
      break;
    case ALARM_SETUP:
    case ALARM_HOUR_SETUP:
    case ALARM_MINUTE_SETUP:
      this->__ledDisplay.setDot(true, 1);
      break;
  }
}

/* 7 */
void LedClockDisplay::_displayAlarm() {
  this->__ledDisplay.setDot(this->_alarmStatus, 0);
}

/* 8 */
void LedClockDisplay::__displayByte(uint8_t value, uint8_t pos) {
  this->__ledDisplay.displayDigit(value % 10, pos);
  this->__ledDisplay.displayDigit(value / 10, pos + 1);
}

/* 9 */
void LedClockDisplay::__clearByte(uint8_t pos) {
  this->__ledDisplay.clearDigit(pos);
  this->__ledDisplay.clearDigit(pos + 1);
  this->_displayStatus();
}

/* 10 */
void LedClockDisplay::__clearStatus() {
  for (int i = 1; i < 4; i++) {
    this->__ledDisplay.setDot(false, i);
  }
}
  1. La classe LedClockDisplay utilise la classe LedDisplayNDigits auquel il faut indiquer les trois pins de l'Arduino utilisées et le nombre de digits affichés. Ces numéros de pin doivent être fournis — dans l'ordre Data, Latch, CLock  — au constructeur de la classe LedClockDisplay qui les transmet en invoquant le constructeur de la classe LedDisplayNDigits sur l'attribut __ledDisplay, suivi du nombre de digits (4), après avoir invoqué le constructeur de la classe parente ClockDisplay.
  2. Dans la méthode begin(), il y a deux choses à effectuer :
    • Invoquer la méthode begin() de la classe parente ClockDisplay. Cela induit l'initialisation du timer de clignotement implémentée dans celle-ci.
    • Invocation de la méthode begin() sur l'instance __ledDisplay pour initialiser les afficheurs LED.
  3. Dans la méthode loop(), il y a deux choses à effectuer :
    • Invoquer la méthode loop()  de la classe parente ClockDisplay pour gérer le clignotement.
    • Invoquer la méthode loop()  sur l'instance __ledDisplay pour gérer l'activation alternative individuelle de chaque digit. 
  4. En mode réglage et lorsque le signal d'alarme est déclenché, l'affichage de l'heure doit clignoter. Le clignotement est implémenté par un timer défini dans la classe de base ClockDisplay qui va périodiquement (toutes les 500 ms en fait) invoquer la méthode _doBlink() en passant en paramètre alternativement la valeur true ou la valeur false. Lorsque le paramètre blinkStatus vaut true, l'heure est affichée normalement sur les digits par l'invocation de la méthode privée __displayByte(). Lorsqu'il vaut false, les digits sont éteints par l'invocation de la méthode privée __clearByte(). Si la donnée affichée est HH, la position passée en paramètre est 2. Si la donnée est MN, la position passée en paramètre est 0.
    • Dans les états HOUR_SETUP (réglage de la valeur HH de l'heure) et ALARM_HOUR_SETUP (réglage de la valeur HH du déclenchement de l'alarme), la valeur clignotante est HH.
    • Dans les états MINUTE_SETUP (réglage de la valeur MN de l'heure) et ALARM_MINUTE_SETUP (réglage de la valeur MN du déclenchement de l'alarme) la valeur clignotante est MN.
    •  Dans l'état ALARM_ACTIVE (lorsque le signal d'alarme se déclenche), la valeur clignotante est HH.MN.
    • En fonctionnement normal, seul le point séparant HH et MN clignote.
  5. La méthode _displayTime()  affiche l'heure contenue dans l'attribut protégé _time (déterminé par la classe Clock en invoquant la méthode publique changeTime()). L'affichage de l'heure est toujours effectué au format HH.MN
  6. Sur un afficheur à quatre digits, il est assez difficile d'indiquer l'état dans lequel se trouve l'horloge. Il serait possible d'utiliser les quatre broches disponibles du deuxième 74HC595 pour piloter des LED colorées. Mais la carte d'expérimentation du projet étant déjà surchargée, un chois arbitraire a été fait d'utiliser les points décimaux des digits (la led DP) en invoquant la méthode setDot() sur l'instance __ledDisplay.
    • Le point le plus à gauche (position 3) allumé indique que l'horloge est en mode réglage de l'heure (états CLOCK_SETUP, HOUR_SETUP et MINUTE_SETUP). 
    • Le point séparant HH et MN (position 2) clignotant indique que l'horloge est en mode normal.
    • Le point situé au milieu de MN (position 1) allumé indique que l'horloge est en mode réglage de l'alarme (états ALARM_SETUP, ALARM_HOUR_SETUP et ALARM_MINUTE_SETUP).
    • Le point situé le plus à droite ( position 0) allumé indique que l'alarme est activée.
  7. La méthode _displayAlarm() indique l'état d'activation de l'alarme (attribut _alarmStatus) en invoquant la méthode setDot() sur le digit de position 0 (le  plus à droite). Cela joue le même rôle que la petite cloche affichée sur l'écran LCD).
  8. La méthode __displayByte() affiche la valeur passée dans le paramètre value sur deux digits. Le paramètre pos indique s'il s'agit de MN (pos = 0) ou HH (pos = 2). value est décomposée en deux chiffres affichés sur deux digits consécutifs pos et pos+1 en invoquant la méthode displayDigit() sur l'instance __ledDisplay.
  9. La méthode __cleayByte() éteint les deux digits consécutifs à partir de la position pos passée en paramètre, lequel indique s'il s'agit de MN (pos = 0) ou HH (pos = 2). Pour cela elle invoque la méthode clearDigit() sur l'instance __ledDisplay. Cette dernière méthode éteint le digit complètement, y compris la led DP. Pour préserver l'affichage de l'état de l'horloge, une invocation de _displayStatus() est effectuée pour rétablir l'affichage correct des points décimaux.

Utilisation de la classe LedClockDisplay dans un sketch Arduino

L'utilisation du système d'affichage à base d'afficheurs à LED ne diffère pas beaucoup de l'affichage sur un écran LCD. Pour cela, il suffit d'instancier la bonne classe de système d'affichage, la classe LedCLockDisplay en lieu et place de la classe LCDClockDisplay.

#include "LedClockDisplay.h"
#include "ClockAlarm.h"
#include "Clock.h"


// Déclaration des pins de connexion des boutons de commande.
#define BTN_CMD 9
#define BTN_UP 7
#define BTN_DOWN 8
// Déclaration de la pin de connexion du buzzer passif.
#define BUZZER 10
// Déclaration des pin du 74HC595
#define IC_DATA 2
#define IC_LATCH 3
#define IC_CLOCK 4

LedClockDisplay clockDisplay(IC_DATA, IC_LATCH, IC_CLOCK);
BuzzerClockAlarm clockAlarm(BUZZER);
Clock myclock(clockDisplay, clockAlarm, BTN_CMD, BTN_UP, BTN_DOWN);


void setup() {
  myclock.begin();
}

void loop() {
  myclock.loop();
}

Conclusion

Cet article variante du projet Horloge qui utilise un afficheur à LED. D'autres systèmes d'affichage, tous dérivés de la classe générique ClockDisplay, pour ce projet, avaient déjà été présentés :
  • La classe SerialClockDisplay proposait un système d'affichage via le port série de l'Arduino, bien pratique pour déboguer le projet.
  • La classe LCDClockDisplay proposait un système d'affichage très convivial en utilisant un écran LCD piloté par le driver LiquidCrystal
Le système d'affichage de l'horloge basé sur la classe LedClockDisplay peut sembler moins convivial que l'usage d'un écran LCD. Il offre néanmoins l'avantage de ne mobiliser que 3 pins de l'Arduino contre les 6 nécessaire pour piloter l'écran LCD. Encore qu'il soit possible de remédier à cela pour ce dernier en l'interfaçant également par un convertisseur 74HC595 ou en utilisant une interface I2C. Cela fera l'objet de futurs articles. 

Commentaires

Posts les plus consultés de ce blog

Afficheur à LED 7 segments (classe SegmentLedDisplay)

Piloter un clavier matriciel sur le bus I2C avec un PCF8574

Utiliser Visual Studio Code pour développer sur Arduino