Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

Ricerca personalizzata
20
voti

Termometro digitale con PIC18 Pierin e NTC senza utilizzare ADC

Indice

Una piccola introduzione

Ecco che finalmente riesco a scrivere un bell'articolo sulla mia prima esperienza con il PIC18 Pierin ed anche i microcontrollori in generale, a parte Arduino. Ringrazio anticipatamente TardoFreak, senza il quale non avrei creato un bel niente grazie al suo microcontrollore open source. Partiamo dalle basi: come si può leggere dal titolo ho realizzato un termometro digitale, che in qualche modo segnerà dei numeri su 2 piccoli display a 7 segmenti rossi; i dati provenienti dall'NTC sono stati elaborati dal PIC18 il quale infine li ha mostrati sui 2 display già citati. Siccome non ho ancora imparato bene come si utilizza l'ADC del PIC18 ho cercato una via alternativa, cioè quella di usare solo una porta del micro come ingresso (RC1).

A quel punto mi son chiesto: se non posso acquisire dati su più bit ma solo su uno come posso fare? La risposta mi è arrivata subito vedendo un led del PC lampeggiare con frequenza fissa. Così ho deciso di montare un piccolo oscillatore ad onda quadra con una porta NOT a trigger di schmitt ed inserire il mio NTC come resistenza parallela. Poi ho scelto un condensatore per avere una frequenza di 5Hz circa e dal calcolo è risultato 18uF. Valore più vicino disponibile: 22uF elettrolitico.

Progettare e montare il circuito su bread board

Per fortuna a casa ho parecchie bread board usate con i fori allargati dal tempo (le usava mio padre) e così sono riuscito ad inserirci anche il PIC18 Pierin senza tanti problemi in una di quelle. Mancando di convertitori BCD to 7 segmenti ho utilizzato l'intero registro B di I/O per il display delle decine e l'intero registro D per il display delle unità. All'uscita dell'oscillatore ho collegato anche un led per avere un'idea della frequenza a cui lavora (e mi piacciono tanto i led blu). L'alimentazione proviene da un vecchio trasformatore a 16V molto instabile; infatti con un carico piccolissimo, l'impedenza del tester digitale, quest'ultimo segnava circa 18V, mentre con un carico più pesante, una resistenza da 100 ohm, arrivava a 13V. Di norma il costruttore indica 12V così ho utilizzato un LM7812 e morta lì. Ora ho sempre 12V sull'integrato CD40106 per evitare, oltre che a bruciare il dispositivo, che la costante di carica e scarica del condensatore vari in base alla tensione e quindi cambi poi (leggermente) anche la frequenza dell'oscillatore ad onda quadra. Ecco a voi uno schema completo del progetto

Schema

Schema

Qui l'immagine ingrandita

Montando il circuito sono passato subito alla programmazione ed alla calibrazione del termometro........

Scrittura del programma C

Funzione per creare un ritardo

Parte del programma, come il timer software, è stata presa dal codice del modello base che per primo viene testato sul PIC18 Pierin. Molte funzioni invece le ho dovute inventare appositamente come quelle per comandare i display sui registri di I/O B e D.
Comunque ho modificato solo il file main.c, quindi fornirò in seguito solo il codice di questo senza ricopiare anche gli altri presenti quali main.h, configurazione.h, mappa_int.h e main.h.
Ho inoltre utilizzato la libreria math.h per poter usufruire della funzione logaritmo naturale.
Iniziamo ad analizzare il programma: la funzione ritardo permette di far aspettare al programma un certo lasso di tempo prima di proseguire

void ritardo(int tempo)
{
  timer_delay=tempo;
  while(1)
    {
      if(!timer_delay)break;
    }
}

Si inizializza tempo con un valore, per esempio 1000 (il timer software di sistema è gestito da interrupt di 1ms); la variabile tempo viene copiata nel timer_delay che ogni 1ms diminuisce di 1 grazie al timer2 che genera l'interrupt; Il ciclo while permette al programma di continuare solo dopo che il timer software ha raggiunto lo 0.
Quindi la funzione ritarda l'esecuzione del programma di tanti millisecondi quanti ne vengono indicati tra parentesi nella funzione.

Gestione dei display

Per gestire i display ho creato delle semplici funzioni void che scrivono 8 bit sulla porta senza dover ogni volta ricordarmi la corrispondente uscita collegata ad ogni segmento. Con un'esempio sarà tutto più chiaro:

void numero0D(void)
{
LATDbits.LATD7 = 1;
LATDbits.LATD6 = 1;
LATDbits.LATD5 = 1;
LATDbits.LATD4 = 0;
LATDbits.LATD3 = 1;
LATDbits.LATD2 = 1;
LATDbits.LATD1 = 1;
}

Lo schema delle connessioni al display sono riportate nella seguente immagine. Nella funzione non appare lo 0 perchè non ho collegato il punto, dato che era superfluo.

display 7 segmenti

display 7 segmenti

Come si può vedere dalla foto i collegamenti non sono molto logici, (per esempio "segmento a" collegato a 0, "segmento b" collegato a 1 ecc..) perchè tanto avrei dovuto scrivere lo stesso la funzione riportata sopra e in questo modo ho potuto collegare i cavetti jumper nel modo più comodo visto che non sono tutti lunghissimi. Naturalmente la funzione l'ho ripetuta per tutti i numeri (0,1,2,..,9) e sia per il display sul registro B che quello sul D. Andiamo avanti....

Funzione per numeri composti sui 2 display

Mancava ancora una funzione che mi permettesse (dato un numero maggiore o uguale a 0 e minore di 100) di scriverlo sui 2 display. Praticamente di trasformarli in un unico "schermo" che segni il numero su 2 cifre. Più che fantasia mi è costato scriverla (ho qualche lettera della tastiera consumata)... Eccola a voi nella sua semplicità:

int scrivi_numero(int x)
{
if (x == 0){ numero0B(); numero0D();}
if (x == 1){ numero0B(); numero1D();}
if (x == 2){ numero0B(); numero2D();}
if (x == 3){ numero0B(); numero3D();}
if (x == 4){ numero0B(); numero4D();}
if (x == 5){ numero0B(); numero5D();}
if (x == 6){ numero0B(); numero6D();}
if (x == 7){ numero0B(); numero7D();}
if (x == 8){ numero0B(); numero8D();}
if (x == 9){ numero0B(); numero9D();}
if (x == 10){ numero1B(); numero0D();}
if (x == 11){ numero1B(); numero1D();}
if (x == 12){ numero1B(); numero2D();}
if (x == 13){ numero1B(); numero3D();}
//eccetera......................
if (x == 99){ numero9B(); numero9D();}
if (x > 99){ trattiniB(); trattiniD();}
if (x < 0){ trattiniB(); numero0D();}
}

Da notare le ultime 2 righe che servono per segnalare il superamento di 99 e la discesa sotto lo zero. La funzione trattini accende i segmenti centrali del diplay. Nel caso di (x > 99) si accendono entrambi, nel caso di (x < 0) appare la scritta - 0. Quindi inserendo un numero intero nella funzione scrivi_numero(int x) esso verrà visualizzato sul display a 2 cifre. Dopo tutte queste funzioni inizia ora il main vero e proprio......

Il main

Dichiaro un paio di variabili:

double a;
double durata = 0;
double duratas = 0;
double logaritmo1 = 0;
double resistenza;
double temperaturaK = 0;
double temperaturaC = 0;
double resistenza_at_25C = 11740;
double costanteB = 2704;
double temperatura_base = 295;

Serviranno per il calcolo di una temperatura a partire da un tempo. Lo scopo è questo: misurare la durata di una oscillazione positiva e contare ogni millisecondo per 2 (in questo modo trovo subito il periodo invece che il semi-periodo, visto che in realtà si misura solo la semi-oscillazione positiva e non tutto), dividere il periodo per 1000 perchè era in millisecondi, trovare la resistenza ed infine la temperatura. Ecco un po di formule...... Data una certa durata:

Durata(s) = Durata(ms) / 1000

Trovo ora la resistenza dato che:
T = 1{,}4 \cdot R \cdot C
R = T / ( 1{,}4 \cdot C)

E ora, partendo dalla resistenza, di ricava la temperatura:

Temperatura(K) = 1/(((ln(RT/R0))/B)+(1/T0))

R0 sta per la resistenza a T0 che vale 25°C. La B è un parametro caratteristico di ogni NTC che varia tra un minimo di 2500K ad un massimo di 5700K. Nel mio caso vale 2704. Si può calcolare invertendo la formula soprastante a patto che si abbiano a disposizione i dati a due temperature e le relative resistenze.

Infine, si convertono i gradi Kelvin in gradi celsius

Temperatura(C) = Temperatura(K) − 273


Le istruzioni che nel main controllano il timer2, i suoi interrupt e il PLL sono rimaste invariate. Con prescaler che divide per 16, postscaler che divide per 5 e comparatore a 150 si ha un ritardo di 1ms esatto. La relazione tra queste grandezza è:
Ritardo(s)=(PR2 \cdot POST\cdot PRES \cdot 4)/(48.000.000)
Gli I/O sono stati dichiarati nel seguente modo:

// Inizializza la PORTD
TRISD = 0x00;
// Inizializza la PORTB
TRISB = 0x00;
//Inizializza la PORTC
TRISCbits.TRISC1 = 1;


// Mette a 0 tutte le uscite
LATD = 0;
LATB = 0;

Infine riporto il ciclo for infinito che permette al progamma di continuare a calcolare le temperature e mostrarle all'osservatore..........

for(;;)
{	
//Trovo la durata dell'oscillazione per poi arrivare alla temperatura.
//Il primo ciclo while serve a "sintonizzarsi" col circuito in modo che il conteggio 
//non inizi mentre l'oscillatore è già ad un livello alto. Inizializo sempre la durata 
//a 0 altrimenti continuerebbe da dove si era fermato il ciclo precedente


durata=0;


while(!PORTCbits.RC1){}


while(PORTCbits.RC1)
    {
durata = durata + 2;
ritardo(1);
    }			


//Trasformo il ritardo in secondi dopo averlo acquisito in ms
duratas = durata/1000;


//Trovo la resistenza partendo dalla solita formula dell'oscillatore a not triggerate: T = 1,4*RC......
//.....dati i condensatori che posseggo della prima guerra mondiale e gli integrati ancora peggio...
//...ho trovato che 15uF nella formula funzionano meglio di 22uF durante la taratura.
	
resistenza = (duratas / (1.4*0.000015));


//Trovo subito il logaritmo per poi poterlo inserire già calcolato nella formula successiva.....
//..serve solo per una maggiore leggibilità del programma anche per me.




logaritmo1 = log(resistenza/resistenza_at_25C);	
//Eseguo prima il logaritmo per poi inserirlo come variabile nel calcolo successivo........la temperatura
//base (25°C) è espressa in Kelvin. La costante B è un particolare numero 
//compreso tra 2500k e 5700k che è caratteristico di ogni singolo NTC. Esso serve nella formula che lega 
//temperatura e resistenza.




a = logaritmo1/costanteB + (1/temperatura_base);

//Faccio l'inverso per trovare la temperatura in Kelvin;
temperaturaK=1/a;

//Trovo la temperatura in gradi celsius sottraendo 273K.
temperaturaC = temperaturaK - 273;

//Uso la funzione scrivi numero per digitare la temperatura sui 2 display a 7 segmenti.
scrivi_numero(temperaturaC);

//Chiudo il for(;;)}

La parte finale consiste in alcuni calcoli e l'uso della funzione scrivi_numero(int x) per mostrare il risultato sul display a 2 cifre. Se avete domande o correzioni da eseguire non esitate a commentare.
Per scaricare il progetto MPLAB IDE v8.92 completo clicca qua.
Spero che l'articolo sia stato di vostro gradimento alla prossima, Luca
Ecco a voi un breve video--------------------------------------------------------------------------------------

Termometro digitale Pic18 Pierin NTC no ADC youtbe

12

Commenti e note

Inserisci un commento

di ,

sisi ho aperto un argomento sul forum chiedo scusa ma non sapevo come fare

Rispondi

di ,

guidoi8311, credo che dovresti postare questa domanda aprendo un argomento sul forum.

Rispondi

di ,

Salve sono nuovo del forum sto scivendo un programma in c con Hi-Tech per pilotare dei led rgb e tutto funzionaa bene ora dovrei implementare un trimmer da 10k che ho collegato in questo modo: centrale al pin RA0, un terminale in seria ad un condensatore .1k a massa e l'altro volatile. premetto che io utilizzo una pic 16f628a . Ho visto questa configurazione sul manuale del pic basic he usa un comando pot(pin,scale,var) ho buttato giu un paio di righe cercando di imitare il sudetto comando ma non funziona!!! In pratica devo leggere il tempo di scarica del condensatore ma non so dove sbaglio cmq ora vi posto il codice da me scritto: Tempo1=0; TRISA0=0; RA0=ON; DelayUs(10); TRISA0=1; while (RA0==1) { Tempo1++; DelayUs(1); } TRISA0=0; RA0=0; DelayUs(20); eei_write_int(Tempo1,32); return; }

Rispondi

di ,

Anche se con molto ritardo, mi accodo a chi fa i complimenti per l'idea carina :-) Aggiungo che lo stesso concetto si può applicare con un quarzo speciale invece che con un oscillatore RC: http://www.statek.com/products/pdf/Temp%20Sensor%2010162%20Rev%20B.pdf Ovviamente se si punta ad una misurazione accurata e ripetibile, comincia a diventare critico il clock di riferimento.

Rispondi

di ,

Ma in questo modo non devo usare anche un piedino come ingresso per leggere il tempo/frequenza? Comunque un'ottima idea Adesso che faró un progetto simile con una fotoresistenza per misurare la luce ambientale lo proveró sicuro. Grazie :)

Rispondi

di ,

C'e` una tecnica simile che permette addirittura di fare a meno dell'integrato oscillatore. Si collega il condensatore fra piedino del micro e ground, e l'NTC fra piedino del micro e alimentazione positiva. Si programma la porta in out, e si mette in uscita un livello basso. In questo modo il condensatore si scarica. Poi si programma la porta in INPUT e il condensatore comincia a caricarsi con costante di tempo RC verso l'alimentazione positiva. Con un timer guardi quanto tempo passa da quando rilasci l'uscita a quando leggi un livello alto e da li` ricavi la costante di tempo e la temperatura. Il condensatore deve essere molto piu` piccolo rispetto a quello di prima (ma non ho fatto i conti).

Rispondi

di ,

Complimenti davvero!

Rispondi

di ,

Grazie dei suggerimenti che sarebbero da seguire all'istante ma siccome adesso non ho molto tempo di cambiare tutto l'articolo li userò per i prossimi che scriverò :) Dagli errori si impara tanto :)

Rispondi

di ,

Bravo Luca, ho letto con grande interesse il tuo articolo, ottima idea quella di convertire la temperatura in frequenza! I consigli di Pietro sono d'oro, intravedo un ottimo upgrade per un progetto che già di per sé è originale e genuino! Bravo!

Rispondi

di ,

Bene, bene bene. Ottimo articolo i cui link sono già nel sito del PIERIN. :)

Rispondi

di ,

Come prima cosa volevo farti i complimenti per l'articolo.
Volevo però segnalarti un paio di miglioramenti:
1. I display non hanno la resistenza di limitazione, dovresti metterla per avere il pieno controllo della corrente circolante. Ho visto talvolta non utilizzare le R di limitazione per i display, ma venivano alimentati in PWM con D molto stretto (cosa poco gradevole)
2. Potresti alimentare il 40106 a 5V ed usare il port 5V tolerant del Pierin ed evitare quindi il partitore. Inoltre, nel caso in cui volessi o dovessi utilizzare 12V potresti utilizzare una sola resistenza e affidare il clipping ai diodi interni (è sufficiente progettarla in modo che, verso il basso, non scorra una corrente eccessiva a livello alto e che, verso l'alto, la banda passante non sia troppo penalizzata)
3. Devi prevedere una procedura software di taratura del termometro. Tieni conto che la tolleranza di un condensatore elettrolitico è molto grande, quindi la misura della temperatura ne subisce le conseguenze in modo drammatico. Anche tarando il sistema (che va comunque tarato indipendentemente dalla tecnologia utilizzata) va considerato che un condensatore elettrolitico invecchia rapidamente e quindi il circuito andrebbe ricalibrato in modo periodico e frequente. Sarebbe meglio utilizzare un condensatore ceramico multistrato (magari in classe 1), anche a costo di alzare la frequenza.
4.Le funzioni per creare i ritardi interni al microcontrollore non si fanno mai bloccando l'esecuzione di tutto il main, piuttosto si programma un timer interno
5.Per fare la funzione che scrive sul display è preferibile, invece di fare tutti quei controlli, fare un vettore che contenga direttamente il dato da mandare sul port. Si passerà poi in uscita Vect[x], dove x è il numero da scrivere.Gli unici controlli che fai, con questa tecnica, sono solo due: underflow e overflow. Inoltre puoi gestire separatamente i due diplay (in modo da avere un solo vettore per entrambi) facendo una sola divisione intera per 10.
5. Ti sei ricordato di fare il casting tra le varie conversioni tra float, double e int?
In ogni caso l'idea che hai avuto, e cioè di convertire la quantità che vuoi misurare in una frequenza è una idea ottima. Si cerca di fare sempre così in fisica, quando si vogliono fare misure di un certo livello. Devi solo fare attenzione agli errori che introduci nel cammino di conversione.
Ciao e a presto da Pietro.
PS: lo so che sono uno scassa, me lo dicono tutti... :)

Rispondi

di ,

(a tutti piacciono i LED blu) complimenti per l'articolo!

Rispondi

Inserisci un commento

Per inserire commenti è necessario iscriversi ad ElectroYou. Se sei già iscritto, effettua il login.