Ardu? No!Les boutons ≫ Une touche est appuyée?

Une touche est appuyée
(un bouton par broche)?

Voir la page Un bouton par broche qui décrit ce montage.

C'est un grand classique car c'est presque le plus simple. Je dis presque car il faut que les appels ne soient pas trop espacés sinon on manquerait des appuis, ou un délai trop important interviendrait entre l'appui et l'action que cela engendre. On appelle l'instruction ou la fonction qui lit l'état du ou des boutons.

Avec un seul bouton

Voici par exemple un programme qui allume une led avec un poussoir, mais avec une fonction va et vient (un appui allume la led, un appui l'éteint):

const byte BOUTON = 2; // Pin2 -- bouton -- GND
const unsigned long BOUNCE = 20; // 20ms pour attendre la fin des rebonds

byte etat = HIGH; // Permettra de voir les changements
unsigned long tempsDuDernierAppui; // Pour l'anti-rebond

void setup()
{
  pinMode(BOUTON, INPUT_PULLUP); // Un appui, j'allume, un appui j'éteins
  pinMode(LED_BUILTIN, OUTPUT); // Led qui va s'allumer ou éteindre
}

void loop()
{
  if ((digitalRead(BOUTON) != etat) && (millis() - tempsDuDernierAppui > BOUNCE)) // Si on a un changement et hors rebonds
  {
    etat = etat == LOW ? HIGH : LOW; // On mémorise le nouvel état
    tempsDuDernierAppui = millis(); // Départ pour l'anti-rebond
    if (etat == LOW) digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ==LOW ? HIGH : LOW); // Change la led si on appuie sur le bouton
  }
}

L'instruction qui lit l'état du bouton digitalRead(BOUTON) est très courte. Dans ce cas il est impossible de faire mieux lors d'un appel régulier.

Avec plusieurs boutons

Par contre si on a plusieurs boutons, câblés sur un même port, la lecture globale du port permet avec une comparaison de savoir si un des boutons est appuyé ou pas. Avec une Uno, le port D se trouve sur les broches 0 à 7. En réservant les broches 0 et 1 pour la programmation et le dialogue, on peut mettre 6 boutons sur les sorties 2 à 7 (PD2 à PD7). La lecture globale de PORDD nous dira si un bouton a changé d'état ou pas. Si c'est le cas, on a perdu un peu de temps, mais si ce n'est pas le cas, on en gagne beaucoup. Voici le même programme si on utilise 6 boutons (explications après):

const byte BOUTON2 = 2; // Pin2 -- bouton -- GND
const byte BOUTON3 = 3; // Pin3 -- bouton -- GND
const byte BOUTON4 = 4; // Pin4 -- bouton -- GND
const byte BOUTON5 = 5; // Pin5 -- bouton -- GND
const byte BOUTON6 = 6; // Pin6 -- bouton -- GND
const byte BOUTON7 = 7; // Pin7 -- bouton -- GND
const unsigned long BOUNCE = 20; // 20ms pour attendre la fin des rebonds

byte pinD; // Lecture mémorisée (pour avoir la même valeur pour une loop())
byte etat = 0b11111100; // Permettra de voir les changements sur un des 6 boutons
byte etat2 = 0b00000100; // Pour le bouton 2, "HIGH" sur le bit de poids 2 
unsigned long tempsDuDernierAppui2; // Pour l'anti-rebond du bouton 2

void setup()
{
  pinMode(BOUTON2, INPUT_PULLUP); // Un appui, j'allume, un appui j'éteins
  pinMode(BOUTON3, INPUT_PULLUP); // D'autres boutons, d'autres applications non détaillées ici
  pinMode(BOUTON4, INPUT_PULLUP);
  pinMode(BOUTON5, INPUT_PULLUP);
  pinMode(BOUTON6, INPUT_PULLUP);
  pinMode(BOUTON7, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT); // Led qui va s'allumer ou éteindre
}

void loop()
{
  pinD = PIND & 0b11111100; // Valeurs binaires des 6 boutons
  if (pinD != etat) // On a un changement sur un des 6 boutons
  { // Ne sera fait que si on a un changement
    etat = pinD; // Prend alors la valeur lue

    // Pour le bouton 2:
    if (((etat & 0b00000100) != etat2) && (millis() - tempsDuDernierAppui2 > BOUNCE)) // Si on a un changement et hors rebonds
    {
      etat2 = etat & 0b00000100; // On mémorise le nouvel état
      tempsDuDernierAppui2 = millis(); // Départ pour l'anti-rebond
      if (etat2 == 0) digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ==LOW ? HIGH : LOW); // Change la led si on appuie sur le bouton 2
    }
    // Pour le bouton 3
    /* Code pour la gestion d'un changement pour ce bouton */

    // Pour le bouton 4
    /* Code pour la gestion d'un changement pour ce bouton */

    // Pour le bouton 5
    /* Code pour la gestion d'un changement pour ce bouton */

    // Pour le bouton 6
    /* Code pour la gestion d'un changement pour ce bouton */

    // Pour le bouton 7
    /* Code pour la gestion d'un changement pour ce bouton */
  }
}

Il ne faut utiliser dans la boucle qu'une seule fois la lecture via PIND, il faut donc la mémoriser. Si on la lisait deux fois, à cause des rebonds, on pourrait avoir 2 lectures différentes.

Le bouton 2 est branché sur le port D à la position 2 (c'est pareil pour les chiffres 3 à 7). Pour la lecture:

pinD = PIND & 0b11111100; // Valeurs binaires des 6 boutons

On masque les bits PD0 et PD1 qui servent à la liaison série. Ainsi, les 6 premiers bits indiquent chacun l'état d'un bouton et les deux derniers seront toujours à 0. Si on avait mis les boutons sur les broches A0 à A5 (PORT C), on n'aurait pas eu besoin des masques.

etat & 0b00000100 isole le bit 2, c'est à dire retourne sur le poids 22 un bit à 1 si le bouton n'est pas appuyé et un bit à 0 si il l'est. Et c'est cette valeur que l'on va garder pour les prochaines fois.

Bilan temporel

Voici le code utilisé si aucun bouton n'est appuyé (temps t):

  pinD = PIND & 0b11111100; // Valeurs binaires des 6 boutons
  if (pinD != etat) // On a un changement sur un des 6 boutons
  {
  }

Voici maintenant une idée code exécuté si que l'on ferait si on n'utilisait pas l'idée "un bouton est-il appuyé?" (temps T):

    // Pour le bouton 2:
    if (((etat & 0b00000100) != etat2) && (millis() - tempsDuDernierAppui2 > BOUNCE)) // Si on a un changement et hors rebonds
    {
    }

    // Pour le bouton 3:
    if (((etat & 0b00000100) != etat3) && (millis() - tempsDuDernierAppui3 > BOUNCE))
    {
    }

    // Pour le bouton 4:
    if (((etat & 0b00000100) != etat4) && (millis() - tempsDuDernierAppui4 > BOUNCE))
    {
    }

    // Pour le bouton 5:
    if (((etat & 0b00000100) != etat5) && (millis() - tempsDuDernierAppui5 > BOUNCE)) 
    {
    }

    // Pour le bouton 6:
    if (((etat & 0b00000100) != etat6) && (millis() - tempsDuDernierAppui6 > BOUNCE))
    {
    }

    // Pour le bouton 7:
    if (((etat & 0b00000100) != etat7) && (millis() - tempsDuDernierAppui7 > BOUNCE))
    {
    }

On peut comprendre que c'est nettement plus rapide de faire systématiquement que le premier code et rarement les deux ensemble.