Uno dei principali problemi legati alle letture effettuate con l'ADC di Arduino è il valore del riferimento di tensione usato per confrontare le letture.
In base al modello di Arduino è possibile usare il riferimento a 5V, a 3V3, a 2.56V e a 1.1V.
Il valore del riferimento a 5V è preso direttamente dall'alimentazione Vcc del microcontrollore (ad esempio nella UNO). E' possibile con una semplice procedura leggere il valore di Vcc in modo da poterlo usare nelle formule di conversione dell'ADC.
A pag. 252 del datasheet del 328P (http://www.atmel.com/Images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet.pdf) c'è la formula per le letture effettuate dall'ADC.
Ovvero:
ADC = (Vin * 1024) / Vref
Noi infatti per ottenere il valore di lettura usiamo
volt = ADC * Vcc / 1024.0
Per ottenere i millivolt, spesso più utili
milliVolt = ADC * Vcc * 1000.0 / 1024.0
Se noi andassimo a leggere modificando i registri interni il valore del riferimento di tensione di 1.1V con l'ADC avremo che la tensione è proporzionale a Vcc. Quindi ribaltando la formula è possibile risalire a Vcc
Vcc = (Vref * 1024) / ADC
sostituento il valore di Vref e passando ai milliVolt la formula diventa
Vcc = (1.1V * 1000 * 1024) / ADC
ovvero
Vcc = 1126400L / ADC
long readVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
return result; // Vcc in millivolts
}
Per ottimizzare le letture esistono altri accorgimenti tra cui la possibilità di disabilitare il collegamento dei pin digitali dai pin analogici
DIDR0 = B00111111; // Disable unused digital buffers.
e altri accorgimenti di natura hardware che lascio ad utenti più esperti da questo punto di vista.
p.s. il codice della funzione prevede la moltiplicazione con valore massimo dell'ADC ovvero 1023.0.
Devo studiare bene i passaggi e fare delle verifiche strumentali.
Come ti dicevo nell'altro thread, vero che abbiamo 1024 possibilità, ma sono da 0 a 1023, ovvero l'ADC a 10 bit ritorna un numero che è compreso tra 0 (0x0000) e 1023 (0x03FF).
Ora, quando ritorna il massimo 0x03FF indica 5V quindi significa che se vuoi ogni singolo step devi dividere per 1023 e non per 1024 ...
5 / 1023 = 0.0048876 il che significa che quando ti ritorna il massimo (1023 appunto) tu hai : 1023 * 0.0048876 = 5.00001
Se tu dividessi per 1024 avresti :
5 / 1024 = 0.0048828 il che significherebbe che, quando ti ritorna il massimo avresti : 1023 * 0.0048828 = 4.99510 che non esatto.
Il problema è in quello scritto nel datasheet. (pag. 252 di quello linkato)
Li la costante è 1024, anche se parla di valore massimo pari a 0x3FF (Vref - 1 LSB, che quindi non è il massino).
ADC Conversion Result
After the conversion is complete (ADIF is high), the conversion result can be found in the ADC Result Registers
(ADCL, ADCH).
For single ended conversion, the result is
where VIN is the voltage on the selected input pin and VREF the selected voltage reference (see Table 24-3 on page
254 and Table 24-4 on page 255). 0x000 represents analog ground, and 0x3FF represents the selected reference
voltage minus one LSB.
Anche l'autore originale usa 1023.
Bisogna fare delle verifiche strumentali con voltmetri che abbiano un accuratezza e precisione maggiore dell'ADC di Arduino.
Messa come la mettono sul datasheet sembrerebbe che l'ADC darà il valore massimo di 0x03FF NON quando Vin == Vref, ma già quando è uno step sotto
Del resto, se è vero che all'interno applica la formula :
ADC = (Vin * 1024) / Vref
avendo poi solo fino a 0x03FF ... non ha altra possibilità ...
La cosa è corroborata dall'ultima frase : "0x000 represents analog ground, and 0x3FF represents the selected reference voltage minus one LSB." in cui dice espressamente che 0x0000 è il valore 0 (GND), ma che il valore massimo di 0x03FF (1023) NON è Vref, ma è il valore 1 step sotto ...
Quindi, se tutto questo è vero ... il range ch si può leggere con l'ADC del ATmega328P NON è da 0 a Vref, ma da 0 a (Vref - (Vref/1024)) ! Ed in tal caso allora mi torna la divisione per 1024 per calcolare ogni singolo passo ...
5 / 1024 = 0.0048828 il che significherebbe che, quando ti ritorna il massimo avresti : 1023 * 0.0048828 = 4.99510 che non esatto.
Ogni step è di 0.00488 V.
Quindi se io fornisco una tensione pari a GND ad un pin analogico in lettura ottengo 0.
E se gli fornisco 0.003 o 0.004 (3 o 4 millivolt) dovrei leggere 1 oppure 0?
E tra 4.995 e 5.000 leggo 1023?
Chi ha un generatore di tensione cosi sensibile da poter verificare questi casi?
Salvo smentite pratiche (... di cui dubito perché significherebbe che il datasheet è sbagliato), la teoria corroborata da quanto c'è scritto sul datasheet è molto chiara :
Vref / 1024 è lo step
quindi ogni valore restituito dal ADC è pari a (Vref / 1024) x valore_fornito_ADC !
Non mi sembra molto complicato XD
La sola cosa da tenere presente è ben specificata nell'ultima frase : "0x3FF represents the selected reference voltage minus one LSB." ...
Ovvero, 1023 (valore massimo che può fornire con 10 bit) rappresenta Vref meno uno step (== meno 1 bit meno significativo) ... cosa, del resto, piuttosto ovvia ... se dividi per 1024 e moltiplichi per 1023
Oh ... intanto una piccola precisazione ...
... stiamo facendo tante belle chiacchiere sulla carta, poi ... o disponete di un generatore di tensione di precisione (... per la Vref) o ... 1020, 1023, 1024 ... non significa nulla XD XD XD
Sto misurando adesso la tensione che Arduino fornisce sui 5V e, indipendentemente dal fatto se siano più o meno ... è che sono stabili solo alla seconda cifra decimale, nel mio caso 4.98V dalla terza in poi ... balla che è una bellezza quindi ... sai che precisione di misura XD XD XD
gpb01:
Paolo, possiedi un generatore di tensione professionale che ti garantisca la precisione dei millivolt ?
No. Infatti non ci sono arrivato.
Durante il caricamento AVRdude mi da errore di verifica. Mentre se carico un blink sullo stesso micro no.
Inoltre se cambio leggermente codice compila e carica, con il primo codice che ho scritto compila e da errore.
Adesso non ho con me tutta l'attrezzatura per fare verifiche.
Ne riparliamo dopo.