Mesurer la tension d'alimentation d'une carte Arduino / Genuino ou d'un microcontrôleur AVR

Parfois, l'unité pifométrique n'est pas suffisante.

Image d'entête

par skywodd | | Licence (voir pied de page)

Catégories : Tutoriels Arduino | Mots clefs : Arduino Genuino Analog AVR


Dans ce tutoriel, nous allons voir comment mesurer la tension d'alimentation d'une carte Arduino / Genuino ou de n'importe quel montage à base de microcontrôleur ATMega328p, simplement avec quelques lignes de code. En bonus, nous verrons comment modifier le code pour le faire fonctionner avec des microcontrôleurs ATMega2560 (carte Arduino Mega) et ATMega32u4 (carte Arduino Leonardo / Micro).

Sommaire

Bonjour à toutes et à tous !

Imaginez la scène suivante : vous êtes devant votre carte Arduino ou votre montage à base microcontrôleur AVR avec le PC en face vous. Vous êtes en train de faire votre code et quelque part parmi les lignes de code, se trouve une formule du style tension = (5.0 * valeur) / 1023.0.

Cette formule très classique, qui n'est rien d'autre qu'un produit en croix, vous permet de convertir la valeur numérique en sortie d'un appel à analogRead() en une tension sous forme de nombre à virgule. Avec 5.0 correspond à la tension d'alimentation de la carte et 1023 à la valeur maximum qui correspond à la tension d'alimentation en sortie de analogRead().

La valeur 1023 est fixe et elle est liée à la précision du convertisseur analogique / numérique (10 bits) : 2 ^ 10 = 1024, soit des valeurs possibles de 0 (0 volt) à 1023 (5 volts). La valeur 5.0 est une valeur théorique parfaite de la tension de la carte.

Or, entre théorie et pratique, il y a souvent beaucoup de différences. La tension d'alimentation par exemple est souvent plus proche de 4.8 volts ou encore 5.1 volts en fonction de la carte utilisée. Dans certains cas, cette différence minime peut suffire à fausser le résultat d'un calcul comme celui ci-dessus. Le fait de connaitre la tension d'alimentation réelle de la carte permet donc de se passer de cette valeur en dure dans le code et d'améliorer la précision de la mesure finale.

Autre cas d'usage, plus simple : vous êtes toujours devant votre carte Arduino avec votre PC et vous avez un problème. Vous voulez détecter une chute de la tension d'alimentation avant qu'elle ne devienne problématique. Ce genre de cas arrive souvent quand on travaille sur des montages alimentés par batterie. Dans ce cas, connaitre la tension d'alimentation de la carte est une première solution au problème.

Dans cet article, on va voir comment mesurer la tension d'alimentation d'un microcontrôleur AVR (ce qui inclut toutes les cartes Arduino "classique" et compatible) sans avoir à câbler le moindre fil.

Mesurer la tension d'alimentation d'un microcontrôleur pour détecter un niveau de batterie faible est quelque peu dangereux dans le sens où la chute de tension est souvent rapide. Il y a donc en général un délai assez court entre la détection de niveau de pile faible et l'arrêt brutal du montage.

Je publierai d'ici quelques jours un follow-up expliquant la méthode de mesure de la tension d'une batterie. L'article sera mis en lien en bas de cet article une fois disponible.

Théorie et principe

Comme beaucoup de microcontrôleurs, les microcontrôleurs AVR (Atmega, ATTiny, etc) qui sont au coeur des cartes Arduino "classiques" disposent d'une (ou plusieurs) référence(s) de tension.

Cette référence de tension (en général une simple diode "band gap") est une source de tension qui ne varie pas, qu'importe la tension d'alimentation du microcontrôleur. Dans le cas des microcontrôleurs AVR, la référence de tension interne est généralement de 1.1 volts.

Cette référence de tension a de multiples usages, comme mettre le microcontrôleur en pause quand l'alimentation est instable par exemple.

Tableau de valeurs pour ADMUX du microcontrôleur ATMega328p

Les valeurs pour ADMUX

Ce qui est très intéressant avec les microcontrôleurs AVR, c'est que le concepteur a prévu un moyen de mesurer la tension de cette référence à partir du convertisseur analogique / numérique.

On serait en droit de se poser la question suivante : pourquoi vouloir mesurer une tension qui est par définition toujours fixe !?

Mesurer la tension de référence pour le simple plaisir de la mesurer n'a pas grand intérêt. L'intérêt de cette référence c'est de la comparer à une autre valeur mesurée par le convertisseur analogique / numérique, comme la tension d'alimentation de la carte par exemple.

La tension d'alimentation de la carte sert de référence pour la conversion numérique. Donc à moins d'avoir configurer une référence externe sur la broche AREF via la fonction analogReference(), si vous mesurez la tension en VCC (qui équivaut à la tension sur la broche 5V pour les cartes 5 volts et sur la broche 3V3 pour les cartes en 3.3 volts) vous obtiendrez toujours 1023 en retour de analogRead(). En mesurant la tension correspond à la référence haute, on obtient la valeur maximum possible, c'est logique.

  • En théorie, pour une carte Arduino fonctionnant en 5 volts, si on mesure la tension sur la broche 5V, on obtient toujours 1023.

  • Toujours en théorie, si 5 volts = 1023, alors 1.1 volts = (1023 * 1.1) / 5.0 = 225.

Arrêtons-nous ici un instant. Si 1.1 volts est toujours égale à 225 en théorie, comme la tension de 1.1 volts est constante aussi bien en théorie qu'en pratique, cela signifie que l'on peut renverse le produit en croix est déterminer la tension d'alimentation réelle à partir de la valeur lue pour la tension de 1.1 volts ! Dans ce cas, on obtient la formule suivante : VCC = (1023 * 1.1) / Valeur_référence.

Si on test la formule dans un contexte idéal avec analogRead(1.1 volts) = 225, on obtient : (1023 * 1.1) / 225 = 5.00133 volts.

Le code

Trêve de bavardage, voici le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 * Code Arduino / AVR permettant de mesurer la tension d'alimentation du microcontrôleur.
 */

/* Fonction setup() */
void setup() {
 
  /* Initialisation du port série */
  Serial.begin(115200); 
  Serial.println(F("VCC-O-Meter"));
}

/** Mesure la référence interne à 1.1 volts */
unsigned int analogReadReference(void) {
  
  /* Elimine toutes charges résiduelles */
  ADMUX = 0x4F;
  delayMicroseconds(5);
  
  /* Sélectionne la référence interne à 1.1 volts comme point de mesure, avec comme limite haute VCC */
  ADMUX = 0x4E;
  delayMicroseconds(200);
  
  /* Active le convertisseur analogique -> numérique */
  ADCSRA |= (1 << ADEN);
  
  /* Lance une conversion analogique -> numérique */
  ADCSRA |= (1 << ADSC);
  
  /* Attend la fin de la conversion */
  while(ADCSRA & (1 << ADSC));
  
  /* Récupère le résultat de la conversion */
  return ADCL | (ADCH << 8);
}

/* Fonction loop() */
void loop() {
  
  /* 
   * Idéalement :
   * VCC = 5 volts = 1023
   * Référence interne = 1.1 volts = (1023 * 1.1) / 5 = 225
   * 
   * En mesurant la référence à 1.1 volts, on peut déduire la tension d'alimentation réelle du microcontrôleur
   * VCC = (1023 * 1.1) / analogReadReference()
   */
  float tension_alim = (1023 * 1.1) / analogReadReference();
  Serial.println(tension_alim, 3);
  delay(1000);
}

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).

Ne pas utiliser analogReference() / AREF avec ce code !

Pour fonctionner, le code ci-dessus (et ci-dessous) a besoin de configurer la référence de tension à GND puis à VCC. Si une tension est injectée sur la broche AREF, il y a aura création d'un court-circuit. Si vous utilisez la broche AREF, n'utilisez pas ce code.

Le résultat

Voici ce que donne le code ci-dessus avec une de mes cartes Arduino UNO :

1
2
3
4
5
6
7
8
VCC-O-Meter
4.871
4.871
4.892
4.892
4.892
4.892
4.892

Comme on peut le voir, la tension d'alimentation n'est pas de 5.0 volts, comme dans la théorie. La différence est minime, mais suffisante pour causer des erreurs de calculs ou d'imprécision dans certains cas. En utilisant cette valeur mesurée, on peut corriger en partie le problème.

N.B. Dans l'idéal, pour des applications à haute précision, il serait plus adapté d'utiliser une référence de tension sur le broche AREF. La référence de tension interne des microcontrôleurs AVR est précise à +/- 80mV en moyenne.

Bonus : le code version Arduino Mega / ATMega2560 et Arduino Leonardo /Micro / ATMega32U4

Tableau de valeurs pour ADMUX du microcontrôleur ATMega2560

Les valeurs pour ADMUX

Les microcontrôleurs Atmega2560 (cartes Arduino Mega) et ATMega32U4 (cartes Arduino Leonardo et Micro) nécessitent un code bas niveau très légèrement différent pour mesurer la tension de référence interne de 1.1 volts.

Le code ci-dessous est compatible avec les microcontrôleurs ATMega328p (cartes Arduino UNO), Atmega2560 (cartes Arduino Mega) et ATMega32U4 (cartes Arduino Leonardo et Micro) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * Code Arduino / AVR permettant de mesurer la tension d'alimentation du microcontrôleur.
 */

/* Fonction setup() */
void setup() {
 
  /* Initialisation du port série */
  Serial.begin(115200); 
  Serial.println(F("VCC-O-Meter"));
}

/** Mesure la référence interne à 1.1 volts */
unsigned int analogReadReference(void) {
  
  /* Elimine toutes charges résiduelles */
#if defined(__AVR_ATmega328P__)
  ADMUX = 0x4F;
#elif defined(__AVR_ATmega2560__)
  ADCSRB &= ~(1 << MUX5);
  ADMUX = 0x5F;
#elif defined(__AVR_ATmega32U4__)
  ADCSRB &= ~(1 << MUX5);
  ADMUX = 0x5F;
#endif
  delayMicroseconds(5);
  
  /* Sélectionne la référence interne à 1.1 volts comme point de mesure, avec comme limite haute VCC */
#if defined(__AVR_ATmega328P__)
  ADMUX = 0x4E;
#elif defined(__AVR_ATmega2560__)
  ADCSRB &= ~(1 << MUX5);
  ADMUX = 0x5E;
#elif defined(__AVR_ATmega32U4__)
  ADCSRB &= ~(1 << MUX5);
  ADMUX = 0x5E;
#endif
  delayMicroseconds(200);

  /* Active le convertisseur analogique -> numérique */
  ADCSRA |= (1 << ADEN);
  
  /* Lance une conversion analogique -> numérique */
  ADCSRA |= (1 << ADSC);
  
  /* Attend la fin de la conversion */
  while(ADCSRA & (1 << ADSC));
  
  /* Récupère le résultat de la conversion */
  return ADCL | (ADCH << 8);
}

/* Fonction loop() */
void loop() {
  
  /* 
   * Idéalement :
   * VCC = 5 volts = 1023
   * Référence interne = 1.1 volts = (1023 * 1.1) / 5 = 225
   * 
   * En mesurant la référence à 1.1 volts, on peut déduire la tension d'alimentation réelle du microcontrôleur
   * VCC = (1023 * 1.1) / analogReadReference()
   */
  float tension_alim = (1023 * 1.1) / analogReadReference();
  Serial.println(tension_alim, 3);
  delay(1000);
}

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).

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.