La conversion analogique / numérique avec Arduino / Genuino

Avoir deux de tension n'est pas un problème pour ce tutoriel

Image d'entête

par skywodd | | Licence (voir pied de page)

Catégories : Tutoriels Arduino | Mots clefs : Arduino Genuino Analog CAN ADC

Cet article n'a pas été mis à jour depuis un certain temps, son contenu n'est peut être plus d'actualité.


Dans ce tutoriel, nous allons apprendre ensemble à mesurer des tensions avec une carte Arduino / Genuino. Nous verrons comment mesurer une tension et comment interpréter le résultat de la mesure. En bonus, nous verrons comment ajuster la précision de la mesure en fonction de la plage de tension à mesurer. Nous verrons aussi comment adapter la plage de tension si celle-ci est trop élevée pour être mesurée directement.

Sommaire

Bonjour à toutes et à tous !

Dans mes précédents articles, nous avons vu ensemble beaucoup de choses concernant le domaine numérique. Aujourd'hui, nous allons nous intéresser au domaine analogique.

On parle tellement souvent de numérique de nos jours, qu'on en oubli presque que derrière un bon nombre de valeurs numériques se trouve des grandeurs physiques analogiques.

Les capteurs fournissant une tension (plus ou moins) proportionnelle à la grandeur physique mesurée sont nombreux. Sans ces capteurs, il serait impossible de mesurer une température, une pression atmosphérique, une luminosité, etc.

Il existe aujourd'hui beaucoup de capteurs disposant d'une sortie directement au format numérique (UART, I2C, SPI, OneWire, etc.). Cependant, les capteurs à sortie analogique sont encore couramment utilisés, car simples d'utilisation et peu chers.

De plus, un capteur à sortie numérique n'est rien de plus qu'un capteur à sortie analogique avec un peu d'électronique pour faire la conversion analogique / numérique à l'intérieur du capteur. Cette électronique intégrée au capteur a parfois des avantages, mais aussi parfois des inconvénients en fonction du montage.

Dans ce tutoriel, nous allons voir comment mesurer une tension avec une carte Arduino / Genuino. Savoir mesurer une tension est la base pour pouvoir "lire" la valeur d'un capteur, quel qu'il soit.

Avant propos : analogique / numérique

Avant de commencer, faisons un petit point sur ce qu'est un signal numérique et ce qu'est un signal analogique.

Capture signal analogique et numérique

Capture d'un signal analogique et d'un signal numérique

Un signal numérique est un signal pouvant prendre deux états binaires : haut (HIGH) ou bas (LOW).

Un signal analogique est un signal pouvant prendre une infinité de valeurs intermédiaires.

Dans les faits, un signal numérique est un signal analogique, ne pouvant prendre que deux valeurs. On notera aussi que toutes les grandeurs physiques présentent dans la nature sont des grandeurs analogiques. Le numérique est une invention de l'homme ;)

Pour faire un parallèle un peu imagé, en numérique, le verre est soit plein, soit vide. En analogique, le verre peut être plein, vide ou dans une infinité d'états de remplissage intermédiaire entre les deux.

Conversion analogique / numérique, kézako ?

En électronique numérique, on travaille avec des bits et des octets. En analogique, on travaille avec des grandeurs physiques : tension, courant, résistance, fréquence, etc.

Pour pouvoir exploiter des mesures analogiques avec un microcontrôleur, il faut convertir la mesure analogique en une grandeur numérique. C'est justement le but des convertisseurs analogique / numérique.

Sans entrer dans les détails techniques, certes très intéressants, mais aussi très compliqués, un convertisseur analogique / numérique permet de mesurer une tension (valeur analogique) et de représenter cette tension au moyen d'une valeur numérique.

Signal analogique versus signal numérisé

Signal analogique versus signal numérisé

L'idée est simple : associer une valeur numérique (un nombre entier) pour chaque valeur analogique d'une plage de tension bien précise.

Il existe différents types de convertisseurs analogiques / numériques, certains plus précis que d'autres, certains plus rapides que d'autres, pour les plus curieux d'entre vous, je vous laisse ce lien pour votre lecture du soir ;)

N.B. Plusieurs termes sont employés pour parler d'un convertisseur analogique / numérique : CAN (en français) ou ADC (en anglais).

Mesurer une tension

Mesurer une tension est la base pour lire la sortie d'un capteur analogique, quel qu'il soit.

C'est donc sans grande surprise que les microcontrôleurs modernes – comme ceux utilisés dans les cartes Arduino – disposent d'un convertisseur analogique / numérique intégré.

Entrées analogique d'une carte Arduino UNO

Emplacement des entrées analogiques d'une carte Arduino UNO

Dans le cas d'une carte Arduino UNO, il y a 6 entrées analogiques, pouvant mesurer des tensions comprises entre 0 et 5 volts, avec une précision de 10 bits (soit 1024 points).

Si on fait rapidement le calcul, 1024 points sur une plage de 5 volts donne une précision absolue de 0,0048828125 volt, soit environ 4,9mV.

Ha le vilain bit de poids faible

Quand on utilise un convertisseur analogique / numérique, il faut garder en tête que celui-ci n'est pas parfaitement linéaire. L'espace entre deux "points" n'est pas forcément parfaitement identique à chaque fois. L'erreur est souvent de quelques microvolts, c'est donc négligeable la plupart du temps, mais il faut garder cela en tête.

De plus, tous les convertisseurs analogiques / numériques ont une précision à plus ou moins un LSB = un bit de poids faible.

Si un convertisseur analogique / numérique vous retourne une valeur, disons 521, sans toucher au montage, vous pouvez vous retrouvez avec 520 à la mesure suivante. Il suffit de tomber sur une tension à la limite entre deux points pour que cela se produise.

Ce comportement a un impact direct sur la façon de concevoir un programme utilisant un convertisseur analogique / numérique.

Si le programme doit mesurer une température par exemple, il faudra prévoir une moyenne pour avoir une précision plus élevée.

Si le programme doit effectuer une action quand la valeur mesurée est égale à une consigne donnée, il faudra prévoir une fenêtre de plus ou moins N valeurs lors du test. Si vous faites un test sur une valeur bien précise, votre code ne marchera qu'une fois sur deux à cause de l'erreur de LSB et de l'imprécision des composants du montage.

Entre la théorie et la pratique, il y a souvent de grosses différences ;)

Précautions à prendre

Avant de faire le montage du chapitre suivant, gardez en tête que :

  • Injecter une tension supérieure à 5 volts ou inférieure à 0 volt sur une broche analogique endommagera immédiatement et définitivement votre carte Arduino.

  • La précision de la mesure (10 bits) n'est pas modifiable,

  • La mesure prend environ 100µs, cela fait un maximum de 10 000 mesures par seconde,

  • Laisser une broche non connectée revient à avoir une antenne, mesurer une tension sur une broche non connectée retourne des valeurs de l'ordre de 300 à 500, même s’il n'y a pas de signal,

  • Le résultat est sur 10 bits, soit entre 0 et 1023 (inclus).

Dans le cas des cartes Arduino Due et Zero, les choses sont un peu plus complexes. Si vous avez une carte Due ou Zero, je vous recommande vivement de lire la documentation en anglais sur le site officiel ;)

Le montage de démonstration

Afin de comprendre comment mesurer une tension avec une carte Arduino, nous allons faire un petit montage de démonstration très simple.

Dans notre montage, la carte Arduino va lire la tension en sortie d'un potentiomètre et envoyer la valeur numérique mesurée à l'ordinateur via le câble USB.

Matériel pour exemple "AnalogReadSerial"

Matériel nécessaire

Pour réaliser ce montage, il va nous falloir :

  • Une carte Arduino UNO (et son câble USB),

  • Un potentiomètre (la valeur de celui-ci n'a pas d'importance), j'utilise un potentiomètre de 10K ohms,

  • Une plaque d'essai et des fils pour câbler notre montage.

Parenthèse technique : le potentiomètre

Un potentiomètre est un composant très pratique. Il s'agit en réalité d'une résistance variable.

Photo et symbole d'un potentiomètre

Photographie et symbole d'un potentiomètre

Un potentiomètre est composé d'une résistance de valeur fixe et d'un curseur. Le curseur peut se déplacer le long de la résistance de valeur fixe pour donner une résistance de valeur variable.

Schéma de montage d'un potentiomètre en résistance variable

Schéma de montage d'un potentiomètre en résistance variable

En connectant une extrémité de la résistance fixe et le curseur à un circuit, on obtient une résistance de valeur variable.

Schéma de montage d'un potentiomètre en générateur de tension variable

Schéma de montage d'un potentiomètre en générateur de tension variable

En connectant les deux extrémités de la résistance fixe à une alimentation et le curseur à un circuit, on obtient une sorte de mini générateur de tension variable.

Vue schématique du montage de l'exemple Arduino AnalogReadSerial

Vue schématique du montage

Vue prototypage du montage de l'exemple Arduino AnalogReadSerial

Vue prototypage du montage

Pour commencer notre montage, nous allons câbler les deux broches de la résistance fixe du potentiomètre respectivement aux broches 5V et GND de la carte Arduino.

Montage de l'exemple Arduino AnalogReadSerial

Le montage fini

On achève ensuite le circuit en reliant le curseur du potentiomètre à la broche A0 de la carte Arduino avec un fil.

N.B. Pour les potentiomètres à trois broches "en ligne", le curseur est soit sur la broche au centre, soit sur la broche n°1. En général, une petite flèche permet de savoir où se trouve la broche du curseur.

Le code de démonstration

Maintenant que nous avons notre montage, passons au code !

Le but de notre code va être de :

  1. Lire la tension sur la broche A0

  2. Convertir la valeur en une tension (pour l'affichage)

  3. Envoyer la valeur au PC (pour l'affichage)

  4. Recommencer au point 1.

Pour réaliser ce morceau code, nous allons utiliser la fonction analogRead().

Détails de la fonction analogRead()

La fonction analogRead() permet de mesurer une tension sur une broche analogique (dont le nom commence par le préfixe "A…").

1
int analogRead(broche);

La fonction analogRead() accepte un paramètre obligatoire : le numéro de broche analogique à lire.

Dans le cas d'une carte Arduino UNO, ce paramètre peut prend une des valeurs suivantes : A0, A1, A2, A3, A4, A5.

N.B. Ne confondez pas les broches analogiques (préfixe "An") avec les broches numériques (préfixe "Dn").

La fonction analogRead() retourne un nombre entier (int) compris entre 0 et 1023. Ce nombre correspondant à la tension mesurée, 0 = 0 volt, 1023 = tension alimentation = 5 volts (ou 3v3 suivant les cartes Arduino).

La mesure prend environ 100µs, il n'est donc pas possible de faire plus de 10 000 mesures par secondes.

N.B. Si la broche n'est pas connectée, la valeur retournée par analogRead() va fluctuer entre 300 et 500. Ce n'est pas un bug, c'est tout simplement dû au fait qu'une entrée analogique flottante est une antenne qui capte tous les parasites environnants.

1
2
3
void setup() {
  Serial.begin(9600);
}

Nous allons commencer notre programme de démonstration avec la fonction setup() qui va simplement initialiser la communication avec le PC.

PS Ne cherchez pas à comprendre comment utiliser de Serial.begin() pour le moment, cela fera l'objet d'un futur tutoriel ;)

1
2
3
4
5
6
7
8
void loop() {
  int valeur = analogRead(A0);

  float tension = valeur * (5.0 / 1023.0);

  Serial.println(tension);
  delay(250);
}

Dans la fonction loop(), nous allons faire trois choses : 1) Mesurer la tension sur la broche A0 avec analogRead(). 2) Transformer le résultat de la mesure en un nombre à virgule (type float) en faisant un simple produit en croix. 3) Envoyer la valeur au PC et attendre quelques millisecondes pour avoir le temps de lire ce qui se passe côté PC.

N.B. On utilise valeur * (5.0 / 1023.0) dans le calcul du produit en croix, car lors de la compilation du programme, c'est le type des valeurs d'une opération qui définit le type du résultat. Si on faisait valeur * (5 / 1023) comme valeur, 5 et 1023 sont des nombres entiers, le résultat serait un nombre entier, ce qui n'est pas notre but, nous voulons un calcul avec des nombres à virgule. On utilise donc 5.0 et 1023.0 pour forcer un calcul avec des nombres à virgule.

Le code complet avec commentaires :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
 * Code d'exemple pour la fonction analogRead().
 */

// Fonction setup(), appelée au démarrage de la carte Arduino
void setup() {

  // Initialise la communication avec le PC
  Serial.begin(9600);
}

// Fonction loop(), appelée continuellement en boucle tant que la carte Arduino est alimentée
void loop() {
  
  // Mesure la tension sur la broche A0
  int valeur = analogRead(A0);
  
  // Transforme la mesure (nombre entier) en tension via un produit en croix
  float tension = valeur * (5.0 / 1023.0);
  
  // Envoi la mesure au PC pour affichage et attends 250ms
  Serial.println(tension);
  delay(250);
}

L'extrait de code ci-dessus est disponible en téléchargement sur cette page (le lien de téléchargement en .zip contient le projet Arduino prêt à l'emploi).

Le résultat

Capture d'écran du moniteur série pour l'exemple AnalogReadSerial

Capture d'écran du moniteur série

Après avoir envoyé le programme dans la carte Arduino, en ouvrant le moniteur série (onglet outils), puis en sélectionnant la bonne vitesse de communication (ici 9600 bauds), vous devriez voir apparaitre en temps réel la tension en sortie du potentiomètre.

Si votre montage est correct, en bougeant le curseur du potentiomètre, les valeurs dans le moniteur série doivent normalement changer.

Bonus : améliorer la précision de la mesure

Pour qu'un convertisseur analogique / numérique puisse faire son travail, il a besoin d'une tension maximum de référence. Cette tension s'appelle AREF par convention.

Pouvoir choisir cette tension est très intéressant, car cela permet de réduire la plage de tension de mesure et donc d'avoir des points de mesure plus proche les uns des autres. Cela a pour effet d'améliorer drastiquement la précision des mesures.

Si on mesure un signal analogique toujours compris entre 0 volt et 1.8 volt par exemple, il est dommage d'avoir une référence à 5v, car plus de la moitié des points de mesure ne sont jamais utilisés.

Les microcontrôleurs disposent donc généralement d'une broche spéciale nommée AREF sur laquelle on peut venir injecter sa propre tension de référence. Il va de soi que cette tension de référence doit être la plus précise possible et la plus stable possible pour avoir de bonnes mesures.

À défaut d'injecter sa propre tension de référence, certains microcontrôleurs, comme ceux utilisés dans les cartes Arduino, disposent de plusieurs tensions de référence interne. La tension de référence par défaut est VCC (la tension d'alimentation du microcontrôleur, soit 5 volts ou 3v3 suivant la carte Arduino).

Avec une carte Arduino classique (Uno, Mega2560, etc.), il est possible de choisir la référence de tension en utilisant la fonction analogReference().

1
void analogReference(byte reference);

La fonction analogReference() prend en argument le type de référence à utiliser et ne retourne aucune valeur.

En fonction de la carte Arduino, le type de référence utilisable varie. Voici les différentes valeurs possibles :

  • Sur Arduino UNO et Mega2560 :

    • DEFAULT : il s'agit de la référence par défaut à VCC, soit 5 volts ou 3.3 volts en fonction de la carte Arduino.

    • EXTERNAL : il s'agit d'un mode spécial permettant d'utiliser une tension de référence personnalisée comprise entre 0 et 5 volts à injecter via la broche AREF.

  • Sur Arduino UNO :

    • INTERNAL : il s'agit d'une tension de référence interne à 1.1 volt. Celle-ci est indépendante de la tension d'alimentation de la carte Arduino.

  • Sur Arduino Mega2560 UNIQUEMENT :

    • INTERNAL1V1 : il s'agit d'une tension de référence interne à 1.1 volt. Celle-ci est indépendante de la tension d'alimentation de la carte Arduino.

    • INTERNAL2V56 : il s'agit d'une tension de référence interne à 2.56 volts. Celle-ci est indépendante de la tension d'alimentation de la carte Arduino.

N.B. L'appel à la fonction analogReference() doit être fait au démarrage avant de faire le moindre appel à analogRead().

Le changement de référence est effectif après quelques millisecondes. Il est donc possible que les premières mesures de tension après un changement de référence ne soient pas justes.

Ne rien connecter sur la broche AREF si une tension de référence interne est utilisée

Tout est dans le titre, ne connectez rien à la broche AREF si une tension de référence interne est utilisée.

Si vous connectez quelque chose sur la broche AREF sans être dans le mode EXTERNAL, vous allez déclencher un court-circuit à l'intérieur du microcontrôleur, ce qui lui sera fatal.

Si jamais vous dépassez la tension de référence que vous utilisez, sans pour autant dépasser la tension d'alimentation interne de la carte (5v ou 3v3 suivant la carte), il n'y aura pas de dégât.

Evitez cependant de dépasser la tension de référence, vos mesures seront fausses et en plus cela n'est pas très bon pour le microcontrôleur à long terme.

Voici un exemple de code qui part du principe que vous avez une tension de référence sur AREF :

1
2
3
4
5
6
7
8
void setup() {
  analogReference(EXTERNAL);
}

void loop() {
  int val = analogRead(A0);
  // 0 = 0v, 1023 = tension AREF
}

Bonus : Adapter la plage de tension pour la mesure

Mesurer des tensions comprises entre 0 et 5 volts, c'est bien. Cependant, il arrive régulièrement qu'on ait besoin de mesurer des tensions plus élevées.

Prenons l'exemple d'un montage relié à une batterie de 12v, si votre montage veut connaitre la tension de la batterie, il va falloir ramener la plage de tension 0 - 12v vers la plage mesurable 0 - 5v.

Schéma d'un pont diviseur de tension

Schéma d'un pont diviseur de tension

Pour faire cela, on utilise un pont diviseur de tension. Comme son nom l'indique, un pont diviseur de tension permet de diviser une tension par un coefficient fixe.

Un pont diviseur de tension se construit toujours de la même façon. Tout d'abord, deux résistances sont mises en série pour former un pont de résistances. Les deux extrémités des résistances sont respectivement reliées à la masse d'un côté et à la "haute" tension de l'autre. Le point milieu au centre des deux résistances en série est la sortie "basse tension".

La formule mathématique d’un pont diviseur de tension est la suivante : Vs = Vin * (R2 / (R1 + R2))

Avec Vs = tension de sortie, Vin = tension d'entrée, R1 & R2 = valeurs en ohms des résistances du pont diviseur.

La méthodologie de calcul d'un pont diviseur de tension est la suivante :

  1. Vin = tension max de la plage d'entrée (prévoir 10% de marge pour éviter les mauvaises surprises)

  2. Vs = tension max de la plage de sortie (5v dans la plupart des cas, 3v3 parfois suivant la carte Arduino utilisée)

  3. Choisir une valeur de R2 arbitrairement (je commence toujours avec 10K ohms ou 1K ohms)

  4. Calculer R1 avec la formule : R1 = ((Vin / Vs) * R2) - R2

  5. Ajuster la valeur de R2 si besoin pour obtenir les meilleures valeurs normalisées possibles de R1 et R2.

  6. Enjoy

N.B. Je passe la partie théorique, loi des mailles, etc. Ce n'est pas le but de ce tutoriel.

Exemple pratique : passer d'une plage 0 - 18v à une plage 0 - 5v.

Avec R1 = 3300 ohms (3.3K ohms) et R2 = 1100 ohms (1.1K ohms), on obtient pour Vin = 20v (soit 18v maximum admissible + 10% marge) : Vs = 20 * (1100 / (3300 + 1100)) = 20 * (1100 / 4400) = 20 * 0.25 = 5 volts

Ces valeurs de R1 et R2 sont communément utilisées pour mesurer des tensions d'alimentation en provenance d'une batterie, d'un panneau solaire, etc.

Remarque: pour ceux qui ne voudraient pas se casser la tête à faire le calcul du pont diviseur de tension, voici une calculette en ligne conçue pour vous simplifier la vie : http://www.electronique-radioamateur.fr/elec/schema/calcul-diviseur-tension.php

Et pour ceux qui voudraient en savoir plus sur la théorie derrière le calcul, je vous donne un peu plus de lecture pour ce soir : http://www.sonelec-musique.com/electronique_bases_diviseurs_res.html

N.B. Pour les curieux, un potentiomètre est un pont diviseur de tension un peu particulier avec la somme de R1 et R2 toujours égale à la valeur de la résistance fixe du potentiomètre.

Bonus : Faire une courbe en temps réel avec l'environnement Arduino 1.6.x

Un article du Carnet du Maker ne serait pas complet sans un bonus qui sort de l'ordinaire ;)

Voici donc un petit bonus sympathique pour les utilisateurs de l'environnement de développement Arduino 1.6.6 (et supérieur) : le traceur série.

Capture d'écran du traceur série pour l'exemple AnalogReadSerial

Capture d'écran du traceur série

Si votre code envoi régulièrement sur le port série un nombre (entier ou à virgule) sur une ligne (avec Serial.println()), vous pouvez demander au logiciel Arduino de vous tracer un graphique en temps réel de ces valeurs.

Capture d'écran du menu outils du logiciel Arduino

Capture d'écran du menu outils

Le traceur s'active dans le menu outils. N.B. Celui-ci est extrêmement basique pour le moment.

Vous pouvez choisir la vitesse de communication du port série avec la liste de sélection en bas du graphique.

Traceur ou moniteur série, il faut choisir

Il n'est pas possible pour le moment d'avoir le traceur série et le moniteur série ouvert en même temps.

Si vous fermez puis ouvrez le traceur ou le moniteur série, la carte Arduino redémarrera, gardez bien cela en tête.

Pour le graphique lui-même, il n'y a pas grand-chose à configurer.

L'axe X représente le temps, chaque nouvelle ligne de texte crée un point sur le graphique. 500 points sont affichés sur l'axe X avant que les anciens points ne soient effacés. Pensez à mettre un delay() dans votre code sinon vous n'aurez pas le temps de voir le graphique se tracer.

L'axe Y affiche les valeurs reçues. L'échelle du graphique s'adapte automatiquement en fonction des valeurs reçues, ce comportement n'est pas modifiable pour le moment.

Il n'y a pas d'échelle d'affichage ou d'échelle temporelle pour le moment, c'est vraiment un outil très simpliste. De plus, il n'y a pas de documentation officielle sur cet outil. C'est une fonctionnalité encore au stade de prototype.

Conclusion

Ce tutoriel est désormais terminé.

Si ce tutoriel vous a plu, n'hésitez pas à le commenter sur le forum, à le diffuser sur les réseaux sociaux et à soutenir le site si cela vous fait plaisir.