Questo post, come anticipato in spam-bar, e' per discutere della costruzione di uno strumentino portatile (formato tester), in grado di misurare quantita' abbastanza piccole (fino ai microvolt, nanoampere e microohm, forse di piu, da vedere), in modo abbastanza facile e decentemente preciso (anche se non sara' uno strumento da laboratorio)
L'idea e' nata dal fatto che tre diversi strumenti che avevo reallizzato una ventina di anni fa (analogici, all'epoca le MCU costavano troppo, e peraltro io non me ne ero ancora interessato), hanno fatto negli anni una brutta fine, per cui mi era presa l'idea di rifarmi il milliohmetro migliorandolo ... poi la sindrome di Gundam ha iniziato ad uscire dallo scatolone in cui l'avevo relegata ed ha iniziato a suggerirmi modifiche ed integrazioni (ma se la misura di resistenza e' basata sulla misura della tensione ai suoi capi quando ci scorre dentro una corrente nota, allora perche' non misurare anche le tensioni ? ... e gia che ci siamo, perche' non collegare in parallelo ai terminali di misura una resistenza nota e misurarci pure le correnti ? ... e se poi la scala base non basta, perche' non farne due ? ... e ...) ... ed a questo punto ho dato una botta in testa alla sindrome di Gundam e l'ho risigillata nel suo scatolone, che qui gia avevo rifatto lo stampato 4 volte.
Inizialmente l'idea era di usare degli INA, ma nessuno che avesse prezzi decenti aveva anche le caratteristiche che volevo (e poi la complessita' circuitale avrebbe aumentato i componenti, e quindi il rumore), cosi ho preso in considerazione gli ADC che si usano di solito per le celle di carico, e dopo averne scartati alcuni, ho trovato gli ADS1232 ... il datasheet riporta 3.5nA di corrente di ingresso, filtro digitale interno, 10 letture per secondo (piu che sufficenti), e diverse altre caratteristiche che mi hanno fatto decidere di provare ad usarlo (non ultimo il fatto che costano intorno ai 6 Euro l'uno, contro i 30 o 40 di altri modelli con caratteristiche simili)
In teoria, senza considerare errori e rumore, con 24 bit ed il PGA impostato a 2 (fondoscala 1.25V), la risoluzione per ogni bit dovrebbe essere di 74.5 nV, mentre con il PGA impostato a 128, (fondoscala di 19.5mV) avrei una risoluzione per bit di 1.2nV, ma in pratica sono piu ridotte, sia per via del rumore che dei disturbi (ed anche della conversione interna dell'ADC)
Secondo il datasheet, con il PGA a 2 (fondoscala a +/-1.25V), la risoluzione effettiva e' di 23.5bit (consideriamone 23, che non ho nessuna voglia di star li ad affettare i bit), che mi da' una risoluzione effettiva di 149 nV per bit, mentre quella "noise-free" e' di 21.4 (21), che mi da' una risoluzione effettiva di 596 nV per bit ... mentre con il PGA impostato a 128 (fondoscala di +/- 19.5mV), la risoluzione effettiva e' di 21.1bit (21), che da una risoluzione effettiva di 9.3nV, e quella "noise-free" e' limitata a 18.4bit (18), dove avrei una risoluzione "priva di rumore" di 74.4nV (ma va testato se ne vale la pena ... in fondo, a quanti potrebbe mai servire di misurare nanovolt con uno strumento portatile, sul campo ? sono perfino indeciso se aggiungerla, questa scala, nel software, perche' ricordiamoci che questo non e' uno strumento da laboratorio, ma solo una specie di "tester" portatile per valori molto piccoli)
Dato che lo stampato ancora mi deve arrivare, e non posso certo montare uno di quei "cosi" su una millefori, per il momento la discussione verte piu che altro sul come riuscire a tirare insieme un programma decente, che funzioni su un 328PB (PB perche' ne ho in casa un po), e creare le necessarie funzioni per gestire tutto quanto.
La lettura dell'ADC non dovrebbe essere un problema (almeno in teoria), non servono librerie, ha un'interfaccia "pseudo-SPI", serve semplicemente all'accensione dargli una precisa sequenza di impulsi con il pin PWDN per inizializzarlo, poi metterlo alto, lui inizia a leggere, e quando i dati sono pronti manda basso il pin DATA, a quel punto gli si mandano 24 impulsi sul pin CLK e si leggono i 24bit uno alla volta, poi con un ulteriore impulso di clock si "resetta" il pin DATA, che si rimette alto finche' non ci sono altri dati da leggere, e cosi via ... con questa modalita' di funzionamento, non servono temporizzazioni critiche o gestione del flusso dati complicata, si puo fare in modo che sia direttamente l'ADC a controllare il programma della MCU ... nel loop si controla lo stato di DATA, quando va basso, si chiama una funzione che legge, carica tutto in un buffer, fa la media del buffer, poi si chiamano altre funzioni per fare i calcoli, formattare le stringhe in base al tipo di lettura che si sta eseguendo, aggiornare il display, e poi si torna a controllare lo stato del pin ed a leggere se qualche pulsante e' stato premuto, siccome il ciclo si ripete ogni 100mS, dopo la lettura e la media dovrebbe rimanere tempo in abbondanza per fare tutto il resto.
Quindi la funzione e' relativamente semplice secondo il datasheet, quando DATA va basso, bisogna inviare 24 impulsi di clock, larghi minimo 50uS (non c'e' un massimo, quindi il classico delayMicroseconds (4) di Arduino dovrebbe essere ok), e ad ogni ritorno del clock a zero, leggere il corrispondente bit, alla fine si manda un venticinquesimo impulso di clock per resettare DATA alto di nuovo ... inoltre ho deciso di usare un buffer circolare (per ora da 5 elementi) e fare una media fra tutti i valori contenuti nel buffer, per filtrare un po (avro' un ritardo massimo di aggiornamento di 0.5 secondi in questo modo, ma credo sia accettabile per avere un'aggiornamento del display ogni 100mS) ... credo cosi possa funzionare (ovviamente se non ho fatto qualche casino interpretando male gli esempi dei testi):
...
// variabili globali per non dover passare valori qua e la con i return
// dichiaro locali solo quelle che servono solo all'interno della funzione
volatile int32_t bufADC[5] = {0}; // buffer circolare
volatile uint8_t indBuf = 0; // indice buffer
volatile int32_t mediaValori = 0; // media delle letture
...
// PIN_PD6 e' il clock (uscita), PIN_PD5 e' il data (ingresso)
void leggiADC() {
int32_t valoreLetto = 0; // per immagazzinare il valore dell'ADC
for (uint8_t i = 0; i < 25; i++) { // 25 impulsi di clock, ma i dati vengono letti solo per i primi 24
digitalWrite(PIN_PD6, HIGH); // mando alto il clock
delayMicroseconds(4); // aspetto 4ÎĽs (risoluzione minima di delayMicroseconds)
digitalWrite(PIN_PD6, LOW); // mando basso il clock
delayMicroseconds(4); // aspetto 4ÎĽs
if (i < 24) { // per leggere solo dati validi
valoreLetto = (valoreLetto << 1) | digitalRead(PIN_PD5); // leggo ed aggiungo bit per bit a valoreLetto
}
}
if (valoreLetto & 0x800000) { // gestione valori negativi
valoreLetto |= 0xFF000000; // !!! DA CONTROLLARE !!!
}
bufADC[indBuf] = valoreLetto; // aggiorna buffer
indBuf = (indBuf + 1) % 5;
int64_t somma = 0; // fa la media del contenuto del buffer
for (uint8_t i = 0; i < 5; i++) {
somma += bufADC[i];
}
mediaValori = somma / 5; // valore da usare altrove per i calcoli
}
NOTA: qualcuno si e’ divertito a sputtanare il funzionamento del tag “code”, che se non gli si lasciano almeno due righe prima e dopo converte in code l’intero messaggio, oppure spezzetta il codice ogni volta che trova una riga vuota ? … complimenti … ![]()
Secondo voi e' abbastanza corretta, oppure ho fatto cavolate ? ... nota: la gestione del segno l'ho presa da un'esempio in rete che parlava di uscita in complemento a 2, non so se sia corretta cosi.
Per quanto riguarda invece le funzioni di formattazione delle stringhe, e dei mal di testa che mi fanno venire ... Ancora non le posto perche' sono arrivato solo a meta', e neppure so se sia corretta questa meta', dato che la funzione dovra' fare parecchie cose in base al modo di lettura, vedremo piu avanti.
Di sicuro postero' tutto il programma, inclusi files Eagle e gerber, se riusciro' a tirarci fuori qualcosa di funzionante, cosi se qualcuno vorra' farsene uno, potra' farlo ... ma lasciatemi prima farlo funzionare ... per ora posto comunque lo schema elettrico, cosi per farvi divertire (mi raccomando, se iniziate a tirare pomodori, che siano maturi al punto giusto, ma non marci ne acerbi, cosi me li mangio
)
Piu avanti spiego anche il resto, che senno’ il post diventa chilometrico.
EDIT: Dimenticavo lo schema
