Les exercices de l'Atelier des Outils Pour le Numérique

Clignotement variable d'une LED

L'objectif de cet exercice est de piloter le clignotement d'une LED à l'aide d'un potentiomètre.

Des sous-exercices de difficulté graduelle vous sont proposés.

Montage associé

Pour cet exercice vous aurez besoin du matériel suivant :

TypeQuantité
platine Arduino1
Plaque à essais1
Fil de câblage6
Résistance de 330 Ω1
LED (couleur au choix)1
Potentiomètre1

Le montage à effectuer est celui de la figure ci-dessous :

Piloter le clignotement d'une LED avec un potentiomètre

Le signal carré

L'objectif de ce premier exercice est d'allumer une LED N millisecondes puis de l'éteindre N autres millisecondes et de répéter cela tout le temps. La valeur de N sera déterminée par la position du potentiomètre.

Indications

Correction et commentaires

#define LED 3

int pot ;
int cycle ;

void setup() {
  pinmode(LED, OUTPUT) ;
}

void loop() {
  pot = analogRead(A0) ;
  cycle = map(pot, 0,1023, 250, 2000) ;

  digitalWrite(LED, HIGH) ;
  delay(cycle) ;
  digitalWrite(LED, LOW) ;
  delay(cycle) ;
}
Exemple de code pour générer un signal carré.

Ici, c'est extrèmement simple, il suffit de lire la valeur du potentiomètre et de la recadrer via la fonction map() dans les bornes voulues pour N.

Ensuite, on allume la LED, on attend N millisecondes avec la fonction delay(N), on éteind la LED et on fait une dernière petite attente.

On pourrait remarquer que 1) lire la valeur du potentiomètre, 2) faire le recadrage avec map() et 3) allumer et éteindre la LED demande du temps. Du coup, la période totale de la fonction loop() est supérieure à 2×N. C'est tout à fait exact et si l'on devait réaliser une application où la précision est importante, il faudrait tenir compte de ces durées et les soustraire aux valeurs passées à la fonction delay().

Le pulsar

D'après Wikipedia « Un pulsar est un objet astronomique produisant un signal périodique allant de l'ordre de la milliseconde à quelques dizaines de secondes. Ce serait une étoile à neutrons tournant très rapidement sur elle-même (période typique de l'ordre de la seconde, voire beaucoup moins pour les pulsars milliseconde) et émettant un fort rayonnement électromagnétique dans la direction de son axe magnétique. »

Nous allons simuler cela en faisant clignoter une LED très rapidement toutes les N secondes. La valeur de N étant déterminée par la position du potentiomètre.

Indications

Correction et commentaires

#define LED 3

int pot ;
int cycle ;

void setup() {
  pinmode(LED, OUTPUT) ;
}

void loop() {
  pot = analogRead(A0) ;
  cycle = map(pot, 0,1023, 1000, 5000) ;

  digitalWrite(LED, HIGH) ;
  delay(100) ;
  digitalWrite(LED, LOW) ;
  delay(cycle-100) ;
}
Exemple de code pour générer un signal type pulsar.

Dans cet exercice la solution est extrèmenent proche de l'exercice précédent. Ce qui change c'est le rapport cycleque du signal (le pourcentage du temps où la LED est allumée par rapport au pourcentage du temps où la LED est éteinte).

La première partie de la période est fixe (100 ms). La seconde partie doit donc être adaptée pour qu'en tout, la période fasse N ms, soit N - 100 ms.

Nous changeons également le recadrage de la valeur du potentiomètre pour que les valeurs répondent à ce qui est demandé pour cet exercice.

Enfin, nous pourrions faire la même remarque qu'à l'exercice précédent sur la pécision du programme par rapport au temps. Il suffirait alors d'appliquer les mêmes correctifs.

Le signal triangulaire (en dents de scie)

Nous reprenons l'image du signal carré en augmentant sensiblement la difficulté.

Il s'agit à présent de réaliser un signal en dents de scie. C'est à dire que, sur la première partie de N millisecondes, la LED ne sera plus simplement allumée, mais son intensité devra croître en passant de complètement éteinte à complètement allumée. Sur la seconde partie, toujours de N millisecondes, l'intensité de la LED devra décroître pour passer de complètement allumée à complètement éteinte.

Indications

Correction et commentaires

#define LED 3
#define POT A0

int potVal ;
int cycle ;
int delta ;
int pas ;

void setup() {
  pinmode(LED, OUTPUT) ;
}

void loop() {
  potVal = analogRead(A0) ;
  cycle = map(pot, 0,1023, 250, 2000) ;

  delta = cycle / 255 ;
  for (pas=0 ; pas<255 ; pas++) {
    analogWrite(LED, pas) ;
    delay(delta) ;
  }
  for (pas=255 ; pas>=0 ; pas--) {
    analogWrite(LED, pas) ;
    delay(delta) ;
  }
}
Exemple de code pour générer un signal en dents de scie. Version linéaire.

Ici la solution est un peu plus compliquée que précédemment. En effet, nous devons faire varier l'intensité de la LED de son minimum à son maximum sur un temps de N ms.

Pour parvenir à ce résultat, nous pouvons diviser le temps de cette demi-période en 255 pas. Chacun aura donc une valeur de N÷255 ms.

Ensuite, il faut, à chaque pas, allumer la LED un peu plus ou un peu moins, en fonction de la demi-période sur laquelle nous nous trouvons (montée ou descente de la dent de scie).

Visuellement le résultat n'est pourtant pas complètement satisfaisant. Ceci est du au fait que notre œil n'a pas une perception linéaire. Il faut donc adapter la luminosité de la LED en fonction de ça. C'est ce qui est proposé dans le code ci-dessous.

La correction logarithmique est absolument empirique, mais donne un résultat plutôt convaincant. La courbe de réponse est celle présentée sur la figure ci-dessous.

Courbe correspondant à la réponse de la LED.
#define LED 3
#define POT A0

int potVal ;
int cycle ;
int delta ;
int pas ;

int correction[256] ;

void setup() {
  pinmode(LED, OUTPUT) ;

  for (pas=1 ; pas<257 ; pas++) {
    correction[256-i] = (int)(255-255*log(i)/log(256)) ;
  }
}

void loop() {
  potVal = analogRead(A0) ;
  cycle = map(pot, 0,1023, 250, 2000) ;

  delta = cycle / 255 ;
  for (pas=0 ; pas<255 ; pas++) {
    analogWrite(LED, correction[pas]) ;
    delay(delta) ;
  }
  for (pas=255 ; pas>=0 ; pas--) {
    analogWrite(LED, correction[pas]) ;
    delay(delta) ;
  }
}
Exemple de code pour générer un signal en dents de scie. Version corrigée pour donner une réponse logarithmique.

Dans ce code, nous tabulons la correction (c'est le rôle de la boucle for dans la fonction setup()). Calculer des logarithmes pouvant être éprouvant pour de petites architectures comme l'Arduino, le calcul est fait une fois pour toute au début et les résultats sont stockés dans un tableau. Nous aurions également pu faire faire les calculs par notre ordinateur et ne stocker que les valeurs finales dans ledit tableau. L'Arduino n'aurait de fait pas eu de calcul à faire et donc, économiserait de l'énergie.

Ces valeurs sont simplement utilisées dans la fonction analogWrite() en lieu et place du compteur pas.

Visuellement nous observons un plus grand nombre de pas vers les valeurs de forte luminosité, là où avant la saturation arrivait très tôt. Ce résultat n'est pas parfait, mais est plus convaincant que le précédent non corrigé.

Pour aller plus loin

Pour réaliser les exercices ci-dessus vous avez sans doute utilisé la fonction Arduino delay(). Vous aurez donc remarqué que les changements de position du potentiomètre ne sont pas pris en compte directement (c'est surtoût observable pour les valeurs de N élevées).

En vous inspirant de l'exemple Arduino "BlinkWithoutDelay" (à trouver dans le menu Fichier/Exemples/02.Digital) réécrivez les programmes ci-dessus en utilisant un modèle non bloquant.

Correction et commentaires

#define LED 3
#define POT A0

int potVal ;
int cycle, delta ;
int pas, sens ;
unsigned long dernierePeriode ;

int correction[256] ;

void setup() {
  pinmode(LED, OUTPUT) ;

  for (pas=1 ; pas<257 ; pas++) {
    correction[256-i] = (int)(255-255*log(i)/log(256)) ;
  }

  dernierePeriode = millis() ;
}

void loop() {
  potVal = analogRead(A0) ;
  cycle = map(pot, 0,1023, 250, 2000) ;

  delta = cycle / 255 ;
  if ( (millis() - dernierePeriode) > delta ) {
    dernierePeriode = millis() ;
    analogWrite(LED, correction[pas]) ;
    if ( pas > 255 ) sens = -1 ;
    else if (pas < 0 ) sens = 1 ;
    pas += sens ;
  }
}
Version non bloquante du signal en dents de scie corrigé.

Ici, le principe est le même que celui de l'exemple Arduino BlinkWithoutDelay. Dans la boucle principale, on ne fait l'action de changer la valeur d'intensité de la LED que toutes les delta millisecondes. Il n'y a plus de fonction delay(), ce qui permet de faire autre chose entre deux modifications de la valeur de luminosité de la LED.

Pour pouvoir réaliser cela, il est nécessaire de se souvenir de moment où la dernière action a eu lieu. C'est pour cela qu'a été introduite la variable dernierePeriode. Elle est remise à jour à chaque fois que la valeur de luminosité de la LED est modifiée.

Une excellente illustration de la possibilité de faire autre chose est la prise en compte immédiate du changement de la valeur du potentiomètre. Dans les exemples des sections précédentes, la prise en compte du changement de la valeur du potentiomètre n'arrivait qu'une fois une période complète passée. Ici, c'est quasiment immédiat.

Cette façon de faire, sans utiliser de fonction bloquante, sera très utile dans les programmes où la prise en compte rapide de plusieurs entrées, ou de plusieurs sorties sont nécessaires. Il est donc important de bien comprendre ce fonctionnement.