Réponse de l'ADC d'un Arduino

De Wiki LOGre
Révision datée du 27 juillet 2017 à 18:49 par Ebonet (discussion | contributions) (→‎Formules classiques : Corrigé erreur de signe.)
Aller à la navigation Aller à la recherche


La fonction analogRead() de l'Arduino renvoie un entier compris entre 0 et 1023. Comment convertir cette valeur en une vraie tension en volts ? On trouve des informations contradictoires sur le Web, les formules les plus courantes étant

[math]\displaystyle{ V = V_\text{ref} \times \frac{n}{1023} }[/math]

et

[math]\displaystyle{ V = V_\text{ref} \times \frac{n}{1024}, }[/math]

Vref est la tension de référence (typiquement 5 V) et n est la valeur lue. La différence est faible, mais elle a alimenté de longues discussions. J'ai donc voulu trancher expérimentalement. Cette expérience a aussi fourni des informations sur le niveau de bruit et la capacité du bruit à augmenter la résolution de la mesure.

Formules classiques

Les formules que j'ai trouvées sur le Web peuvent toutes se mettre sous la forme

[math]\displaystyle{ V = V_\text{ref} \times \frac{n+n_0}{E}, }[/math]

n0 est un offset (typiquement zéro) et E un facteur d'échelle.

En réalité, avec un convertisseur idéal à 10 bits, l'intervalle des tensions mesurables est divisé en 1024 sous-intervalles, chacun correspondant à un résultat possible. La formule ci-dessous est alors sensé donner le milieu de l'intervalle associé à la valeur n, sauf éventuellement pour les valeurs extrêmes 0 et 1023.

Dans ce qui suit, chaque formule est caractérisée par ses valeurs de E et n0, et pour chacune est représenté le découpage de l'intervalle mesurable en les 1024 sous-intervalles.

E = 1023, n0 = 0 : tutoriels Arduino

Beaucoup de blogs et tutoriels Arduino utilisent cette formule, à commencer par le tutoriel officiel Read Analog Voltage. Le découpage de l'intervalle mesurable est alors comme ceci :

   0                                         Vref
   |---|-------|------- ⋅⋅⋅ ------|-------|---|
   ^       ^                          ^       ^
   0       1                        1022    1023

En haut, l'intervalle de tensions entre 0 et Vref. En bas, les tensions données par la formule pour chaque valeur de n sont repérées par le caractère ^.

On remarque que, avec cette formule, le premier et le dernier intervalle sont moitié moins larges que tous les autres.

E = 1024, n0 = 0 : fiche technique de Atmel

C'est la formule qu'on trouve dans les fiches techniques des microcontrôleurs AVR, et notamment sur celle de l'ATmega328P (en bas de page). Étant donné que c'est la documentation du constructeur, c'est a priori la formule de référence.

   0                                         Vref
   |---|-------|--- ⋅⋅⋅ --|-------|-----------|
   ^       ^                  ^       ^       ^
   0       1                1022    1023   (1024)

Ici, le premier intervalle est moitié moins large que les autres, alors que le dernier fait 1,5 fois la largeur normale.

E = 1024, n0 = 0,5 : Nick Gammon

J'ai trouvé cette formule sur le site de Nick Gammon : ADC conversion on the Arduino (analogRead) – How to interpret the results. Elle n'est pas très répandue, mais j'apprécie le fait qu'elle divise l'intervalle mesurable en 1024 intervalles d'égale largeur :

   0                                         Vref
   |-------|-------|--- ⋅⋅⋅ --|-------|-------|
       ^       ^                  ^       ^
       0       1                1022    1023

Technique de mesure

Je génère la tension variable à l'aide d'un potentiomètre et je la mesure à la fois avec un multimètre et avec un Arduino. Le circuit est différent suivant que je veux mesurer des tensions proches de zéro où proches de Vref :

     proche de 0       proche de Vref
   
         Vcc               Vcc
         ╶┬╴               ╶┬╴
         ┌┴┐                ├────┐
         │ │1 MΩ            │  ╭─┴─╮
         │ │               ┌┴┐ │ V │
         └┬┘         10 kΩ │ │ ╰─┬─╯
          │                │ │←──┴─── Ain
         ┌┴┐               └┬┘
   10 kΩ │ │←──┬─── Ain     │
         │ │ ╭─┴─╮         ┌┴┐
         └┬┘ │ V │         │ │
          │  ╰─┬─╯         │ │1 MΩ
          ├────┘           └┬┘
         ╶┴╴               ╶┴╴
         GND               GND
Lecture de l'ADC d'un Arduino. En haut à gauche : les mesures moyennées sont affichées par l'Arduino sous forme de bargraphe. À droite : les résultats sont reportés à la main dans vim.

Ceci permet d'avoir une bonne résolution, à la fois pour le réglage au potentiomètre et pour la mesure au multimètre (symbolisé par le rectangle arrondi avec un « V »).

L'Arduino est programmé pour mesurer en continu (« free running mode ») à raison de une mesure toutes les 104 µs. Ces mesures passent par un filtre passe-bas du premier ordre avec une constante de temps de 4096 mesures, soit environ 426 ms. Le résultat est affiché sur le port série sous forme de bargraphe, avec une résolution de 1/128 de pas de convertisseur.

Résultats

Ci dessous les résultats de mes mesures sur un STEMTera Breadboard, qui est basé sur un ATmega328P et est compatible avec un Arduino Uno. D'abord, le bas de l'échelle, soit les potentiels compris entre 0 et environ 50 mV :

ADC-Arduino-bottom.png

Sur ce graphique, la valeur moyennée est tracée en fonction de la tension d'entrée. Les croix rouges représentent les mesures, la courbe continue est un ajustement.

Ensuite, les mesures pour le haut de l'échelle, soit environ Vref − 50 mV à Vref :

ADC-Arduino-top.png

La courbe continue représente l'ajustement, qui est commun aux deux jeux de mesures. Cet ajustement donne E = 1026,1 et n0 = 2,2, soit la formule

[math]\displaystyle{ V = V_\text{ref} \times \frac{n+2,2}{1026,1} }[/math]

À noter que la courbe ajustée comporte aussi un terme oscillant, qui permet d'obtenir une succession de marches et qui représente donc l'effet de la discrétisation.

J'ai ensuite refait mes mêmes mesures sur un Arduino Uno rev. 2 et un Uno rev. 3, avec des résultats similaires. Voici un tableau comparatif :

carte E n0
STEMTera 1026,1 2,2
Uno rev. 2 1026,3 2,4
Uno rev. 3 1026,1 2,3

Discussion

Les valeurs obtenues pour E et n0 représentent des constantes d'étalonnage pour un Arduino particulier. Je ne pense pas qu'elles puissent s'appliquer à d'autres Arduinos, même de la même famille. Ma conclusion personnelle est que la différence entre les différentes formules « théoriques » qu'on trouve sur le Web n'est significative : au niveau de précision où on pourrait voir la différence l'étalonnage est indispensable.

Un autre aspect intéressant de ces mesures est le fait que les courbes ressemblent à une succession de marches. On trouve beaucoup d'articles qui prétendent qu'on peut augmenter la résolution de mesure en moyennant. Les courbes précédentes montrent que même en moyennant beaucoup (4096 mesures), on observe toujours la discrétisation initiale. Les marches ne sont pas raides pour autant : le moyennage combiné au bruit a pour effet de les adoucir. Le fait qu'elles ne soient pas complètement éliminées montre seulement que le bruit dans cette expérience est insuffisant pour vraiment augmenter la résolution. Pourtant, aucune précaution n'a été prise pour limiter le bruit : le potentiomètre était manipulé à la main, sur un breadboard. On trouve sur le Web des tentatives d'amélioration de la résolution qui se basent sur l'injection de bruit synthétique. Les mesures ci-dessus montrent que l'ajout de bruit est effectivement nécessaire.