Les entrées/sorties numériques

Configurer une sortie, connecter une LED, l’allumer...

On s'intéresse ici aux entrées/sorties numériques. Dès qu'on a besoin de connecter un organe extérieur à un microcontrôleur, on a souvent recours à de simples entrées/sorties numériques.

La vidéo suivante traite d'une première étape, celle qui consiste à configurer une sortie, lui connecter une LED, l'allumer et la faire clignoter.

Le code présenté dans la vidéo :

#define PIN_LED_ROUGE 3

void setup() {
  // put your setup code here, to run once:
  pinMode(PIN_LED_ROUGE, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(PIN_LED_ROUGE, HIGH);
  delay(500);
  digitalWrite(PIN_LED_ROUGE, LOW);
  delay(500);
}

Configurer une entrée, utiliser un bouton poussoir...

Avant de connecter un signal numérique à l'entrée de la carte Arduino, il me semble intéressant d'avoir un aperçu de la structure interne des entrées d'un microcontrôleur. C'est l'objet de la vidéo suivante qui proposera pour finir de recopier l'état du bouton poussoir sur une LED et de visualiser les rebonds qui apparaissent au moment du relachement du bouton poussoir.

Remarque concernant les niveaux d'entrées : le niveau VCC/2 pour caractériser l'état logique d'une entrée est une présentation simplificatrice. Pour un ATmega328P alimenté entre 2.4V et 5.5V, le niveau bas est garanti entre -0.5V et 0.3xVCC; le niveau haut l'est entre 0.6xVCC et VCC+0.5 . Entre 0.3VCC et 0.6VCC, le niveau logique peut donc être mal interprété.

Le code présenté dans la vidéo :

#define PIN_PB 2
#define APPUYE LOW
#define PIN_LED_ROUGE 3

void setup() {
  pinMode(PIN_PB, INPUT_PULLUP);
  pinMode(PIN_LED_ROUGE, OUTPUT);
} 

void loop() {
  /*if (digitalRead(PIN_PB) == APPUYE) digitalWrite(PIN_LED_ROUGE, HIGH);
  else digitalWrite(PIN_LED_ROUGE, LOW);*/
  digitalWrite(PIN_LED_ROUGE, !digitalRead(PIN_PB));
}

Réagir à l'appui sur un bouton poussoir. Eviter les rebonds

Dans cette section, il s'agit d'apprendre à détecter les fronts montants et descendants d'un signal et, lorsqu'il s'agit du signal issu d'un bouton poussoir, de filtrer les rebonds afin d'éviter la prise en compte des impulsions intempestives dont ils sont à l'origine.

Notes importantes :

  • on travaille pour le moment sans utiliser les interruptions,
  • il est également possible de supprimer les rebonds en mettant en place un condensateur aux bornes du bouton poussoir.

Le code présenté dans la vidéo :

#define PIN_BP 2
#define PIN_LED_ROUGE 3
#define DELAI_SANS_REBOND 10000

void setup() {
  // put your setup code here, to run once:
  pinMode(PIN_LED_ROUGE, OUTPUT);
  pinMode(PIN_BP, INPUT_PULLUP);
}

void loop() {
  // Début l'exécution de la boucle
  static uint8_t dernierBP = HIGH;
  uint8_t BP = digitalRead(PIN_BP);
  static unsigned long derniereTransition = 0;
  unsigned long _micros;

  // Présence d'un front
  if (dernierBP != BP) {
    _micros = micros();
    if ((_micros - derniereTransition) >= DELAI_SANS_REBOND) {
      // Présence d'un front descendant
      if (BP == LOW) {
        digitalWrite(PIN_LED_ROUGE, !digitalRead(PIN_LED_ROUGE));
      }
    }
    derniereTransition = _micros;
  }
  // Fin de l'exécution de la boucle
  dernierBP = BP;
}

Programmer les sorties en accédant aux registres internes du microcontrôleur

Il s'agit maintenant de s'intéresser à la programmation des entrées/sorties en accédant directement aux registres du microcontrôleur. Dans de nombreux cas, les fonctions Arduino comme pinMode, digitalWrite ou digitalRead font parfaitement leur office. Lorsque la consommation devient un critère important, on peut en revanche préférer un accès direct aux registres de façon à proposer un code plus concis, à l'exécution plus rapide et donc moins consommateur en énergie comme c'est le cas, par exemple, pour programmer le thermomètre Arduino proposé dans les projets.

Dans une première vidéo, je vous propose de découvrir comment exploiter les entrées/sorties digitales de l'ATmega328 par les registres. La vidéo suivante montre le gain de temps obtenu en faisant le choix d'une commutation des sorties par un accès direct via les registres plutôt que par la fonction digitalWrite Arduino. 

Code utilisé dans la vidéo :

#include <Arduino.h>

void setup() {
  // PD2 pour le bouton poussoir => entrée avec résistance de pullup
  // PD3 pour la LED => sortie
  DDRD &= ~(1<<DDD2);     // Configuration en entrée de PORTD2
  bitSet(DDRD, DDD3);     // Configuration en sortie de PORDT3
  bitSet(PORTD, PORTD2);  // Activation de la résistance de pullup
}

void loop() {
  // Bouton appuyé alors on allume la LED sinon on éteint la LED
  if (bitRead(PIND, PIND2) == 0) bitSet(PORTD, PORTD3); else bitClear(PORTD, PORTD3);
}

Code de la vidéo présentée ci-dessus :

#include <Arduino.h>

void sortiesDirectes() {
  u32 time1, time2, time3;
  time1 = micros(); 

  // 50 transitions pour augmenter la précision de la mesure temporelle
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);

  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);

  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);

  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);

  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);

  // Mesure du temps pris par la lecture de micros()
  time2 = micros();
  time3 = micros();

  Serial.print("Délai pour 1 sortie directe : ");
  Serial.println((time2 - time1 - (time3 - time2))/50.0);
}

void sortiesViaDigitalWrite() {
  u32 time1, time2, time3;
  time1 = micros();

  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);

  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);

  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);

  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);

  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);

  time2 = micros();
  time3 = micros();

  Serial.print("Délai pour 1 digitalWrite : ");
  Serial.println((time2 - time1 - (time3 - time2))/50.0);
}

void setup() {
  Serial.begin(9600);
  delay(100);
  pinMode(8, OUTPUT);
  sortiesDirectes();
  sortiesViaDigitalWrite();
  delay(500);
  noInterrupts();
}

void loop() {
  // Impulsion à 0 pour déclencher l'oscilloscope
  digitalWrite(8, 0);
  delayMicroseconds(20);

  // Salve à la Arduino
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);
  digitalWrite(8, 0);
  digitalWrite(8, 1);

  // Salve par un accès direct aux registres
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
  bitClear(PORTB, PORTB0);
  bitSet(PORTB, PORTB0);
}