lettura mV ad alta precisione

ciao a tutti,

vorrei leggere da uno strumento un segnale (di output) in tensione. Il segnale ha un range da 0 a 500 mV e una precisione di 0,01 mV fino a 200 mV e di 0,1 mV da 200 a 500 mV.
Vorrei quindi poter fare letture fino alla seconda cifra decimale, ma mi "accontento" della prima cifra decimale.

Se non ho capito male, è possibile cambiare la Vref dei canali analogici, settandola per es. a 1,1 V. Ma in tal caso, avendo precisione a 10 bit, avrei passi a 1,1V / 1024 = 1,07 mV.

Qualcuno sa consigliarmi un metodo per fare letture del genere?

Intanto per quella precisione si necessita di un riferimento esterno e preciso, quindi bisogna procurarsi specifici integrati.

no, aspetta
per precisione alla prima cifra decimale intendi una cosa del tipo distinguere tra 498.0 MVe 498.1 mV?
vorrebbe dire una parte su 5000, lascia stare, serve ben più di un arduino e una sorgente ben stabilizzata

Come dice giustamente Standardoil, per quel tipo di precisione occorrono ADC ben più precisi di quello di Arduno e generatori di tensioni di riferimento di ben altro livello.

Se vuoi Arduino puoi usarlo giusto per leggere l'uscita (magari in I2C) di un tale convertitore ADC e visualizzarla su un display ... o poco più.

Guglielmo

speedyant:
Intanto per quella precisione si necessita di un riferimento esterno e preciso, quindi bisogna procurarsi specifici integrati.

Ammettiamo che io abbia un riferimento di tensione molto preciso. Come potrei procedere poi? Il riferimento può essere anche molto piccolo? Tipo 10 mV?

Ha senso amplificare il segnale che vorrei leggere per portarlo sul range 0...5V ?

Le letture dovrebbero essere del tipo:

(poco probabili quelle sotto i 30 mV)
35,23 mV
123,56 mV
212.4 mV
255,2 mV
(poco probabili quelle sopra 250 mV, pressochè a probabilità nulla quelle sopra 300mV)

Amplificando il segnale, diciamo con un guadagno 50, potrei portare es. 50 mV a 2,5V e 500mV a 25V, in tal caso avrei passi da 5 mV che corrispondono a 0,1 mV. Ovviamente dovrei usare un partitore di tensione fornendo i soliti 5V max ad arduino.
Temo di star dicendo un po' di cavolate...

Il riferimento può essere anche molto piccolo? Tipo 10 mV?

No, la tensione minima del Vref è di 1V

Ha senso amplificare il segnale che vorrei leggere per portarlo sul range 0...5V ?
etc etc etc

Al massimo puoi amplificarla di 10X ( mi spieghi che vantaggio avresti ad amplificarla di 50X se poi devi ridurla con un partitore !! ) ed anche in questo caso non potresti apprezzare i decimali
Ma scusa, ti hanno suggerito di usare un ADC esterno, ad esempio c'è l'ADS1115 che costa niente ed è un 16 bit, collegabile in I2C. E mi pare che sul range richiesto abbia un risoluzione di 15µV

mikiti:
Amplificando il segnale, diciamo con un guadagno 50, potrei portare es. 50 mV a 2,5V e 500mV a 25V, in tal caso avrei passi da 5 mV che corrispondono a 0,1 mV. Ovviamente dovrei usare un partitore di tensione fornendo i soliti 5V max ad arduino.
Temo di star dicendo un po' di cavolate...

Beh, l'usare un partitore di tensione giusto dopo esserti amplificato il segnale (apposta) no, non ha molto senso. La risoluzione è come l'energia, bene che vada si conserva, ma non ha mai rendimenti superiori a uno! :smiley:

Comunque la vedo dura fare quelle misure senza strumenti adatti ad un laboratorio, la teoria è facile, poi in pratica accendono un neon nella stanza di fianco e non ti capisci più coi numeri che leggi.

In ogni caso, almeno a livello teorico, con un ADS1115 te la potresti cavare, è un ADC a 16 bit (qui o qui).
Impostato col guadagno corretto, va abbastanza vicino alle tue richieste.

 // ads.setGain(GAIN_EIGHT);      // 8x gain   +/- 0.512V  1 bit = 0.25mV   0.015625mV

La pratica è comunque molto lontana dalla teoria, 0.01mV di accuratezza non saranno mai reali, ma per il costo che ha forse vale la pena provare, i decimi di mV potrebbero essere precisi.

Prima di andare avanti: che cosa vuoi fare, esattamente?

Datman:
Prima di andare avanti: che cosa vuoi fare, esattamente?

in laboratorio abbiamo un vecchio termometro che oltre a dare la misura a display, fornisce un'uscita in mV, con due cifre decimali (perciò 24,45°C corrispondono a 24,45 mV). Vorrei poter monitorare tale uscita, interfacciandola col PC (usando Labiew), con un hardware molto semplice, tipo un arduino. L'interfacciamento non è un problema, la misura sì...
L'alternativa sarà usare i costosi moduli della National In
truments... oppure spendere ancora di più e comprare un altro termometro...

Un multimetro con uscita seriale?

Domanda: il termometro è vecchio, quando è stata fatta l'ultima calibrazione? Perché se vuoi il centesimo di grado dovresti avere un termometro calibrato da recente e non con acqua e ghiaccio ma almeno una cella tripla.

al di la' della precisione del termometro, che condivido
io credo che un prodotto di adafruit un ADS1115 o un ADS1015 (rispettivamente a 16 o 12 bit di risoluzione) possano andare bene, per cominciare, vedi tu se ti serve a 12 bit (una parte su 4000) o 16 bit (una parte su 32000) ma per la differenza di prezzo....
non avevo pensato al multimetro con usicta in seriale, ma è un'idea sopraffina (se zoomx è d'accordo me ne approprio)

e aggiungo, se per caso il termometro avesse un'uscita di calibrazione potresti usare uno degli ingressi che ti avanzano per leggere il valore della calibrazione e auto-correggere la scala della conversione

zoomx:
Un multimetro con uscita seriale?

Domanda: il termometro è vecchio, quando è stata fatta l'ultima calibrazione? Perché se vuoi il centesimo di grado dovresti avere un termometro calibrato da recente e non con acqua e ghiaccio ma almeno una cella tripla.

esatto, il termometro verrà mandato in un centro accreditato, per la ricalibrazione (probabilmente su 15 punti, ovviamente seguendo le procedure per le certificazioni LAT); dato il costo di questa operazione stiamo però indagando sulla eventualità di dotarci di un nuovo termometro di precisione (che purtroppo costa 3000+ euro). All'interno di questa "indagine" ci siamo posti il problema dell'acquisizione dati dal termometro...
Come detto, oltre al display, lo strumento ha un'uscita in tensione.

Probabilmente verrà acquistato anche un calibratore di mA/V, per altre attività, con accuratezza 0,02 mV, che potrebbe essere usato anche per leggere questo "benedetto" termometro. Ma purtroppo quest'altro strumento non ha uscita seriale o comunque interfacciabile col pc. Per inciso, io sarei per l'acquisto di un nuovo termometro ma... la ricerca ha pochi soldi...

Sono andato fuori tema... cmq, l'ipotesi ADS1115 è interessante. Nei 16 bit mi pare di capire che 1 bit è per il segno, quindi avrei quantizzazione su 32768, o sbaglio?

@Standardoil
Anche il multimetro con uscita seriale va calibrato se si vuole quella precisione.

@mikiti
conosco la problematica degli acquisti nel mondo della ricerca.
Tu hai espresso già quello che stavo per suggerirti, conviene un nuovo termometro già tarato con una bella uscita per PC.
Le soluzioni che ti possiamo proporre purtroppo non sono certo certificate e avrai sempre bisogno di qualcosa per il confronto o la calibrazione.
Immagino che il termometro che già avete usi una PT100 di qualità e abbia un buon amplificatore la cui uscita è quella che è messa a disposizione.

Ci sarebbe una terza via, una telecamera che riprende lo schermo e un programma di riconoscimento cifre. Non è un'idea mia, l'ho letto in rete, ci sono progetti con RaspberryPI, la sua camera e openCV.
C'è anche gente che intercetta ed interpreta i segnali che vanno all'LCD ma qui la vedo molto più complicata.

@zoomx
eh, infatti andrebbero certificati tutti gli strumenti usati nella catena di misura. Abbiamo un multimetro idoneo (e certificato) ma senza uscita seriale.
Il termometro in questione è ovviamente con Pt100.
Per la verità, la soluzione di "acquisire" il display la stavo già esaminando, ma senza ricorrere ad un sistema di visione avanzato. Avevo pensato di usare un arduino mega e 15 fotoresistenze. In pratica, dovrei costruire un circuito per leggere i 7 segmenti di cui è composta la cifra, leggerei le ultime 2 cifre (decimali) indicate dal display. Ovviamente ogni 99 dovrei incrementare di 1 °C.
Il display è molto luminoso, per cui dovrei poter leggere facilmente gli on/off dei segmenti. Ovviamente leggerei in analogico. Dovrei usare un delay() molto breve per evitare di perdere troppe letture.

Tenete presente che in realtà a me serve la possibilità di visualizzare un grafico per vedere quando la T misurata dallo strumento si stabilizza intorno ad un certo valore.

Che ne pensate? E' una soluzione molto "agricola" (con rispetto parlando per gli "agricoli") ma forse ha un po' di senso...

Anche quella potrebbe essere una soluzione. Forse l'ho già vista ma in ogni caso mi sembra semplice anche se non elegantissima.

Se cerchi "opencv recognize lcd numbers" o parole chiave simili trovi parecchi risultati. Io ricordavo l'uso del RaspberryPi ma dimenticavo l'uso di un normale PC con una camera USB.

Dunque, ho scoperto che esiste un ADC a 24 bit (l'LTC2400 della Analog Devices) interfacciabile in SPI con Arduino.
Il circuito è linkato sotto:
link
Ho costruito il circuito in questione, usando come riferimento di tensione il chip LT1021BCN8-5.

Per chi non volesse aprire il link sopra e volesse solo vedere il codice, lo riporto sotto:

/* LTC2400 24 Bit ADC Test
* Connect an LTC2400 24 Bit ADC to the Arduino Board in SPI Mode
*
*
*
* KHM 2009 /  Martin Nawrath
* Kunsthochschule fuer Medien Koeln
* Academy of Media Arts Cologne

*/
#include <Stdio.h>

#ifndef cbi
#define cbi(sfr, bit)     (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit)     (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define LTC_CS 2         // LTC2400 Chip Select Pin  on Portb 2
#define LTC_MISO  4      // LTC2400 SDO Select Pin  on Portb 4
#define LTC_SCK  5       // LTC2400 SCK Select Pin  on Portb 5

void setup() {

 cbi(PORTB,LTC_SCK);      // LTC2400 SCK low
 sbi (DDRB,LTC_CS);       // LTC2400 CS HIGH

 cbi (DDRB,LTC_MISO);
 sbi (DDRB,LTC_SCK);

 Serial.begin(57600);
 // init SPI Hardware
 sbi(SPCR,MSTR) ; // SPI master mode
 sbi(SPCR,SPR0) ; // SPI speed
 sbi(SPCR,SPR1);  // SPI speed
 sbi(SPCR,SPE);   //SPI enable

 Serial.println("LTC2400 ADC Test");

}
float volt;
float v_ref=3.0;          // Reference Voltage, 5.0 Volt for LT1021 or 3.0 for LP2950-3

long int ltw = 0;         // ADC Data ling int
int cnt;                  // counter
byte b0;                  //
byte sig;                 // sign bit flag
char st1[20];             // float voltage text

/********************************************************************/
void loop() {

 cbi(PORTB,LTC_CS);             // LTC2400 CS Low
 delayMicroseconds(1);
 if (!(PINB & (1 << PB4))) {    // ADC Converter ready ?
   //    cli();
   ltw=0;
   sig=0;

   b0 = SPI_read();             // read 4 bytes adc raw data with SPI
   if ((b0 & 0x20) ==0) sig=1;  // is input negative ?
   b0 &=0x1F;                   // discard bit 25..31
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;

   delayMicroseconds(1);

   sbi(PORTB,LTC_CS);           // LTC2400 CS Low
   delay(200);

   if (sig) ltw |= 0xf0000000;    // if input negative insert sign bit
   ltw=ltw/16;                    // scale result down , last 4 bits have no information
   volt = ltw * v_ref / 16777216; // max scale

   Serial.print(cnt++);
   Serial.print(";  ");
   printFloat(volt,6);           // print voltage as floating number
   Serial.println("  ");

 }
 sbi(PORTB,LTC_CS); // LTC2400 CS hi
 delay(20);

}
/********************************************************************/
byte SPI_read()
{
 SPDR = 0;
 while (!(SPSR & (1 << SPIF))) ; /* Wait for SPI shift out done */
 return SPDR;
}
/********************************************************************/
//  printFloat from  tim / Arduino: Playground
// printFloat prints out the float 'value' rounded to 'places' places
//after the decimal point
void printFloat(float value, int places) {
 // this is used to cast digits
 int digit;
 float tens = 0.1;
 int tenscount = 0;
 int i;
 float tempfloat = value;

 // if value is negative, set tempfloat to the abs value

   // make sure we round properly. this could use pow from
 //<math.h>, but doesn't seem worth the import
 // if this rounding step isn't here, the value  54.321 prints as

 // calculate rounding term d:   0.5/pow(10,places)
 float d = 0.5;
 if (value < 0)
   d *= -1.0;
 // divide by ten for each decimal place
 for (i = 0; i < places; i++)
   d/= 10.0;
 // this small addition, combined with truncation will round our

 tempfloat +=  d;

 if (value < 0)
   tempfloat *= -1.0;
 while ((tens * 10.0) <= tempfloat) {
   tens *= 10.0;
   tenscount += 1;
 }

 // write out the negative if needed
 if (value < 0)
   Serial.print('-');

 if (tenscount == 0)
   Serial.print(0, DEC);

 for (i=0; i< tenscount; i++) {
   digit = (int) (tempfloat/tens);
   Serial.print(digit, DEC);
   tempfloat = tempfloat - ((float)digit * tens);
   tens /= 10.0;
 }

 // if no places after decimal, stop now and return
 if (places <= 0)
   return;

 // otherwise, write the point and continue on
 Serial.print(',');

 for (i = 0; i < places; i++) {
   tempfloat *= 10.0;
   digit = (int) tempfloat;
   Serial.print(digit,DEC);
   // once written, subtract off that digit
   tempfloat = tempfloat - (float) digit;
 }
}

I pin che uso sull'arduino UNO sono:
LTC_CS 10
LTC_MISO 12
LTC_SCK 13

Non funziona.
Sul monitor seriale leggo:

...
168;  -5,000000  
169;  -5,000000  
170;  -5,000000  
171;  -5,000000  
172;  -5,000000  
173;  -5,000000  
174;  -5,000000  
175;  -5,000000  
176;  -5,000000  
...

(i valori hanno una spaziatura temporale variabile, da 5 a 8 secondi.)
Non avendo collegato alcuna alimentazione in input, mi aspettavo di leggere 0,000000 (o qualcosa di prossimo allo 0).
Ovviamente, collegando qualcosa in input il risultato non cambia.
Ho provato a cablare il CS sul pin 2, ottenendo letture molto rapide ma come sopra (-5,000000).

Ho ricontrollato i collegamenti del circuito (ho stampato un PCB) ma mi sembra non ci siano problemi (anche perchè è molto semplice lo schema, fatto di soli 4 componenti...).

Da cosa può dipendere il problema??

Ho provato a cablare il CS sul pin 2, ottenendo letture molto rapide ma come sopra (-5,000000).

Cioè? Aspe pin 2 o bit 2 di PORTB?

LTC_CS 10
LTC_MISO 12
LTC_SCK 13

I pin fisici di Arduino uno sono quelli, però il codice non lo devi modificare, devi lasciare:

#define LTC_CS 2         // LTC2400 Chip Select Pin  on Portb 2
#define LTC_MISO  4      // LTC2400 SDO Select Pin  on Portb 4
#define LTC_SCK  5       // LTC2400 SCK Select Pin  on Portb 5

perché il codice non si riferisce ai pin fisici di arduino ma alle porte della MCU.

Ciao.

Inoltre

float v_ref=3.0;          // Reference Voltage, 5.0 Volt for LT1021 or 3.0 for LP2950-3

Visto che usi un riferimento a 5V devi cambiare quella riga.

Maurotec:
Cioè? Aspe pin 2 o bit 2 di PORTB?
I pin fisici di Arduino uno sono quelli, però il codice non lo devi modificare, devi lasciare:

#define LTC_CS 2         // LTC2400 Chip Select Pin  on Portb 2

#define LTC_MISO  4      // LTC2400 SDO Select Pin  on Portb 4
#define LTC_SCK  5      // LTC2400 SCK Select Pin  on Portb 5



perché il codice non si riferisce ai pin fisici di arduino ma alle porte della MCU.

l'errore era proprio quello! Grazie!
Infatti ripristinando i valori indicati nel codice originario il sistema funziona.

Come faccio a specificare altri pin di collegamento?
Io ho predisposto il pcb con i pin (uno a fianco all'altro sull'arduino):
GND --> GND
CS --> 13
MISO --> 12
SCK --> 11

Come faccio a fare la media di 10 valori?
Io di solito uso (es.):

int media = 0;
 for (int i=0; i < 9; i++) {
 media = media + analogRead(A0);
 }
 media = media/9;

ma nel codice riportato nel primo post non so proprio come fare....

In realtà ho trovato un altro sketch, in cui posso specificare "facilmente" i pin di collegamento, ma quando provo a fare la media come ho indicato su, il valore non è corretto... non capisco perchè...

zoomx:
Inoltre

float v_ref=3.0;          // Reference Voltage, 5.0 Volt for LT1021 or 3.0 for LP2950-3

Visto che usi un riferimento a 5V devi cambiare quella riga.

ovviamente. A quello c'ero arrivato. Grazie comunque per la nota.