PrettyPrint

domenica 17 dicembre 2023

Arduino UNO R3: "correggere" la tensione di riferimento interna

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:


Tale valore, infine, verrà memorizzato nella memoria EEPROM della scheda e richiamato, all'occorrenza, nel software che impiega la tensione di riferimento da 1,1V.

Evidentemente, tale procedura andrà ripetuta ogni volta si cambia la scheda Arduino UNO R3 ovvero il microcontrollore ATmega328, essendo la tolleranza di fabbricazione del bandgap interno piuttosto variabile.

La procedura è stata testata solo su questo microcontrollore, ma dovrebbe funzionare anche per altri micro ATmegax che hanno un bandgap interno da 1,1V

Altri metodi reperibili in rete, usano un procedimento simile, che raggiunge lo stesso obiettivo finale di ricavare il coefficiente di correzione:  "giocando" con le configurazioni dei registri interni del micro, si misura la tensione del bandgap, usando come tensione di riferimento quella di alimentazione 5V, di default; poi, con una formula inversa di quella di ricostruzione, si calcola la tensione di riferimento che ne scaturisce (più piccola o più grande di 5,0V a seconda se la tensione bandgap effettiva è, rispettivamente, minore o maggiore del valore nominale di 1,1V). Alla fine si confronta questo valore calcolato con il valore misurato "reale" della tensione di alimentazione di Arduino, mediante il DMM, e da qui si ricava lo stesso coefficiente di correzione. 

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

E' stato usato un trimmer resistivo da 10K, collegato all'ingresso analogico A0, regolandolo per una tensione intorno ai 0,7V:






MISURA TENSIONE VR'' CON DMM:


SOFTWARE

1)  BANDGAP_1V1_CAL.INO

Calcolo del fattore di correzione e salvataggio su EEPROM

/****** 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):




Il programma procederà nell'esecuzione, calcolerà il fattore correttivo, lo stamperà e lo salverà nella locazione di memoria EEPROM (valore predefinito 0, che può essere cambiato nella sezione dichiarativa: costante EEADD):






Con il successivo sketch, si andrà a verificare l'effettiva scrittura in EEPROM del fattore di correzione.

2)  BANDGAP_1V1_TEST.INO

Stampa su monitor seriale del fattore correttivo salvato su EEPROM

/*
 * 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:



USARE IL VALORE MEMORIZZATO NELLO SKETCH UTENTE

Nello sketch utente, ovvero lo sketch che impiegherà questo valore occorrerà innanzitutto inserire all'inizio la direttiva di inclusione del file di intestazione della libreria EEPROM:

#include <EEPROM.h>

A seguire, nella sezione globale, si defineranno la costante int EEADD e la variabile float CAL  e la costante  int VREF=1.1

Nel corpo della setup() si richiamerà la seguente funzione di assegnazione del valore letto dalla EEPROM alla variabile CAL:

EEPROM.get(EEADD, CAL);

Nella formula di ricostruzione della tensione analogica, la costante VREF andrà ovviamente moltiplicata per la costante CAL. Esempio:

 int N = analogRead(A0);

 float v = CAL*VREF*N/1024.0;