5. Les entrées: interaction avec l'Arduino


Maintenant que nous sommes capables d'utiliser les différentes sorties de l'Arduino et de contrôler, par exemple, l'allumage de LEDs, intéressons-nous aux entrées.

Il y a deux types d'entrées sur un Arduino: le type analogique et le type numérique. Nous nous consacrerons pour l'instant à la description du type numérique puisqu'il est plus simple à comprendre et suffisant pour nos applications. En quelques mots, une entrée analogique peut "lire" des valeurs de tension comprises entre 0 et 5V. Nous pourrions ainsi détecter la position d'un potentiomètre... Une entrée numérique, quant à elle, ne détecte que deux états: 0 ou 5V, soit LOW ou HIGH.

Identifions les ports d'entrées numériques sur l'Arduino... Et bien il s'agit des mêmes ports que les sorties numériques que nous avons utilisés dans les exemples précédents! En effet, chaque port physique peut être utilisé comme une entrée ou une sortie. C'est au moment de l'écriture du programme que nous définissons l'usage d'un port E/S.

Nous avons vu dans les exemples précédents la définition d'un port en mode sortie en utilisant l'instruction pinMode( 3, OUTPUT ). Dans le cas d'une entrée, il suffit d'utiliser l'instruction pinMode( 3, INPUT ) et le tour est joué... Le port 3 est maintenant une entrée.

Remarque: par défaut, un port numérique est défini comme une entrée. Il ne devrait donc pas être nécessaire d'utiliser l'instruction précédente. Cependant, pour des questions d'apprentissage et afin d'acquérir une bonne pratique de programmation, il est préférable de définir en tout temps la fonction des ports utilisés dans un programme (dans la fonction setup()).




5.1. Lecture d'une entrée

Commençons par le petit montage suivant, constitué de deux LEDs et de leur résistance de protection, ainsi que d'un bouton poussoir. La LED rouge est connectée au port de sortie 4 et la LED verte au port de sortie 5, comme nous l'avons vu dans les premiers chapitres. Maintenant, nous ajoutons un bouton poussoir, dont une patte est connectée au port d'entrée 8 et une patte au port +5V. Ainsi, lorsque le bouton est pressé, le port 8 est en contact avec le +5V: il est à l'état HIGH.


Lors de l'utilisation d'un port numérique en mode sortie, nous avons vu comment envoyer un signal HIGH (ou LOW) sur le port 8 en utilisant l'instruction digitalWrite( 8, HIGH ).

De la même façon, nous pouvons lire un état sur un port d'entrée en utilisant simplement l'instruction digitalRead( 8 ), qui retournera la valeur lue. Ainsi, le petit programme suivant permet de lire l'état de l'entrée 8 et d'allumer une LED en fonction du résultat...

int entree = 8;
int ledRouge = 4;
int ledVerte = 5;

void setup()
{
  pinMode( entree, INPUT );
  pinMode( ledRouge, OUTPUT );
  pinMode( ledVerte, OUTPUT );
}

void loop()
{
  if ( digitalRead( entree ) == HIGH )
  {
    digitalWrite( ledVerte, HIGH );
    digitalWrite( ledRouge, LOW );
  }
  else
  {
    digitalWrite( ledVerte, LOW );
    digitalWrite( ledRouge, HIGH );
  }
}

Le fonctionnement "attendu" de ce programme est:
  • si le bouton est pressé, alors la led verte s'allume
  • sinon (le bouton est relâché) la led rouge s'allume.
Cependant, ce n'est pas ce que nous obtenons... (Je vous encourage à effectuer le montage et insérer le code source précédent pour vérifier ce qu'il se passe) L'état de l'entrée semble incertain et l'allumage des LEDs ne semble pas correspondre à l'état du bouton poussoir. Ceci est dû au fait que la tension lue sur le port d'entrée n'est pas tout-à-fait HIGH... Nous allons voir comment corriger ce problème dans le chapitre suivant.




5.2. Stabilisation d'une entrée: la résistance "pull-up"

Comme il n'est pas facile de détecter exactement l'état HIGH (5V) sur une entrée, il est préférable de détecter l'état contraire, c'est-à-dire l'état LOW (0V), et inverser la logique du programme. Pour cela, nous introduisons la notion de résistance de "pull-up".

La signification de cette résistance est simple: lorsque ce mode est activé, une résistance (interne) est automatiquement montée en série sur le port d'entrée et connectée au 5V. Ainsi, en tout temps, l'état de l'entrée par défaut est HIGH...

Il suffit alors de comparer l'état à LOW (0V) pour être sûr que l'état a changé, et ce, avec précision. En effet, la valeur 0V ou ground, est toujours définie avec justesse, contrairement à l'état HIGH (5V).

Le montage doit être légèrement modifié, en reliant le bouton poussoir au ground (0V) plutôt qu'au +5V:


Le code source est aussi modifié, en remplaçant le test par une comparaison à LOW au lieu de HIGH:

void loop()
{
  if ( digitalRead( entree ) == LOW )
...

Cependant, il ne faut pas oublier d'activer la résistance interne. Pour cela, il suffit d'envoyer une commande au port d'entrée au moment de son initialisation: digitalWrite( entree, HIGH ). La fonction setup() devient alors:

void setup()
{
  pinMode( entree, INPUT );
  digitalWrite( entree, HIGH );
...

Le reste du programme est inchangé. Maintenant, le fonctionnement de notre montage a gagné en stabilité: le changement d'état est correctement détecté!


Mais nous n'en avons pas tout à fait fini avec ce montage. Il peut arriver que l'utilisation d'un bouton poussoir puisse aussi mener à une autre source d'instabilité, mais cette fois d'ordre mécanique. En effet, un tel bouton est simplement fait de deux lamelles qui se touchent. En relâchant légèrement la pression sur le bouton, celui-ci peut "ouvrir" le circuit momentanément et créer un état HIGH (puisque l'entrée n'est plus connectée au 0V). Nous allons voir comment corriger logiciellement cette imperfection.




5.3. Fiabiliser l'utilisation d'un bouton poussoir

Certes, cet exemple semble consacré uniquement à l'utilisation d'un bouton poussoir... Mais la méthode que je vais introduire maintenant est très utile dans d'autres situations, et doit devenir une bonne pratique de programmation pour les prochains projets que le lecteur voudra réaliser...

Reprenons l'exemple précédent:

int entree = 8;
int ledRouge = 4;
int ledVerte = 5;

void setup()
{
  pinMode( entree, INPUT );
  digitalWrite( entree, HIGH );
  pinMode( ledRouge, OUTPUT );
  pinMode( ledVerte, OUTPUT );
}

void loop()
{
  if ( digitalRead( entree ) == LOW )
  {
    digitalWrite( ledVerte, HIGH );
    digitalWrite( ledRouge, LOW );
  }
  else
  {
    digitalWrite( ledVerte, LOW );
    digitalWrite( ledRouge, HIGH );
  }
}

Nous avons vu dans les premiers chapitres que la fonction loop() est appelée et exécutée en permanence et très rapidement. Donc on imagine très bien que l'état de l'entrée est examinée de nombreuses fois par seconde. Ainsi, le simple fait de relâcher très légèrement le bouton peut conduire à un changement d'état.

Par exemple, si le bouton poussoir est utilisé pour changer la position d'un signal, il peut arriver que celui-ci change plusieurs fois d'état alors que la pression sur le bouton n'est pas constante... Dans le cas d'une commande de signal, cela peut devenir problématique pour la gestion du réseau...

Introduisons la notion de "debounce" ou amortissement. L'idée est bien simple: il suffit d'informer le programme que le bouton est relâché uniquement lorsque celui-ci l'est effectivement! Vous allez me dire que c'est un peu idiot de formuler cela ainsi, mais vous allez le comprendre dans l'exemple qui suit...

Normalement, nous détectons un changement d'état ainsi:

if ( digitalRead( entree ) == LOW )
{
  // Action...
}

Nous allons introduire un amortissement pour être sûr que le bouton est bien relâché avant d'exécuter l'action voulu: il suffit d'attendre un peu plus longtemps... ce que nous écrivons ainsi:

if ( digitalRead( entree ) == LOW )
{
  delay( 100 );
  while ( digitalRead( entree ) == LOW )
  {
    delay( 100 );
  }
  // Action...
}

Si nous interprétons les lignes de code précédentes nous obtenons:
  • si l'état de l'entrée est LOW, alors attend 100ms
  • ensuite, tant que l'état est LOW, attend encore 100ms (autant de fois que nécessaire) jusqu'à ce que l'état ne soit plus LOW
  • puis exécute l'action
Le résultat est que le petit délais introduit à chaque tour de la boucle while() permet d'une part de ralentir le programme afin d'éviter des changements d'états instantanés et trop nombreux, et d'autre part, de s'assurer que le bouton a effectivement bien été relâché en "amortissant" les petits changements possibles de pression sur le bouton...

Je vous encourage à utiliser cette technique ou une équivalente dans vos programmes dès que vous voudrez interagir avec l'Arduino à l'aide de boutons poussoirs...




5.3.1. Debounce électronique

On m'a récemment fait remarquer qu'il était possible de réaliser un debounce à l'aide de composants supplémentaires. En effet, c'est une pratique courante, mais je ne voulais pas nécessairement ajouter de notion supplémentaire en électronique, car cela s'éloigne un peu de mon objectif premier: utiliser l'Arduino le plus simplement possible.

Ceci-dit, il ne s'agit pas d'un montage bien compliqué, puisqu'il se résume à deux composants: une capacité montée en parallèle avec le bouton poussoir et une résistance en série. Dans la situation présente, la résistance est déjà "incluse" alors que nous utilisons la résistance de "pull-up" interne connectée au port d'entrée. Il ne reste donc que la capacité à ajouter, comme le montre le montage suivant:


Compte-tenu de la valeur de la résistance de "pull-up" interne qui est de l'ordre de 10k, une capacité de 100nF = 0.1uF fera l'affaire. Bien faire attention au sens de montage de la capacité puisque celle-ci est polarisée.


5.4. Port analogique

Nous avons vu précédemment qu'un port numérique, peut avoir deux états: HIGH (+5V) ou LOW (0V). Un port analogique, quant à lui, s'il est défini comme une entrée, peut échantillonner la valeur de la tension (entre 0 et +5V). Cette tension est convertie en une valeur comprise entre 0 et 1023. Ceci se traduit dans un programme par un appel à la fonction analogRead(). L'exemple suivant permet de visualiser la valeur lue (et convertie) sur le port A0:

int entree = A0;
int valeur;

void setup()
{
  pinMode( entree, INPUT );
}

void loop()
{
  valeur = analogRead( entree );
}

Remarque: on identifiera les ports analogiques par le préfixe A. Ainsi les 6 ports analogiques de l'Arduino UNO sont identifiées par des alias prédéfinis par le système: A0 à A5.

Le montage suivant permet de concrétiser l'utilisation d'un port analogique. A l'aide d'un potentiomètre, on fait varier la tension en entrée sur le port A0. Cette valeur sera échantillonnée et retournée par la fonction analogRead(). Puis celle-ci servira de paramètre à l'illumination d'une led en mode PWM... Voilà de quoi cela a l'air:
  • une led de contrôle est branchée sur une sortie PWM (3) avec sa résistance de protection
  • la sortie d'un potentiomètre de 10k est branchée sur l'entrée analogique A0



Note concernant les potentiomètres: Un potentiomètre est muni de 3 broches. La broche centrale est la sortie alors que les broches de chaque côté sont les références à partir desquelles la valeur de sortie est calculée. Ici, la broche gauche est branchée sur +5V (max) alors que la droite est connectée au 0V (min). Dans le cas présent, retenez seulement:
  • lorsqu'on tourne la vis du potentiomètre, la valeur de sortie varie entre min et max
  • si on inverse les connexions des broches de référence, alors la valeur de sortie du potentiomètre variera dans l'autre sens...
  • il existe plusieurs valeurs de résistance et types de potentiomètre. Nous employons ici une résistance de 10k et le type de variation est linéaire sur un tour.

Le code correspondant à ce montage est très simple:

int entree = A0;
int led = 3;

void setup()
{
  pinMode( entree, INPUT );
  pinMode( led, OUTPUT );
}

void loop()
{
  analogWrite( led, analogRead( entree ) / 4 );
}

Le seul commentaire que je ferai concerne la division par 4 de la valeur lue sur le port A0. En effet, il suffit de se rappeler que la valeur de sortie sur un port PWM (numérique) ne varie qu'entre 0 et 255 alors que celle lue sur un port analogique se situe entre 0 et 1023. Elle est donc 4 fois trop grande...

Voilà pour une introduction très simplifiée des ports analogiques utilisés comme entrée. Nous aurons l'occasion d'y revenir au travers d'autres exemples plus "utiles"...


5.4.1. Pourquoi 255 ou 1023?

J'ajoute une petite note concernant ces valeurs... Je m'éloigne un peu de la volonté de garder ce dossier simple. Ce sont des détails intéressant à connaitre mais qui ne doivent pas vous empêcher de comprendre le fonctionnement d'un Arduino si vous ne les maîtrisez pas... Il suffit de prendre pour acquis qu'un port numérique gère des valeurs de 0 à 255 et un port analogique des valeurs 0 à 1023.

D'où proviennent ces valeurs? En informatique ou en électronique numérique, une information est stockée sous forme de bit ou, plus communément, 0 ou 1, soit 2 valeurs possibles. Les entrées/sorties numériques sont définis en 8 bit. Cela signifie que l'intervalle de valeurs est compris entre 0 et (2^8)-1=255. Ce sont toutes les valeurs qu'il est possible de décrire avec 8 bits... De plus, il s'agit d'entrées/sorties "tout ou rien", "haut ou bas", "HIGH ou LOW", donc on se limite aux deux seules valeurs 0 et 255.

Dans le cas d'un port analogique, un convertisseur Analogique/Numérique est inséré entre le port et le processeur. Cela permet de transformer un signal électrique analogique en une information gérable par le processeur. C'est pour cela que l'on parle souvent "d'échantillonnage" du signal. Afin d'avoir une bonne précision de lecture, ce convertisseur est défini sur un espace de 10 bits. Ainsi, l'intervalle d'échantillonnage comprend les valeurs de 0 à (2^10)-1=1023.

Pourquoi est-ce plus précis? Il suffit simplement de visualiser un signal électrique comme un intervalle de 0 à 5V. Si on utilise 8 bits pour définir les valeurs d'échantillonnage du signal, alors un intervalle élémentaire a une précision de 5V / 256 = 0.02V = 20mV. Dans le cas d'un échantillonnage sur 10 bit, on a 5V / 1024 = 0.005V = 5mV, soit 4 fois plus précis...

Voilà, ce que je pouvais dire avec mes mots... Je ne m'étendrai pas plus sur le sujet. Si vous êtes intéressés à en savoir plus, je vous conseille de consulter les ressources à votre disposition sur internet.



1 commentaire:

  1. Super
    Je débute avec arduino
    Merci pour le partage de votre savoir.

    RépondreEffacer