User Tools

Site Tools


in202:seance_6:jalon_4

This is an old revision of the document!


Jalon 4: Ajouter à la classe Button la réactivité

Retour à la séance 6

Dans l'exemple précédent, nous appelons une fonction externe à la classe Button. En fait, nous aimerions avoir une classe Button à laquelle nous pourrions ajouter le comportement que doit avoir l'objet quand un événement se produit, comme par exemple appuyer sur le bouton ou relâcher le bouton.

Question n°1

Ajouter trois méthodes virtuelles à votre classe Button qui correspondent à :

  • OnPressed() : on appuie sur le bouton.
  • OnReleased() : on relâche le bouton.
  • OnChanged() : soit le bouton a été appuyé, soit le bouton a été rélâché.

ces méthodes sont vides.

Question n°2

La difficulté vient du fait que la méthode attachInterupt ne passe aucune information à la méthode ISR qui est appelé. En conséquence, c'est le nom de la méthode qui est associé avec l'interruption qui permet de savoir quel est le port et qu'elles sont les actions à effectuer.

Si nous supposons monitoré l'ensemble des 13 ports de la carte DUE, nous devrions écire trois fonctions pour chacun des ports :

void OnPressedPort1();
void OnReleasedPort1();
void OnChangedPort1();

et de même pour le port 2, 3, ….

On va profite des objets pour simplifier l'écriture. Pour ce faire, nous allons définir une classe interne de la classe button qui va permetter de fournir les trois fonctions de base Pressed corresspondant à l'événement le bouton a été pressé, Released que le bouton a été relâché, Changed que l'état du bouton a changé. Ces fonctions vont simplement appelé pour le bouton si un bouton est présent sur le port donné la méthode virtuelle OnPressed(), (resp. OnReleased() ou OnChanged()) défini dans la classe Button.

class Button
{
private:
    struct EventHandler
    {
    public:
        static Button* MonitoredButton;
        static void Pressed()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnPressed();
        }
        static void Released()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnReleased();
        }
        static void Changed()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnReleased();
        }
    };

Ceci va donc nous créer nos 3 fonctions Pressed, Released et Changed de type void interruptHandler() que nous pouvons passer à la fonction attachInterupt.

Maintenant, il faut en créer 13. Comment faire ? Simplement en ajoutant un tableau statique correspondant aux 13 ports que nous pouvons monitorer et créant pour chacun des port une classe de type EventHandler.

public class Button
{
class Button
{
private:
    struct EventHandler
    {
    public:
        static Button* MonitoredButton;
        static void Pressed()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnPressed();
        }
        static void Released()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnReleased();
        }
        static void Changed()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnReleased();
        }
    };
 
    static EventHandler m_EventHandlers[14];
}

Un champ static est un champ qui est partagé par tous les objets Button, il n'y a qu'un seul m_EventHandlers qui est commun à tous les les objets Button.

Modifier le constructeur pour que chaque fois que l'on crée un nouveau Button qui est associé au port portNumber, on pense à modifier le tableau m_EventHandlers pour qu'il sache que le nouveau Button créé est associé au port portNumber, donc que l'objet situé à l'indice désigne par le portNumber sache qu'il doit appelé les actions définies dans l'objet nouvellement créé.

Typiquement, nous allons avoir un constructeur de type suivant :

public class Button
{
private:
    struct EventHandler
    {
    public:
        static Button* MonitoredButton;
        static void Pressed()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnPressed();
        }
        static void Released()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnReleased();
        }
        static void Changed()
        {
          if(MonitoredButton != NULL)
            MonitoredButton->OnChanged();
        }
    };
 
    static EventHandler m_EventHandlers[14];
public:
    Button(uint32_t thePortNumber, const char* theName, bool isDebug = false):
        CustomComponent(theName, isDebug)
    {
        ...
        m_EventHandlers[thePortNumber].MonitoredButton = this;
    }
    ...
};

Question n°3

Nous considérons les deux événements que nous définissons dans le type enum suivant qui définit deux valeurs PRESSED indiquant que le bouton vient d'être enfoncé et RELEASED indiquant que le bouton est relâché.

    enum button_event { PRESSED = 0x1, RELEASED = 0x2 };

Nous souhaitons créer une fonction qui active l'interruption ou les interruptions nécessaires pour chacun des événements identifiés par PRESSED et RELEASED.

Typiquement, l'action de MonitorEvents(PRESSED) d'activer le moniteur d'interruption suivant :

    attachInterrupt(m_PortNumber, mEventHandles[m_PortNumber].Pressed, RISING);

Compléter le code de la classe.

class Button
{
public:
    void MonitorEvents(button_event theEvents)
    {
    ...
    }
}

Question n°4

Pour définir un bouton qui implante une action quand l'utilisateur appuie sur le bouton, il suffit de surcharger désormais dans une classe dérivée la méthode OnPressed().

Vous pouvez désormais écrire :

class LedButton: Button { private:

  Led mLed;

public:

  LedButton(uint32_t thePortNumber, Led& theLed): Button(thePortNumber, "LedButton", false)
  {
      MonitorEvents(PRESSED);
  }
  void OnPressed()
  {
      if(mLed.IsSwitchOn())
          mLed.SwitchOff();
      else
          mLed.SwitchOn();
  }

};

in202/seance_6/jalon_4.1619956514.txt.gz · Last modified: 2021/05/02 11:55 by bmonsuez