L'idea di questa procedura mi è nata quando ho dovuto effettuare la correzione software della tensione di riferimento generata dal bandgap entro contenuto nell'ATmega328P, per l'uso con il sensore AD590 senza stadi attivi di condizionamento.
La tensione di riferimento del bandgap interno è estremamente stabile, ottima per misurare range fra 0 e 1,1V, ma ha un'elevata tolleranza di produzione (tipicamente fra 1,0 e 1,2V) che può quindi introdurre errori non trascurabili nel calcolo della tensione misurata dall'ADC.
Il suo utilizzo corretto richiede, quindi, innanzitutto una stima del suo effettivo valore (mediante la procedura di seguito descritta) ma anche lo scarto della prima lettura (che sarebbe non accurato, così come è scritto nel datasheet).
DESCRIZIONE DELLA PROCEDURA
Via software si misura una tensione di prova, VR, compresa fra 0,1 e 1V, ai capi di una resistenza di un partitore (come per esempio un potenziometro), usando nel calcolo, come tensione di riferimento il valore nominale della tensione di riferimento bandgap da 1,1V e si memorizza il valore, che chiameremo VR':
ove N è il numero restituito da analogRead()
Contemporaneamente si misura la stessa tensione mediante un multimetro digitale (con portata 2V=) che chiameremo VR''. Il rapporto fra questi due valori ci darà il coefficiente di "correzione" della tensione di riferimento che verrà usato nei software in cui si impiegherà la tensione di riferimento da 1,1V:
Però, la misura di una tensione di 5V (sempre rimanendo nell'ambito dei più diffusi DMM 3,5 cifre) impone l'uso di portata minima di 20V= , con un'incertezza assoluta di misura, sulle cifre, sicuramente più alta di quando si usa una portata più piccola, come quella usata nella procedura qui descritta (una misura con P=2V infatti ha 3 cifre decimali, mentre una misura con P=20V ne ha solo 2).
HARDWARE
/****** Problema:
* la tensione di riferimento del bandgap interno 1,1V degli ATmegaX, sebbene molto
* stabile, ha una tolleranza produttiva non trascurabile. Conoscere l'effettivo valore,
* dunque, migliora notevolmente il calcolo delle tensioni misurate via software
* che usano come riferimento questa tensione.
*
****** Risoluzione:
* confronta due misure della stessa tensione (compresa fra 0,1 e 1V) applicata a
* un ingresso analogico di Arduino, la prima fatta mediante software
* la seconda mediante un DMM. Dal rapporto fra questi due valori, si determina un
* fattore di correzione, che verrà salvato in una locazione della memoria EEPROM;
* valore che verrà richiamato, successivamente, nel software di lettura A/D
* che utilizza come VREF il bandgap interno.
* La procedura va ovviamente ripetuta a ogni cambio di scheda Arduino/microcontrollore.
*
****** Autore:
* Francesco Parisi, 20231217
*/
#include <EEPROM.h>
const int pinR=A0; // pin collegato al partitore
const float VREF =1.1; // tensione nominale b/gap int.
const int EEADD=0; // ind. EEPROM dove salvare il fatt. correttivo
/*****************************************************/
float VR1; // tensione partitore misurata via software
float VR2; // tensione partitore misurata con il DMM
float CAL; // fattore di correzione calcolato
/*****************************************************/
void setup() {
Serial.begin(9600);
analogReference(INTERNAL); // Vref interna =1,1V nominale
VR1=misuraVR(); // misura ripetuta di VR, via software
Serial.print("\nMisura tensione con bandgap interno: ");
Serial.print( VR1, 3 );
Serial.println(" V");
Serial.println(F("============================================="));
Serial.print("Inserire misura tensione DMM in Volt (es. 0.752): ") ;
while (Serial.available()==0)
{ } // attende input da monitor seriale
VR2=Serial.parseFloat(); // trasforma input in float
Serial.println(VR2,3);
CAL=VR2/VR1;
Serial.print("\n\nFattore di correzione calcolato: ");
Serial.println(CAL, 4);
EEPROM.put(EEADD, CAL); // scrive il val. CAL all'ind. EEADD
Serial.println(F("============================================="));
Serial.print("Fattore di correzione salvato all'indirizzo EEPROM: ");
Serial.println(EEADD);
Serial.println(F("============================================="));
Serial.println("Programma terminato con successo!");
}
void loop() {
}
/* Funzione per la misura ripetuta di VR */
float misuraVR() {
float somma=0; // somma delle letture singole di VR
float vmedio=0; // media delle letture singole di VR
float v; // lettura singola di VR
int n; // numero restituito da analogRead()
int nlett=5; // quante letture singole
for (int i=0; i<nlett+1; i++) {
n=analogRead(pinR);
v=VREF*n/1024;
if (i>0) // prima lettura scartata
somma=somma+v;
delay(50);
}
vmedio=somma/nlett;
return (vmedio); // ritorna la media della misure di VR
}
Caricato sulla scheda il software, si attiverà il monitor seriale. Dopo la stampa della misura software, l'utente inserirà nella casella di input dello stesso, il valore di tensione VR letto con il multimetro (0,766 nell'esempio):
/*
* Test lettura valore CAL memorizzato nella EEPROM
*
*/
#include <EEPROM.h>
const int EEADD=0; // ind. EEPROM dove è stato memorizzato il fatt. correttivo
float CAL; // fattore di correzione memorizzato all'indirizzo EEADD
void setup() {
Serial.begin(9600);
Serial.print("Indirizzo EEPROM :");
Serial.println(EEADD);
EEPROM.get(EEADD, CAL); // legge locazione EEADD e assegna valore a CAL
Serial.print("CAL = ");
Serial.println(CAL,4);
}
void loop() {
}
Attivando il monitor seriale avremo questo esempio di output:
#include <EEPROM.h>
EEPROM.get(EEADD, CAL);
int N = analogRead(A0);
float v = CAL*VREF*N/1024.0;