La bibliothèque CPUVolt expliquée
Posted on dim. 29 décembre 2024 in Electronics/ Programming
Introduction¶
Pendant des années, lorsque la question « comment mesurer la tension de la batterie qui alimente mon Arduino » était posée sur les forums, la réponse était « mesurer la tension au travers d’un diviseur de tension, afin que celle-ci soit comprise entre 0 et 1,1 V, et définir la référence de l’ADC comme étant la référence interne à 1,1 V ».
Bien que fonctionnelle, cette solution nécessite des composants externes (2 résistors pour le diviseur de tension), de monopoliser un pin analogique (pour la lecture de tension), et éventuellement de faire des allers-retours dans le code entre la référence interne 1,1 V et la référence VCC (suivant les projets).
Avec la sortie en 2017 de sa note d’application AN2447 (voir références), Microchip nous a donné un moyen de mesurer la tension d’alimentation un peu plus « économe » en ressources externes. Il aura cependant fallu attendre 2022/2023 pour qu’une bibliothèque simple d’utilisation, masquant les mécanismes internes, ne voie le jour : CPUVolt.
Pendant longtemps, le fonctionnement décrit par l’AN2447 m’a paru obscur, et ce n’est que très récemment que j’ai pris le temps d’en comprendre le fonctionnement.
J’espère donc que ce post vous permettra de comprendre plus facilement le principe d’application de l’AN2447 & sa mise en œuvre dans CPUVolt
.
Principe de fonctionnement électronique et mathématique¶
Habituellement, on mesure une tension variable en fonction d’une référence « fixe ». Le convertisseur analogique numérique renvoie alors une valeur adimensionnelle entre 0
et VALEUR_MAX
, que nous pouvons ensuite convertir en mV, en pourcentage…
Sur un arduino, la valeur maximale est de 1 023, et signifie que la tension mesurée est égale à la tension de référence.
Si la tension de référence est de 5 V et que la tension mesurée est de 2,5 V, le CAN renverra 1023/2 = 512.
Ici, on vient faire le contraire : on définit notre tension d’alimentation (variable) comme étant la référence, et on vient mesurer la valeur du 1,1 V interne. Comme la sortie du CAN est ratiométrique, on aura toujours un résultat représentant un pourcentage de la tension de référence, et nous avons juste à adapter un peu notre calcul.
Prenons un exemple.
Nous avons une tension d’alimentation de 3,3 V, qui devient notre tension de référence.
Nous lisons ensuite la valeur du 1,1 V interne. 1,1/3,3 = 1/3. Le CAN renverra donc 1/3×1023 = 341.
Prenons ensuite un petit produit en croix : si notre ADC mesure une tension égale à la tension de référence (VCC), il renverra 1023. En mesurant V1.1V, il renverra une valeur \(Res_{ADC}\).
En ré-organisant les termes de l’équation, on obtient
Dans l’équation ci-dessus, le résultat est donné en volts. Il faut le multiplier par 1000 pour avoir des millivolts.
Fonctionnement interne du microcontrôleur¶
La bibliothèque CPUVolt
gère pour nous les changements internes pour réaliser la mesure du 1.1 V adossé à Vcc.
On peut toutefois s’intéresser aux opérations à réaliser pour arriver à ce résultat.
L’AN2447 nous indique, dans l’exemple donné pour l’ATMEGA328PB, qu’il faut modifier le registre ADMUX
pour 1) définir la référence et 2) définir la voie d’entrée.
Pour savoir quoi modifier et comment, on peut se reporter aux tableau 3-1 et 3-2 page 9 (annotés ci-dessous).
Il reste également un détail à régler, la présentation des résultats de l’ADC via le bit ALAR
du registre ADMUX
. Comme on veut que les bits soient alignés avec le bit de poids faible le plus à droite possible, il faut définir ALAR
à 0.
On veut donc que, à la fin de notre opération, notre registre ressemble à ceci : 010X 1110
. Comme le bit 4 n’est pas utilisé, on peut lui donner la valeur que l’on souhaite, et définir en un coup l’ensemble du registre avec ADMUX = 0b01001110;
.
On peut maintenant démarrer la conversion avec ADCSRA |= (1<<5)
(on met le bit ADCSC
, « ADC Start Conversion » à 1), ou en utilisant le « raccourci » fourni par la bibliothèque avr
, _BV(bit)
: ADCSRA |= _BV(5)
. La macro _BV(bit) met le bit sélectionné à 1.
Une fois que la conversion est terminée, le bit ADCSC
, sera mis à 0. Nous pouvons détecter ce changement d’état pour passer à la suite du traitement avec la fonction bit_is_set(register, bit)
fournie par la bibliothèque avr
. Cette fonction renvoie 1 si le bit est à 1, 0 sinon.
Une fois que la conversion est terminée, on peut alors récupérer le résultat de la mesure, stocké dans les bytes ADCL et ADCH. Il faut alors recombiner les deux bytes pour obtenir notre valeur mesurée, sans dimension, puis appliquer le calcul donné ci-dessus pour avoir des millivolts.
while(bit_is_set(ADCSRA, ADCSC)); // On attend
// Si on a passé le while, c’est que la conversion est terminée, et qu’on peut récupérer les données
uint8_t low = ADCL;
uint8_t high = ADCH;
unsigned long resultat = (ADCH<<8) | ADCL;
resultat = (1.1 * 1023 * 1000) / resultat; // Le ×1000 permet d’avoir des millivolts.
Limitations¶
Microchip donne toutefois quelques limitations à cette méthode :
- la linéarité de l’équation de conversion n’est pas parfaite ;
- le microcontrôleur choisi doit permettre de définir VCC comme référence externe, et de définir la référence interne comme entrée du CAN.