Tutoriel · Débutant · 30 min de lecture

Lire des tensions analogiques : références CAN et diviseurs

analogRead rend un rapport, pas une tension. Choisissez une référence stable, dimensionnez un diviseur, et construisez un moniteur de batterie qui dit vrai.

Introduction

analogRead() est la fonction la plus accueillante sur Arduino et l’une des plus silencieusement trompeuses. Elle rend un nombre entre 0 et 1023, tout le monde traite ce nombre comme une tension, et la plupart du temps rien ne se passe mal.

Cela se passe mal sur un robot, car un robot a des moteurs, et les moteurs déplacent cela même par rapport à quoi ce nombre est mesuré.

analogRead rend un rapport

Le CAN ne mesure pas des volts. Il compare son entrée à une référence et rend compte du rapport :

reading = 1023 × V_in / V_ref

Sur un Arduino Uno, V_ref vaut par défaut AVCC — le rail 5 V de la carte. Donc analogRead() répond vraiment « quelle fraction du rail 5 V est ceci ? », et le convertir en volts en multipliant par 5/1023 n’est correct que tant que le rail vaut exactement 5 V.

Vous savez déjà que ce n’est pas le cas, dès que les moteurs tournent.

La référence est le bogue

Câblez une batterie via un diviseur vers A0 et regardez le nombre quand les moteurs démarrent.

Deux tracés superposés. Celui du haut montre les pas CAN en fonction de la tension du pack de 6 à 8,4 volts pour deux conceptions : une référence rail 5 volts avec diviseur 20k/10k montant d'environ 409 à 573 pas, et une référence interne 1,1 volt avec diviseur 68k/10k montant d'environ 715 à 1002. Celui du bas montre la tension de batterie annoncée par chaque conception pour un pack stable de 7,4 volts quand le rail 5 volts réel varie de 4,2 à 5,2 volts : la conception référencée au rail chute de 8,8 volts à 7,1 volts tandis que la conception à référence interne reste à 7,4.
En bas : le pack ne bouge jamais. Quand le rail s'affaisse de 5,0 V à 4,2 V en charge moteur, le code qui croit à une référence 5 V annonce la batterie montant de 7,4 V à 8,8 V. La référence bandgap annonce 7,40 V tout du long. En haut : les mêmes 10 bits étalés sur 286 pas au lieu de 164, car le diviseur a été dimensionné pour remplir la référence. Télécharger le SVG

Une coupure batterie basse construite sur la première conception fait l’inverse de son travail : plus le robot travaille dur, plus la batterie semble en forme. Et la panne est silencieuse, car chaque lecture individuelle est un rapport parfaitement valide.

Le correctif est une référence qui ne bouge pas. L’ATmega328P embarque une référence bandgap 1,1 V :

analogReference(INTERNAL);   // 1.1 V on an Uno; INTERNAL1V1 on a Mega
analogRead(A0);              // discard — the reference needs a moment to settle

Jetez toujours la première lecture après un changement de référence. La référence interne et le condensateur d’entrée ont besoin de temps, et le premier échantillon est pris avant qu’ils l’aient eu.

Dimensionner le diviseur

La référence vaut désormais 1,1 V et votre pack 8,4 V, le diviseur doit donc ramener le haut de plage juste sous la référence :

V_adc = V_pack × R2 / (R1 + R2)

Avec R1 = 68 kΩ et R2 = 10 kΩ, le rapport vaut 0,128, donc un pack plein de 8,4 V présente 1,077 V — juste sous la référence, en utilisant presque toute l’échelle. Trois nombres à vérifier sur tout diviseur conçu :

Vérification Ce diviseur Pourquoi
Haut de plage contre référence 1,077 V contre 1,1 V Au-dessus de la référence et ça écrête à plat
Courant de repos 108 µA Draine 2,6 mAh par jour — négligeable face à 2000 mAh
Impédance de sortie (R1∥R2) 8,7 kΩ Garder sous ~10 kΩ, voir ci-dessous

Comparez avec la version naïve — un diviseur 20 kΩ/10 kΩ lu contre le rail 5 V. Il place un pack plein à 2,80 V, soit 573 pas sur 1023 possibles, et il utilise 164 pas sur toute la plage 6,0–8,4 V du pack contre 286 pour la meilleure conception. Presque deux fois la résolution, pour les deux mêmes résistances et une ligne de code.

La résolution n’est pas la précision

Dix bits contre une référence 5 V font 4,88 mV par pas ; contre la référence 1,1 V, 1,07 mV. Aucun de ces nombres n’est votre précision.

Ce qui vous limite vraiment :

  • Tolérance de référence. La bandgap vaut nominalement 1,1 V et varie de quelques pour cent entre puces. C’est une erreur d’échelle fixe, donc mesurez-la une fois par carte : injectez une tension connue, lisez les pas, et résolvez la vraie référence. Stockez cette constante. C’est une étape d’étalonnage, pas un raffinement optionnel.
  • Bruit. L’alimentation d’un robot est bruitée, et moyenner est la réponse bon marché. Moyenner N échantillons réduit le bruit aléatoire de √N — 16 échantillons sont 4 fois plus silencieux, un bit utile de plus. Cela ne fait rien pour une erreur d’échelle.
  • Impédance de source. Le CAN charge un petit condensateur d’échantillonnage-blocage via ce à quoi il est connecté. Une source haute impédance ne peut pas le remplir dans le temps imparti, et le résidu du canal lu précédemment est encore dedans.

Ce dernier point produit un bogue distinctif à nommer.

Des voies qui ressemblent à leurs voisines

Lisez A0 puis A1 avec des sources haute impédance et chaque lecture est tirée vers la précédente. Balayez un potentiomètre sur A0 et regardez A1 bouger en sympathie. Rien n’est mal câblé.

Deux correctifs, utilisables ensemble :

int readSettled(uint8_t pin) {
  analogRead(pin);        // charge the sample-and-hold on this channel
  return analogRead(pin); // then take the one you keep
}

Lire deux fois et jeter la première donne au condensateur une seconde fenêtre d’échantillonnage pour atteindre la nouvelle tension. L’autre correctif est de baisser l’impédance de source — d’où le diviseur ci-dessus en 68 kΩ/10 kΩ plutôt que le 100 kΩ/100 kΩ plus net d’apparence qui donne 50 kΩ d’impédance de sortie.

Où cela apparaît sur un robot

  • Suivi de batterie, comme ci-dessus — et c’est ce qui rend une coupure basse tension assez fiable pour protéger un pack Li-ion.
  • Capteurs de ligne analogiques. Un réseau de réflectance IR donne une valeur continue par capteur, ce qui permet à un suiveur de ligne de calculer une position lisse plutôt qu’un drapeau gauche/droite. Ces capteurs sont lus contre le même rail que les moteurs affaissent, d’où une dérive d’étalonnage du suiveur de ligne quand la batterie se vide.
  • Mesure de courant, un shunt ou capteur à effet Hall dont la sortie est une tension.
  • Potentiomètres comme boutons de réglage, le moyen le plus amical de régler un gain PID sans reflasher.
  • Pas pour un capteur 5 V sur carte 3,3 V. Un ESP32 ou un Raspberry Pi exige le diviseur pour la protection, pas seulement pour la plage.

Quand ça tourne mal

Symptôme Généralement
La lecture batterie monte quand les moteurs démarrent Référencée au rail affaissé ; utilisez INTERNAL
Lecture bloquée à 1023 Entrée au-dessus de la référence — rapport de diviseur trop élevé
Une voie recopie celle lue avant elle Impédance de source trop haute ; lisez deux fois et jetez
Les lectures sautent de dizaines de pas Bruit d’alimentation ; moyennez, et vérifiez les rails d’alimentation
Tension systématiquement fausse de quelques pour cent Référence interne non étalonnée — mesurez-la une fois
Première lecture fausse après analogReference() Attendu ; jetez-la
L’étalonnage du capteur de ligne dérive quand la batterie se vide Lectures référencées au rail sur un rail qui chute
Seulement 15 % de la plage utilisée Diviseur dimensionné pour 5 V lu contre 1,1 V

La règle qui évite l’essentiel : décidez de votre référence avant tout le reste. Chaque nombre rendu par le CAN est relatif à elle, donc une référence qui bouge fait de chaque lecture une histoire sur deux choses à la fois.

Explorer le graphe

Fait partie de ces réalisations

Les projets et parcours d'apprentissage qui incluent ce tutoriel.

Pour aller plus loin

Références