mardi 13 novembre 2018

ARDUINO : les performances de l'ADC




ARDUINO : les performances de l'ADC

Nous allons essayer dans cet article d'approfondir nos connaissances de l'ADC (convertisseur analogique / numérique) de l'ATMEGA328P.
Nous allons explorer les possibilités de ce périphérique et essayer d'en augmenter les performances.

1. L'ADC de l'ATMEGA328P

1.1. Description

Les entrées analogiques de l'ATMEGA328P sont constituées d'un seul convertisseur analogique / numérique 10 bits, précédé d'un multiplexeur à 8 canaux.
Les valeurs mesurées par l'ADC varieront donc entre 0 et 1023.

Les pins de l'ARDUINO UNO réduisent ce nombre à 6 canaux.
Les pins de l'ARDUINO NANO mettent à disposition les 8 canaux, ainsi que la plupart des MINI et PRO MINI.

1.2. Circuit

Cette figure est tirée de la datasheet :

Le texte suivant accompagne la figure:
The ADC is optimized for analog signals with an output impedance of approximately 10k or less. If such a source is used, the sampling time will be negligible. If a source with higher impedance is used, the sampling time will depend on how long time the source needs to charge the S/H capacitor, with can vary widely. The user is recommended to only use low impedance sources with slowly varying signals, since this minimizes the required charge transfer to the S/H capacitor.

Il est donc recommandé de ne mesurer que des sources de tension à basse impédance.

Une légende circule qui tendrait à faire croire qu'il faut systématiquement faire deux lectures dont une pour rien quand on effectue des mesures sur plusieurs entrées différentes. Est-ce vrai dans tous les cas ?

A la lecture du schéma, on peut émettre quelques hypothèses :

Si l'impédance de la source est basse, la mesure ne sera pas influencée, y compris si la tension varie rapidement. La constante de temps induite par la résistance de 100KΩ et la capacité de 14pF est de l'ordre de la µs, largement inférieure à la période de l'horloge de l'ADC. Une impédance de source basse (10KΩ) n'aura qu'une faible influence.

Si l'impédance de la source est élevée par contre, la mesure sera influencée, car la capacité CS/H mettra plus de temps à se charger.

1.3. La référence de tension

Lorsqu'on utilise un ADC, le code que l'on va écrire ressemblera forcément à ceci :

int value = analogRead(0);     // lecture ADC sur A0
float voltage = value * 5.0 / 1023;

Ici, on remarque plusieurs choses :

  • la lecture se fait sur A0
  • la référence de tension est supposée valoir 5V
  • analogRead() retourne une valeur entre 0 et 1023. Il y a 1024 valeurs possibles, et 1023 intervalles

On remarque également que la référence choisie dans le code (5.0V) a peu de chances de refléter la réalité. Pour être plus précis, il faudrait mesurer la tension d'alimentation et utiliser cette valeur dans le code :

#define VREF                  5.07

int value = analogRead(0);     // lecture ADC sur A0
float voltage = value * VREF / 1023;

1.3.1. Référence par défaut

La référence par défaut est la tension d'alimentation du processeur. Sa précision dépend du mode d'alimentation de la carte :

  • si on alimente par la broche VIN, la qualité de la référence sera directement dépendante de celle du régulateur de tension 5V ou 3.3V de la carte
  • si on alimente par la broche VCC, la qualité de la référence sera directement dépendante de celle de l'alimentation
  • si on alimente par l'USB, la qualité de la référence sera directement dépendante de celle de la tension USB du PC

On en déduit assez facilement qu'utiliser la référence de tension par défaut n'est probablement pas la meilleure solution, étant donné que la précision de la mesure dépendra principalement de la précision de la source d'alimentation. Or, toutes les alimentations ne se valent pas, spécialement s'il s'agit de blocs secteurs 5V, souvent de piètre qualité. Le 5V USB d'un PC n'est pas non plus une source d'alimentation  stable, elle peut varier elle aussi.

De plus, la valeur de la mesure sera différente si l'on change de mode d'alimentation, en passant par exemple d'une alimentation par USB à une alimentation par bloc secteur.

Pour corriger ce problème, en fonction du processeur utilisé, on peut soit utiliser la référence interne ou une référence externe.

On peut se référer à cette page :

https://www.arduino.cc/reference/en/language/functions/analog-io/analogreference/

1.3.2. Référence interne

La référence interne est de 1.1V pour un ATMEGA328. La précision de cette tension est de l'ordre de 10%. De plus elle varie légèrement en fonction de la tension et de la température :

Pour utiliser la référence interne, il faut bien entendu la sélectionner :

analogReference(INTERNAL);

Lorsqu'on utilise cette référence, on peut la mesurer sur la pin AREF pour en tenir compte dans le code :

#define VREF                  1.095

int value = analogRead(0);     // lecture ADC sur A0
float voltage = value * VREF / 1023;

On peut également découpler cette référence par un petit condensateur entre la pin AREF et la masse, pour une meilleure immunité au bruit.

1.3.3. Référence externe

Pour utiliser une référence externe, il faut la sélectionner comme ceci :

analogReference(EXTERNAL);

On peut utiliser la sortie 3.3V présente sur certaines cartes, en la branchant sur la broche AREF. Cela peut être suffisant dans la majeure partie des cas.

La référence externe peut être fournie aussi par être n'importe quel composant appelé "référence de tension" :

  • TL431 : 2.5V 0.5% à 2%
  • LM4040 : 2.048V, 2.5V, 3 V, 4.096V, 5 V 0.1%
  • LT1009 : 2.5V 0.2%
  • MCP1501 : 1.024V, 1.25V, 1.8V, 2.048V, 2.5V, 3V, 3.3V, 4.096V 0.1%

Sur ce schéma, j'utilise un TL431 (U1, en haut à gauche) :

Notre code devient :

#define VREF                  2.5        // TL431 reference

int value = analogRead(0);     // lecture ADC sur A0
float voltage = value * VREF / 1023;

Il existe des références très précises. Je vous les donne juste pour information. Bien évidemment, n'utilisez pas une référence à 0.02% avec un ADC 10bits, ce serait du gâchis.

  • MAX6225 : 2.5V 0.02%
  • MAX6241 : 4.096V 0.02%
  • MAX6250 : 5V 0.02%

En fonction de l'application, le besoin sera différent. Par exemple, si on mesure la tension d'une batterie afin de déterminer sa capacité on aura besoin d'une précision importante et surtout d'une bonne stabilité en température.

1.4. Tension maximale mesurée

La tension maximale que l'on pourra mesurer sera égale à la tension de référence. Si l'on applique une tension supérieure sur une entrée analogique, l'ADC va saturer, et le résultat sera faux.

Si la tension à mesurer est supérieure à la tension de référence, il faut l'abaisser :

Dans ce projet, la référence interne de 1.1V est choisie. La tension à mesurer est celle d'une batterie LITHIUM-ION : 4.2V maximum.

Les deux résistances R2 et R3 forment un pont diviseur. Voici la formule de calcul classique :

Vo = VBATT * R3 / (R2 + R3)

Ici la valeur maximale sur A0 pour VBATT = 4.2V sera :

Vo = 4.2V * 330000 / (1000000 + 330000) = 1.04V

Cette valeur est bien en dessous de la référence choisie : 1.1V

Quelles valeurs de résistances choisir ? Cela va dépendre de l'application. Ici, les valeurs sont élevées, car l'alimentation se fait par batterie, et il est hors de question de consommer un courant trop important dans le pont diviseur.

On peut commencer par fixer deux valeurs :

  • la valeur maximale à mesurer : 1V (elle doit être inférieure à la référence 1.1V)
  • la résistance R3 : 330KΩ

Ensuite on en déduit R2 :

R2 = R3 * (VBATT / Vo) - R3 = 330000 * (4.2V / 1V) - 330000 = 1056KΩ

Comme on ne trouvera pas cette valeur dans le commerce, on adoptera la valeur standard la plus approchante : 1MΩ.

Si l'on adoptait une valeur de 910KΩ :

Vo = 4.2V * 330000 / (910000 + 330000) = 1,117V

Cette valeur est au dessus de 1.1V, elle ne convient donc pas.

Que faire dans le code ?

Ici, nous ajustons la valeur de la référence 1.1V à celle qui a été mesurée : 1.095V

Ensuite nous appliquons un coefficient correspondant la la valeur du pont diviseur :

Coeff = R3 / (R2 + R3) = 330000 ÷ (1000000+330000) = 0.248

Cela donne ceci au final :

#define VREF                  1.095
#define COEFF                
0.248

  unsigned int adc = analogRead(0);
  float voltage = adc * VREF / 1023 / COEFF;

2. Augmenter la précision dynamique

Comme vu au paragraphe 1.2, l'entrée de l'ADC comporte une capacité.

Cette capacité d'entrée de l'ADC  risque de nous poser un problème si nous cherchons à effectuer une mesure sur deux entrées ayant des potentiels très différents.

Mais cela nous posera également un problème si nous effectuons une mesure sur une seule entrée avec de fortes et rapides variations de tension.

Pour mettre en évidence le problème, appliquons sur deux entrées A0 et A1 deux tensions différentes :


Sur l'entrée A0 nous mesurerons idéalement :
3.3V / (10000+4700) / 10000 = 2.25V
Sur l'entrée A1 nous mesurerons idéalement :
3.3V / (10000+4700) / 4700 = 1.05V

2.1. La théorie

La fréquence d'horloge de l'ADC pour la librairie ARDUINO est de 125KHz, cela nous donne donc un temps de cycle de 8µs.Entre le moment où la conversion démarre et le moment où l'échantillon est prélevé (sample & hold), il se passe 1.5 temps de cycle soit 12µs :


2.1.1. Impédance élevée
Imaginons que nous ayons à l'entrée de l'ADC une source de tension à mesurer ayant une impédance forte, 1MΩ par exemple.

La constante RC de temps de charge du condensateur de l'ADC sera de :

RC = 1MΩ * 14pf soit 14µs.

On considère que le condensateur sera chargé à 99% au bout de :

t = 5 * RC soit 70µs.

L'idéal serait d'attendre un temps équivalent entre la sélection de l'entrée et le démarrage de la conversion.Or la librairie ARDUINO n'attend pas !

Et comme il faut 12µs entre le démarrage de la conversion et la prise d'échantillon, on voit bien que cela va provoquer une erreur de lecture assez importante. En fait cette erreur dépendra de la différence de tension entre les deux canaux. Si les tensions sont presque identiques, l'erreur sera minime. Si la différence est importante, l'erreur le sera aussi.

2.1.2. Impédance faible
Avec une source de tension ayant une impédance faible, 10KΩ par exemple, il nous faudra tenir compte de la résistance série interne de l'ADC (une valeur de 1 à 100KΩ comme le montre la figure plus haut). Elle est estimée par certains expérimentateurs à une vingtaine de KΩ, ce qui nous donne 30KΩ en tout. Il nous faudra donc attendre 33 fois moins longtemps, donc 2µs. Dans ce cas nous sommes très en dessous des 12µs requises.
Mais n'oublions pas que ces 2µs permettent de charger le condensateur à seulement 99%. Pour garantir une résolution maximale (théoriquement 0.5 LSB), il est possible que l'on ait effectivement besoin des 12µs.

2.1.3. Impédance moyenne
Avec une source de tension ayant une impédance moyenne, 100KΩ par exemple, il nous faudra attendre 8.3 fois moins longtemps, donc 8.4µs. Dans ce cas nous sommes aussi en dessous des 12µs requises, mais n'oublions pas ici aussi que nous nous sommes contentés de charger le condensateur à 99%, ce qui donne quand même 1% d'erreur.

2.1.4. La théorie de la double lecture
Sur le WEB on voit couramment des conclusions hâtives du genre "pour éviter les problèmes faire systématiquement une double lecture et ne conserver que la deuxième.

Prenons un exemple simple :
- une LDR en série avec une résistance, point milieu sur une entrée
- un potentiomètre sur une autre entrée

En fonction du sens de branchement et de la différence de tension entre les deux entrées, le condensateur a une chance sur quatre de se charger ou décharger à travers la LDR. S'il fait nuit noire, certaines LDR ont une résistance de 10MΩ.
Il s'en suit un temps de charge ou de décharge du condensateur de l'ADC de :

RC = 10MΩ * 14pf  * 5 soit 700µs.

Dans ce cas, ce n'est pas avec une double lecture que l'on s'en sortira, ni avec une triple.
Avec une fréquence d'horloge de 125KHz, le nombre de mesures par seconde et de 8900, donc 110µs par mesure. Il faudra donc 7 mesures et non pas 2 !
On voit bien que la théorie fumeuse de la double mesure systématique ne tient pas.
Dans certains cas, elle est totalement inutile, dans d'autres cas, elle est totalement insuffisante.

2.1.5. Expérimentation
Nous allons vérifier ceci par expérimentation dans les paragraphes suivants.

2.2. Les sketches

Nous mesurons alternativement les deux entrées A0 et A1 à l'aide des deux sketches suivants :

Le premier sketch lit alternativement les deux entrées A0 et A1 en prenant un seul échantillon à chaque fois.

// SKETCH N°1 (une lecture)

#define VREF 4.72

void setup() {
  Serial.begin(115200);
}

void loop() {
  uint16_t valA0 = analogRead(A0);
  Serial.print("Voltage A0: ");
  Serial.println((float)valA0 * VREF / 1023);
  uint16_t valA1 = analogRead(A1);
  Serial.print("Voltage A1: ");
  Serial.println((float)valA1 * VREF / 1023);
  delay(1000);
}


Le deuxième sketch lit alternativement les deux entrées A0 et A1 en prenant deux échantillons à chaque fois.
 
// SKETCH N°2 (deux lectures)

#define VREF 4.72

void setup() {
  Serial.begin(115200);
}

void loop() {
 
  analogRead(A0); 
  uint16_t valA0 = analogRead(A0);
  Serial.print("Voltage A0: ");
  Serial.println((float)valA0 * VREF / 1023);
 
  analogRead(A1); 
  uint16_t valA1 = analogRead(A1);
  Serial.print("Voltage A1: ");
  Serial.println((float)valA1 * VREF / 1023);
  delay(1000);
}


2.3. Impédance source faible

Notre premier essai est effectué avec deux ponts diviseurs 10KΩ/4.7KΩ et 4.7KΩ/10KΩ comme sur le schéma ci-dessus.

La mesure avec le sketch N°1 donne les résultats suivants :

Voltage A0: 2.26
Voltage A1: 1.04
Voltage A0: 2.26
Voltage A1: 1.05
Voltage A0: 2.26
Voltage A1: 1.04 


La mesure avec le sketch N°2 donne les résultats suivants :

Voltage A0: 2.26
Voltage A1: 1.04
Voltage A0: 2.26
Voltage A1: 1.05
Voltage A0: 2.26
Voltage A1: 1.04


On obtient le même résultat. On s'y attendait un peu non ?

L'impédance de 4.7KΩ est très faible devant les 100KΩ de l'entrée de l'ADC, donc la capacité d'entrée de l'ADC se charge en un temps très court.

2.3. Impédance source forte

Augmentons les valeurs. Remplaçons ces résistances de 10KΩ et 4.7KΩ par des résistances de 1MΩ et 470KΩ.

Les résistances utilisées sont des modèles film métallique 1%. Les différences de mesure par rapport au test précédent seront minimes mais pas inexistantes.

La mesure avec le sketch N°1 donne les résultats suivants :

Voltage A0: 2.27
Voltage A1: 1.19
Voltage A0: 2.16
Voltage A1: 1.17
Voltage A0: 2.09
Voltage A1: 1.19


La mesure de A0 est systématiquement tirée vers le bas, sauf la première, celle de A1 est tirée vers le haut.
La tension a du mal à se stabiliser après une baisse ou une hausse rapide.

La mesure avec le sketch N°2 donne les résultats suivants :

Voltage A0: 2.27
Voltage A1: 1.03
Voltage A0: 2.27
Voltage A1: 1.03
Voltage A0: 2.26
Voltage A1: 1.03


Ce résultat est nettement meilleur non ?

Le fait de lire deux fois améliore grandement les choses. L'opération peut se décomposer ainsi :
  • sélection de l'entrée
  • démarrage de la conversion
  • attente de la fin de la conversion
  • récupération du résultat (pour rien)
  • sélection de l'entrée
  • démarrage de la conversion
  • attente de la fin de la conversion
  • récupération du résultat
Il y a fort à parier que l'on obtienne le même résultat comme ceci :
  • sélection de l'entrée 
  • delay(x)
  • démarrage de la conversion
  • attente de la fin de la conversion
  • récupération du résultat
L'important n'est pas de lire deux fois, mais de laisser un peu de temps entre la sélection de l'entrée est le démarrage de la conversion.

2.4. Impédance source moyenne

Remplaçons les résistances par des résistances de 100KΩ et 47KΩ.

La mesure avec le sketch N°1 donne les résultats suivants :

Voltage A0: 2.23
Voltage A1: 1.07
Voltage A0: 2.23
Voltage A1: 1.07
Voltage A0: 2.23
Voltage A1: 1.07


La mesure de A0 est légèrement tirée vers le bas, celle de A1 est légèrement tirée vers le haut.

Nous pouvons en tirer la conclusion suivante :

Si l'impédance de la source de tension est très inférieure à 100KΩ, nous pouvons lire plusieurs entrées alternativement, ou lire une entrée dont la tension varie rapidement, ceci sans précaution particulière.

2.5. La solution électronique

Quand l'impédance de la source est élevée, comment faire pour ne pas dégrader les performances de la conversion alors que cela impose de lire deux fois ou d'introduire un délai entre la sélection de l'entrée est le démarrage de la conversion ?

Il est possible d'intercaler un amplificateur opérationnel suiveur entre la source et l'ADC afin d'abaisser l'impédance :


Reprenons le montage avec cette fois-ci :
  • R3 = 4.7KΩ
  • R4 = 10KΩ
  • R1 = 2MΩ
  • R2 = 1MΩ
La mesure avec le sketch N°1 donne les résultats suivants :

Voltage A0: 2.26
Voltage A1: 1.26
Voltage A0:
2.26
Voltage A1: 1.24
Voltage A0:
2.26
Voltage A1: 1.24

La mesure de A0 est normale, celle de A1 est tirée vers le haut.
 
La mesure avec le sketch N°2 donne les résultats suivants :

Voltage A0: 2.26
Voltage A1:
1.05
Voltage A0:
2.26
Voltage A1: 1.04
Voltage A0:
2.26
Voltage A1:
1.05

Ajoutons maintenant un amplificateur opérationnel comme ceci :

L'amplificateur opérationnel est un LT1077, que l'on peut alimenter avec une simple ligne 5V.

La mesure avec le sketch N°1 donne les résultats suivants :

Voltage A0: 2.26
Voltage A1: 1.04
Voltage A0:
2.26
Voltage A1: 1.05
Voltage A0:
2.26
Voltage A1: 1.05


La mesure avec le sketch N°2 donne les résultats suivants :

Voltage A0: 2.26
Voltage A1: 1.05
Voltage A0:
2.26
Voltage A1: 1.04
Voltage A0:
2.26
Voltage A1: 1.05

 
La situation "normale" est rétablie. Il n'y a plus de différence entre une simple mesure et une double mesure.

L'amplificateur opérationnel abaisse l'impédance à l'entrée de A1.

Qu'y avons-nous gagné ?
Nous pouvons nous contenter d'une mesure unique en toutes circonstances. Donc nous avons gagné du temps et une prise d'échantillon plus rapide.


3. Augmenter la vitesse

La librairie standard fixe l'horloge de l'ADC à 125KHz. Or celui-ci peut facilement faire le même travail à 500KHz.
Il est possible également de le configurer en free-running. Dans ce cas, le convertisseur tourne librement et nous venons récupérer le résultat de la conversion quand nous en avons besoin.

Essayons le sketch suivant (tiré de ce site) :

#include "wiring_private.h"

#define TEST  10000L

uint32_t sensorValue;

void testDefaultADC(int pin)
{
  uint32_t start = millis();
  for (int x = 0 ; x < TEST ; x++) {
    sensorValue += analogRead(pin);
  }
  uint32_t elapsed = millis() - start;
  Serial.print("ADC value ");
  Serial.println(sensorValue / TEST);
  Serial.print("elapsed time ");
  Serial.print(elapsed);
  Serial.println(" ms");
  Serial.print("samples ");
  Serial.println(TEST);
  Serial.print("ADC speed: ");
  Serial.print(TEST * 1000L / elapsed);
  Serial.println(" SPS");
}

void startFreeRunningADC(uint8_t pin)
{
  ADMUX = (1 << REFS0) | ((pin - 14) & 0x07); //select AVCC as reference and set MUX to pin
  ADCSRB = 0; //set free running mode
  ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADPS2) | (1 << ADPS0); //set adc clock to 500Khz, enable, free running mode
  //ADCSRA=(1<<ADEN)|(1<< ADATE)|(1<<ADPS2)|(1<<ADPS1); //set adc clock to 250Khz, enable, free running mode
  sbi(ADCSRA, ADSC); //start the ADC
}

inline uint8_t sampleDone()
{
  // ADIF is set when the conversion finishes
  return bit_is_set(ADCSRA, ADIF);
}

inline uint16_t getSampleResult()
{
  uint8_t low, high;
  low = ADCL; //make sure to read L value first
  high = ADCH;
  sbi(ADCSRA, ADIF); //clear conversion complete flag
  return (high << 8) | low;
}

#define ADCport A0

void setup()
{
  Serial.begin(115200);
  testDefaultADC(ADCport);
  startFreeRunningADC(ADCport); //free running mode, only needs to start once.
}

#define ELAPSE    1000L

uint32_t start;
uint32_t count;

void loop()
{
  if (start == 0) {
    start = millis();
    sensorValue = 0;
  }
  while (!sampleDone());
  sensorValue += getSampleResult();
  count++;
  if (millis() - start >= ELAPSE) {
    Serial.print("ADC value ");
    Serial.println(sensorValue / count);
    Serial.print("elapsed time ");
    Serial.print(ELAPSE);
    Serial.println(" ms");
    Serial.print("samples ");
    Serial.println(count);
    Serial.print("ADC speed: ");
    Serial.print(count * 1000L / ELAPSE);
    Serial.println(" SPS");
    while(1);
  }
}


Dans un premier temps, dans le setup, nous mesurons à l'aide de la fonction testDefaultADC le temps d'exécution pour 10000 mesures.
Ensuite, l'ADC est boosté à 500KHz et tourne en free-running, de manière asynchrone.

Les résultats sont éloquents :

ADC value 510
elapsed time 1119 ms
samples 10000
ADC speed: 8936 SPS
ADC value 510
elapsed time 1000 ms
samples 38470
ADC speed: 38470 SPS


La valeur de la mesure (nous mesurons ici VCC/2) ne change pas, par contre nous avons multiplié le nombre de SPS (échantillons / seconde) par 4.

Remarque : pour les besoins du test, la fin de la conversion est attendue :

  while (!sampleDone());

Mais nous pouvons nous dispenser de cette attente de manière à ne pas rester bloqué et pouvoir faire autre chose pendant ce temps :

  if (!sampleDone()) {
    sensorValue = getSampleResult();

  } else {
    // faire autre chose
  }

4. Augmenter la résolution

4.1. ADC externe

Afin d'augmenter la résolution il est possible d'utiliser un ADC 16bits du type ADS1115, fonctionnant en I2C :

Malheureusement, il vous en coûtera une nette dégradation des performances en vitesse. Cet ADC a des performances excellentes en matière de résolution mais il est peu rapide : 860 SPS.

Un ADS1256 SPI 24bits sera beaucoup plus rapide (30000 SPS) :

ADS1256 : ADC Hautes Performances

Sinon, changer de processeur pour un ARDUINO DUE ou un STM32.

L'ADC d'un STM32F401 a une résolution de 12bits, une fréquence d'horloge de 30MHz et peut offrir 2000000 SPS.

4.2. Mesure moyenne

Afin d'obtenir une meilleure immunité au bruit et une meilleure précision, il est possible de lire plusieurs fois la valeur et faire une moyenne :

float readAdc(int pin)
{
  uint32_t val = 0;

  analogRead(pin);
  for (int i = 0 ; i < 4 ; i++) {
    val += analogRead(pin);
  }
  return (float)val * VREF / 1023;
}

Il vous en coûtera une dégradation certaine des performances en vitesse d'acquisition, proportionnelle au nombre de lectures.

5. La totale

Ci dessous le schéma précédent complété a l'aide d'un LM4040 :

Le LM4040 est alimenté par une résistance de 1K, ce qui produit un courant de 5V - 2.5V / 1000 =  2.5mA.

Le sketch utilise la référence externe et effectue une moyenne sur quatre lectures consécutives.

#define VREF 2.5  // LM4040

void setup() {
  Serial.begin(115200);
  analogReference(EXTERNAL);
}

float readAdc(int pin)
{
  uint32_t val = 0;

  analogRead(pin);
  for (int i = 0 ; i < 4 ; i++) {
    val += analogRead(pin);
  }
  return (float)val * VREF / 1023;
}

void loop() {
  Serial.print("Voltage A0: ");
  Serial.println(readAdc(A0));
  Serial.print("Voltage A1: ");
  Serial.println(readAdc(A1));
  delay(1000);
}

La mesure est exceptionnellement stable :

Voltage A0: 2.25
Voltage A1: 1.11
Voltage A0: 2.25
Voltage A1: 1.11
Voltage A0: 2.25
Voltage A1: 1.11
Voltage A0: 2.25
Voltage A1: 1.11

6. Conclusion

L'ADC de l'ATMEGA328P peut offrir des performances acceptables pour peu que l'on se donne la peine de le configurer correctement.


Cordialement
Henri

7. Mises à jour

01/06/2019 : 2.1. La théorie

3 commentaires:

  1. J attendais ce genre d'étude depuis quelques mois , je trouve que c'est un travail très utile et qui intéressera nombre de personne . Merci beaucoup .

    RépondreSupprimer
  2. C'est vraiment un travail très intéressent on vous remercie pour vos efforts .
    asq on peut utilisé arduino comme un CAN utile pour un traitement de signal par une carte FPGA .on tenant compte que l'alimentation de cette carte est de 3.3v

    RépondreSupprimer
    Réponses
    1. Tout dépend de la vitesse nécessaire, et de la résolution.
      Peu importe la tension d'alimentation. Seule l'amplitude du signal à mesurer a une importance.

      Éventuellement un STM32 peut faire beaucoup mieux.
      https://riton-duino.blogspot.com/2019/04/stm32-ladc.html

      Supprimer