Corso programmazione PICMicro in C – Lezione 11 (Parte 2/2) – Il convertitore Analogico Digitale – Esempi pratici – Termostato con isteresi con LM35

In questa seconda parte della lezione sul convertitore A/D faremo ben 3 esempi pratici per sfruttarne a fondo le potenzialità e capire sia come funzionano gli strumenti più costosi che si trovano in commercio, sia come utilizzare tali funzioni a nostro vantaggio permettendoci di ottenere dei semplici sistemi di visualizzazione e controllo di valori analogici.

I primi 2 esempi saranno molto semplici e sfrutteremo unicamente un trimmer per variare la tensione in ingresso ad un pin del modulo A/D e visualizzarne il valore su una barra a led o su un display LCD.

Il terzo esempio più che un esempio è un vero e proprio progetto in quanto ci permetterà di avere una buona base da cui partire per poter realizzare un ottimo termostato completo di:

  • Impostazione soglie di intervento alta e bassa
  • Ritardo di intervento
  • Isteresi
  • Visualizzazione del valore medio di temperatura
  • Segnalazione e regolazione dei parametri tramite display LCD
  • Ritenzione delle impostazioni su eeprom interna

Chi sa di cosa sto parlando sa anche che un prodotto commerciale con tali caratteristiche è abbastanza costoso e non di facile realizzazione per un hobbysta alle prime armi. I codici sorgente possono essere scaricati dagli utenti registrati in fondo all’articolo e consiglio di scaricarli e seguirli man mano che do la spiegazione del funzionamento nell’articolo.

Accendere una serie di led in base alla tensione in ingresso

In questo esempio sfrutteremo un trimmer per pilotare un ingresso del convertitore A/D. Avremo sul pin, quindi, una tensione variabile da 0 a VDD (tensione di alimentazione del picmicro): alimentando il pic a 5 volt avremo quindi una tensione variabile da 0 a 5 volt. Sfruttando tutti e 10 i bit di conversione, avremo che ogni bit sul convertitore rappresenterà 5V/1024bit = 0,00488 V/Bit, ovvero 4,88mV/Bit come già spiegato nella prima parte di questa lezione. Fin qui, nulla di nuovo.

Supponiamo, ora, di voler rappresentare il nostro valore in maniera “grafica” riportandolo su una barra composta da 8 led, un po’ come fa un moderno Vu-Meter o uno dei tanti sensori di livello.

Implementando una scala lineare, ogni led sarà acceso a passi di 5v/8led = 0,625V/led = 625mV/led. Accenderemo, cioè, il primo led se la tensione rientra nel range 0-625mV, il secondo led si accenderà nel range 626-1250mV e così via, fino a 5V per i quali tutti e 8 i led saranno accesi.

Anzichè una “barra” si potrà anche accendere un “punto”, questa modifica è semplicissima e vedremo come farla.

Questa è, ad esempio, la funzione che svolgono i classici integrati LM3914 e LM3915 con i quali tanto ci siamo divertiti anni fa a fare i Vu-Meter.

All’uscita del convertitore A/D sappiamo di non avere un valore espresso in millivolt ma un valore intero variabile tra 0 e 1023 e rappresentativo della tensione. Nella fattispecie sappiamo che, alimentando il pic a 5Volt e utilizzando la tensione di alimentazione come riferimento, ogni bit varrà 4,88mV, per cui, ragionando in termini interi di valori del convertitore A/D (il che ci semplifica enormemente le cose) i led dovranno accendersi a passi di 625mv / 4,88 mv/bit = 128bit. Quindi il primo led sarà acceso nel range 0-128, il secondo nel range 129-256 ecc.

In questo video potete vedere il progetto in funzione:

Vediamo quindi come fare. Il circuito che realizzeremo è il seguente:

Gli schemi che mostrerò presenteranno disegnate solo le parti essenziali. Tutta la parte relativa al reset, alimentazione del picmicro, quarzo e connettore ICSP saranno tralasciati. Chi utilizza la Freedom 2 ha già tutto a disposizione e non deve fare nulla se non abilitare la stringa a led e l’ingresso analogico settando gli appositi jumper. Fate riferimento alla documentazione di Freedom II.

Per questo esempio, e quelli che seguono, anche se utilizzeremo nella pratica una sola porta come analogica, setterò per semplicità come analogiche tutte le porte a disposizione, sarete voi, per le vostre applicazioni poi a decidere quali utilizzare scegliendo i valori da impostare nella tabella presente sul datasheet del pic che avete a disposizione.

Come detto nella prima parte, dovremo settare come ingressi le porte da usare come analogiche, sappiamo che questo si fa agendo sui registri TRIStato. Sul pic16F877A le porte analogiche sono distribuite sui banchi A ed E, del banco A la sola porta RA4 non ha funzione analogica, mentre come sapete RA6 e RA7 non ci sono:

TRISA=0b00101111; // RA4 non è analogico, RA6 e RA7 non esistono
TRISE=0b00000111; // le porte RE3,4,5,6,7 non esistono

Settiamo quindi il registro ADCON0 accendendo il modulo A/D, settando la frequenza a 32Tosc (stiamo lavorando con un quarzo da 20MHz e, se avete letto la prima parte di questa lezione, sapete quanto è importante questa impostazione) e selezionando la porta da cui effettuare la lettura, che nel nostro esempio sarà AN1 (collocata su RA1):

ADCON0=0b10001001;

Quando in un’applicazione vogliamo effettuare letture da vari ingressi analogici, basterà cambiare nelle routine unicamente i bit CHS2:CHS0 di ADCON0 rimanendo invariati gli altri, questo può essere fatto o cambiando tutto il valore del registro, impostandolo mano a mano, oppure sfruttando una maschera per cambiare soltanto quei 3 bit. In pratica si realizza una sequenza del genere:

  • Prima impostazione di ADCON0 (fatta per leggere da una specifica porta)
  • Avvio la conversione (ADGO=1)
  • Terminata la conversione eseguo la lettura e salvo il valore letto in Variabile1
  • Cambio ADCON0 per poter leggere da un’altra porta
  • Avvio la conversione (ADGO=1)
  • Terminata la conversione eseguo la lettura e salvo il valore letto in Variabile2
  • Continuo così per tutte le altre porte che voglio leggere
  • Ritorno all’inizio

Settiamo quindi il registro ADCON1, impostando la giustificazione del risultato a destra e le porte scelte come analogiche (tutte nel nostro esempio):

ADCON1=0b10000000;

Nel main, quindi, andremo ad effettuare la lettura del valore e accendere la serie di led in base al valore letto. Diamo innanzitutto il ritardo di 20μS come spiegato nella prima parte, per poter caricare il condensatore di campionamento. Avviamo la conversione e cicliamo di continuo fino a che il bit ADGO non torna a zero indicandoci che la conversione è terminata.

Andiamo quindi ad effettuare la lettura dei registri ADRESH e ADRESL come già spiegato riportando il valore ottenuto in una variabile a 16bit.

Ora, in base al valore ottenuto, accendiamo i led:

Impostando PORTD a 1, si accenderà il solo led presente su RD0 (bit 0 della porta, valore 20), impostandolo a 3 si accenderanno i led presenti su RD0 e RD1 (bit 0 e 1 di portD: 20 + 21), impostandolo a 7 si accenderano i led presenti su RD0, RD1 e RD2 (20 + 21 + 22) e così via, dando l’effetto di una “barra”.

Si capisce che, se invece della barra, vogliamo visualizzare un “punto”, anzichè sommare i valori dei bit ad ogni passo, basterà attivare unicamente il singolo bit (per il secondo led, ad esempio, anzichè attivare, oltre al bit 1, anche il bit 0, attiveremo soltanto il bit 1 e quindi daremo PORTD=2. Daremo PORTD=4 per il terzo led e così via). Nel codice di esempio ho riportato entrambi i valori (per barra e punto).

Come vedete è davvero molto, molto semplice e, ovviamente, al posto del trimmer ci potrebbe essere un sensore di livello.

Utilizzando sensori di livello, o collegando un segnale analogico qualsiasi all’ingresso del picmicro, se questo supera i 5 volt, dobbiamo ridurlo facendo uso di partitori di tensione, se i livelli non sono sufficienti (pochi millivolt in ingresso) o non ci forniscono una buona “dinamica” in grado di sfruttare i 5mv di risoluzione del convertitore A/D, ci sarà bisogno di amplificatori operazionali. In ognuno di questi casi dovremo fare ovviamente degli aggiustamenti al codice per poter rappresentare il valore effettivamente letto.

Riportare il valore letto dal convertitore A/D su un display LCD

In quest’altro esempio faremo la stessa cosa, ma visualizzeremo il valore su un display LCD riportando sia il valore letto direttamente dal convertitore A/D che il valore espresso in mV. Lo schema sarà il seguente:

Questa volta, per semplificarmi le cose, non terrò conto che un bit vale 4,88mV ma utilizzerò il valore intero: 5mV tenendo conto che, per la mia applicazione, è un’approssimazione ragionevole.

In ogni applicazione vi troverete a dover fare degli scontri con valori numerici: spesso per semplificarsi le cose, si preferisce approssimare utilizzando valori interi che non causano particolari problemi al momento della stesura del codice, altre volte abbiamo la necessità si scrivere codici complicatissimi perchè ci vuole una certa precisione ecc.: tutto sta al risultato che dobbiamo ottenere, al tempo a disposizione e all’economicità dell’applicazione.

Le operazioni saranno le stesse dell’esempio precedente, con l’unica differenza che mostreremo i valori sul display. Nella prima riga mostreremo il valore letto dal convertitore A/D:

LCD_GOTO(1,11);
LCD_PUTUN(valore);
LCD_PUTS("   ");

L’ultima riga che scrive 3 spazi ha questa funzione: quando passiamo dallo scivere un valore con un certo numero di cifre, ad un valore con un numero di cifre più basso, rimarranno impresse sul display le cifre rimanenti dal numero precedente, che non vogliamo visualizzare, quindi in coda ad ogni numero stampiamo 3 spazi che servono al caso limite in cui si passa da un numero a 4 cifre a un numero ad 1 cifra. Questa operazione si poteva fare in mille modi diversi, io l’ho fatta nella maniera più sbrigativa.

Sulla seconda riga del display mostreremo il valore in Volts facendo le opportune operazioni matematiche del caso:

tensione=valore*5; // ottengo il valore espresso in millivolts tenendo conto che 1bit=5mV
LCD_GOTO(2,11);
LCD_PUTUN(tensione/1000);// parte intera dei volts
LCD_PUTS(".");
LCD_PUTUN(tensione%1000);// parte decimale
LCD_PUTS("V   ");

Moltiplicando il valore per 5 otteniamo il valore espresso in millivolt per quanto detto prima. Volendo, però, mostrare il valore in Volts, divido quindi per mille, stampando prima la parte intera (ricordo che operando su numeri interi, in C, il risultato è ancora un numero intero), quindi il punto, e infine i decimali con l’apposito operatore % che mi fornisce il resto di una divisione. Stampo infine il simbolo di Volt seguito dai 3 spazi.

Sempre per la questione delle approssimazioni, e per avvicinarmi di più al valore di 4,88 mv/bit potevo anche moltiplicare per 48 o 49 e poi dividere per 10000 (non voglio operare con valori FLOAT: il codice sarebbe complicatissimo e ci sarebbe uno spreco di memoria che per questa applicazione è inutile). Oppure ancora, potevo moltiplicare per 488 e poi dividere per 100000 e così via. Ma, per quest’ultima operazione in particolare la routine display utilizzata mostra valori sballati, non chiedetemi perchè, siate bravi voi a capirlo oppure accontentatevi dell’approssimazione.

Vediamo che, a causa dell’approssimazione utilizzata, il valore massimo di tensione visualizzato è leggermente superiore alla reale tensione di alimentazione, VDD, del picmicro e usata come riferimento. Se vogliamo semplificarci le cose, possiamo anche utilizzare dei riferimenti di tensione esterni da scegliere in maniera tale che il valore in mV di ogni singolo bit sia un valore il più intero possibile.

Termostato con regolazione separata soglie di intervento alta e bassa, con ritardo ed isteresi

Premetto che qui stiamo per affrontare una cosa abbastanza complicata e sapendo che tale argomento potrebbe non risultare semplice per molti, ho cercato quindi di semplificare quanto più mi è possibile. Realizzeremo un termostato con due soglie di temperatura: bassa e alta. Effettueremo una misurazione di temperatura e scatterà un intervento se scendiamo al di sotto della temperatura bassa e un’altro intervento se la temperatura sarà superiore alla soglia alta.

Si può immaginare questo temostato come operante in casa: se fa troppo freddo accendiamo il riscaldamento, se fa troppo caldo accendiamo il condizionatore, oppure ancora in un’acquario con pesci tropicali: il superamento della soglia bassa attiva il riscaldamento, il superamento della soglia massima attiva invece un allarme, un cicalino (una funzione di sicurezza in più che generalmente negli acquari non è prevista).

Per la misurazione di temperatura sfrutteremo un sensore analogico molto diffuso: l’ LM35 prodotto dalla National  Semiconductor (potete scaricarne il Datasheet in fondo all’articolo) e del quale analizziamo ora le caratteristiche.

  • Il sensore di temperatura LM35
Formato TO92

Abbiamo scelto l’LM35 perchè è uno dei sensori di temperatura più famosi, lo si trova a buon mercato, è semplicissimo da collegare in quanto ha solo 3 terminali: 2 per l’alimentazione Vcc e GND e uno con l’uscita analogica; l’uscita, poi, è lineare: fornisce 10mV per ogni grado centigrado (avremo quindi 0mV a 0°C e 1000mV a 100°C). Viene distribuito in vari package, il più comune è il TO92 (quello uguale ai piccoli transistor col corpo plastico a mezzaluna); in tale package è in grado di misurare da -40 a +110°C, invece nel package metallico TO46 è in grado di misurare da -50 a +150°C.

Avendo il nostro convertitore A/D una risoluzione di 5mV è facile capire che potremo apprezzare massimo il mezzo grado (se 10mV=1°C, 5mV=0,5°C), per cui non visualizzeremo mai temperature del tipo 20,8 o 20,4 ma sempre 20,0 … 20,5 … 21,0 ecc. L’accuratezza di tale sonda, comunque, è di 0,5°C per cui tutto il sistema va bene così.

Da notare inoltre che sto specificando grado centigrado in quanto altri sensori sono tarati in gradi Fahrenheit, altri ancora in Kelvin (e ci tengo a precisare: si dice Kelvin NON gradi Kelvin e il simbolo si scrive K e NON °K, come purtroppo anche su alcuni datasheet è indicato), come l’LM335.

Come si può vedere dal datasheet, tale sensore è anche in grado di misurare temperature negative; per poter fare questo, però, ha bisogno di un riferimento di tensione negativo , e quindi abbiamo bisogno di una tensione di alimentazione duale (il riferimento negativo andrà applicato al pin di uscita tramite una resistenza):

Oppure ancora, per la misura di temperature negative può essere utilizzata un’altra configurazione, che prevede un’unica alimentazione ma una seconda uscita che fornisce il riferimento negativo mediante la caduta di tensione di due diodi di tipo comune:

Sappiamo però che all’ingresso del convertitore A/D non possiamo applicare tensioni negative e comunque non ci vogliamo complicare le cose anche perchè tale termostato abbiamo deciso di utilizzarlo in casa, luogo in cui non misureremo mai temperature sotto lo zero (ci mancherebbe!). Pertanto, il sensore, così come lo utilizzeremo, nella sua maniera più semplice, sarà soltanto in grado di misurare temperature positive (a partire però da +2°C e non da 0°C):

In tale configurazione, quindi, l’LM35 non è adatto a fare un termometro da auto!

  • Problematiche da collegamento

Sul datasheet ci sono alcune note molto importanti da non sottovalutare nel momento in cui decidiamo di realizzare una sonda di temperatura remota. Viene difatti specificato che, specialmente utilizzando il package plastico (TO92), la principale fonte di calore per la misurazione proviene dai pin di collegamento, pertanto, nel caso in cui l’LM35 deve monitorare la temperatura di una superficie, i pin e una relativa piccola parte dei cavi di collegamento di essi, devono trovarsi alla stessa temperatura della superficie da monitorare e sulla quale abbiamo appoggiato la superficie piatta della sonda; questo può essere realizzato utilizzando delle apposite resine. Ovviamente tale problema non sussiste nel caso in cui l’LM35 venga utilizzato per monitorare la temperatura ambiente in quanto sia i pin che il corpo si trovano in aria libera e quindi alla stessa temperatura.

La soluzione migliore per realizzare delle sonde (specialmente per determinare temperature di liquidi, per i quali la sonda dovrà lavorare immersa in essi) è utilizzare il package metallico TO46. Nel caso in cui l’LM35 in tale formato dovrà essere utilizzata per fare una sonda da immersione, si dovrà inoltre provvedere ad un’accurato isolamento dei pin di contatto e della superficie metallica dal liquido da controllare, per prevenire fenomeni di corrosione. La sonda nel formato TO46, inoltre, può anche essere saldata direttamente alla superficie da controllare migliorando di molto le prestazioni.

Altro problema è derivato dall’uso di terminali di collegamento molto lunghi che possono introdurre una capacità della linea superiore a 50pF. In questi casi il datasheet consiglia di mettere una resistenza di 200Ω in serie all’uscita o di disaccopiare l’uscita tramite un circuito RC:

C’è un errore nella figura 3 in quanto la resistenza viene indicata come da 2KΩ mentre nel testo dice che è da 200Ω. Suppongo che il valore corretto sia 200Ω.

In ogni caso è sempre meglio mettere anche un piccolo condensatore da 100nF in parallelo all’alimentazione della sonda qualora si trovi a distanza dal circuito di utilizzo.

C’è inoltre da ricordarsi che stiamo utilizzando una sonda analogica, per cui il segnale in uscita può essere sporcato da eventuali disturbi elettromagnetici causati da apparecchiature nelle vicinanze (relè, luci a neon, linee di corrente ecc).

  • Problematiche da interfacciamento ed utilizzo in campo

Un monitoraggio continuo di una tensione in ingresso al convertitore, non sempre fornisce un segnale stabile, potremmo difatti trovarci nella condizione che il segnale misurato sia a cavallo tra un bit e l’altro, per cui il valore mostrato sul display tenderà a fluttuare causando un effetto davvero molto sgradevole e di sicuro non professionale.

Questo sgradevole effetto avrete anche potuto notarlo nei due esempi precedenti in cui non abbiamo fatto uso di nessuna tecnica per evitarlo, proprio perchè ve ne rendeste conto.

In questa applicazione, per evitare questo fenomeno, effettueremo innanzitutto la misura ogni 200millisecondi e non di continuo, e in più, eseguiremo una serie di 20 misure sulle quali sarà fatta la media e quindi solo allora sarà mostrata sul display. Abbiamo quindi una frequenza di aggiornamento (ideale) della visualizzazione di 0,2 sec/misura x 20misure = 4secondi, che, oltre a fornirci una stima più corretta della temperatura reale, ci eviterà la fastidiosa oscillazione dei valori mostrati sul display.

Abbiamo inoltre anticipato che in questo termostato imposteremo due soglie: una bassa e una alta, superate le quali scatterà un intervento (es.: un pin che comanda un relè, o un led o un suono col cicalino ecc). Prima di decidere se intervenire o meno, però, faremo una serie di verifiche per evitare di intervenire inutilmente, riducendo i consumi e allungando la vita degli elementi di intervento (relè, pompe, celle di peltier ecc).

Innanzitutto, appena si esce al di fuori della soglia impostata, sul display sarà indicata tale condizione (lampeggerà una scritta HIGH o LOW se la temperatura misurata ha superato la soglia alta o è scesa al di sotto della soglia bassa rispettivamente), ma ancora non si interverrà. Verrà quindi conteggiato un tempo, preimpostato da codice a 10 secondi (ma modificabile), allo scadere del quale la temperatura verrà nuovamente verificata: se ci troviamo ancora al di fuori della temperatura impostata + una certa soglia (isteresi), allora si interverrà; questo metodo di operare ci da un certo margine di sicurezza nell’evitare continui interventi magari causati da sbalzi di temperatura momentanei (es.: apertura di una porta nei pressi del sensore se stiamo controllando una temperatura ambiente).

L’impostazione di un’ isteresi serve appunto ad evitare la commutazione continua e ve lo spiego con un banale esempio: supponiamo di avere impostato una temperatura di 20°C, al di sopra della quale vogliamo intervenire azionando, ad esempio, una ventola di raffreddamento. Fino a 20°C non ci sarà commutazione del relè che comanda la ventola. Impostiamo quindi un’isteresi di 0,5°C: diciamo in pratica al nostro circuito di controllo che il relè dovrà scattare a 20,5°C (20°C + isteresi di 0,5°C). Si avrà, quindi che, pur avendo impostato 20°C come soglia di intervento, l’intervento scatterà “un po’ più in alto”, ma dovrà cessare, invece, una volta raggiunta la soglia impostata precisamente: 20°. Questo ci permette, appunto, di evitare commutazioni continue a cavallo della temperatura di soglia che, oltre ad essere dannose (e fatali!) ai dispositivi, sono anche vietate per legge in questo tipo di apparecchiature. In tale termostato, oltre all’impostazione dell’isteresi, come detto prima abbiamo anche aggiunto un certo ritardo di invervento (comunque azzerabile) per avere ancora maggiore sicurezza di intervenire solo se necessario.

Per maggiori informazioni su questo argomento potete consultare wikipedia: Isteresi, Termostato

Anche se impostiamo a zero l’isteresi del termostato presentato in questo esempio, ci sarà sempre un’isteresi fissa di 0,5°C non eliminabile e vi spiego perchè. Facciamo l’esempio della temperatura di soglia alta. Abbiamo impostato da codice che la segnalazione interviene solo se la temperatura è maggiore del valore impostato e NON maggiore o uguale. Essendo il nostro sistema (convertitore a 10 bit + sonda LM35) capace di apprezzare al massimo 0,5°C, vuol dire che già la segnalazione di allarme alto avverrà ad una temperatura di 0,5°C più alta di quella impostata, e quindi 20,5°C nel nostro esempio (ripeto: controlliamo una temperatura maggiore e non maggiore o uguale), e questa è una soglia di isteresi fissa che non possiamo eliminare (e a mio avviso non è uno svantaggio ma una nota positiva).

I valori di isteresi e di ritardo di intervento possono essere modificati da codice per poterli adattare al luogo in cui si dovrà intervenire tenendo conto di una moltitudine di parametri (velocità di diffusione/perdita del calore nel mezzo da controllare, eventuali interferenze esterne ecc), ma i più bravi sapranno di certo parametrizzare tali valori rendendone possibile anche la regolazione di questi dal display.

Il ritorno alle condizioni normali (reset degli azionamenti se la temperatura rientra nel range) è anch’esso vincolato al ritardo di azionamento (ma non all’isteresi per quanto detto prima, altrimenti tutto questo ambaradàn non avrebbe più senso!).

Un sistema così fatto (isteresi+ritardo di azionamento+lettura mediata della temperatura) ritengo sia abbastanza professionale ma, prima di poterlo applicare ad un sistema di termoregolazione casalingo, studiatene bene il funzionamento e segnalate eventuali bug, in quanto non garantisco nulla e il codice esposto è solo a titolo di esempio.

Altre caratteristiche di questo termostato sono:

  • Tempo di inutilizzo: se ci troviamo nella modalità SET e rimaniamo per lungo tempo senza far nulla, in automatico si uscirà dalla modalità set senza salvare i parametri.
  • Controllo impostazioni: sarebbe assurdo settare la temperatura minima con un valore superiore a quella massima; se ciò viene fatto, all’uscita della modalità SET saranno ripristinati i valori iniziali.

Potete vedere nel seguente video come funziona il termostato presentato in questo articolo:

  • Descrizione circuito e funzionamento

Lo schema è il seguente:

Utilizzando la Freedom II dovete tener conto di alcune cose: l’alimentazione non può essere data direttamente dal pickit perchè i pulsanti sulle linee RB7 e RB6 interferiscono con le linee di programmazione. Dovrete abilitare la stringa a led insieme all’LCD, per cui non preoccupatevi se i led si accenderanno a casaccio: riflettono le linee dati che vanno al display in quanto si trovano collocati sugli stessi I/O. L’unico led che dovremo tenere in considerazione è il primo, collocato su RD0

I 3 pulsanti sono collegati senza resistenze di pullup: le abiliteremo internamente con l’apposito bit del registro OPTION.

Riguardo al collegamento della sonda: guardate bene sul datasheet la disposizione dei pin dell’LM35.

All’avvio del programma viene preparato il display, mostrando le scritte che saranno sempre presenti, verranno inoltre mostrati dei trattini al posto del valore della temperatura in quanto all’avvio non sono ancora state effettuate le 20 letture da mediare come detto in precedenza.

Abbiamo un interrupt causato dal timer0 ogni millisecondo, è qui che viene gestita la gran parte delle funzioni del temostato. C’è un primo counter che controlla l’avvio della lettura dal modulo A/D:

407
408
409
410
411
412
count_lm35--; // counter per avvio lettura LM35
if (count_lm35==0) // avvio la lettura se il counter è arrivato a zero
   {
   ADGO=1;
   count_lm35=200; // resetto il contatore
   }

Questo fa in modo di effettuare la lettura dalla sonda ogni 200 millisecondi. Alla fine della lettura e conversione, scatterà l’interrupt di fine conversione A/D, che richiamerà l’apposita routine nell’ISR:

447
448
449
450
451
if (ADIF)
{
calcola=1; // abilito, nel main, l'aggiornamento del valore di temperatura
ADIF=0; // resetto il flag di fine conversione A/D
}

In pratica viene settato un flag “calcola” che nel main avvia la routine che fa la somma delle 20 letture, arrivati a 20 letture viene calcolata la media:

if (calcola)
{
valore=ADRESL + (ADRESH<<8);
media+=valore; // sommo il valore
contavalori++; // incremento il numero di valori letti
 
// abbiamo raggiunto il numero di letture impostate?
if (contavalori==(MEANVALUES-1))
{
displayvalue=(media/MEANVALUES);
media=0; // azzero la media
contavalori=0; // azzero il contatore dei valori sommati

Come vedete in tale routine viene incrementata la variabile “contavalori” che ci permette di tenere sott’occhio quanti valori abbiamo sommato. La variabile “MEANVALUES” è definita in settings e l’ho impostata a 20, rappresenta il numero di letture su cui fare la media. Dal confronto col numero di misure sottraggo 1 perchè contavalori è a base zero. Il valore da mostrare sul display è quindi “displayvalue”.

Nell’ISR vengono inoltre avviati altri contatori:

contatore_inutilizzo : il suo avvio è comandato da un flag “start_contatore” impostato solo se ci troviamo in modalità SET. Il contatore viene resettato di continuo ogni volta che ci troviamo in modalità set e si preme un tasto. Se non premiamo tasti, tale contatore giunge a zero, portando all’uscita della modalità SET e ripristinando i valori di SET a quelli iniziali:

418
419
420
421
422
423
424
425
426
427
if (contatore_inutilizzo==0) // son passati X secondi senza che il menù sia stato usato
{
setmode=MODE_NONE; // esco dalla modalità settaggio
save_settings=0; // senza salvare i settaggi
 
// riporto i valori di set a quelli che erano prima delle eventuali modifiche
valore_hi=pre_valore_hi;
valore_low=pre_valore_low;
// il flag e il contatore saranno ripristinati nel main
}

Abbiamo quindi il contatore “contaflash” che ha la funzione di far lampeggiare le scritte “HIGH” e “LOW” nel caso la temperatura misurata sia al di fuori dal range impostato. Come potete vedere dal codice, il lampeggio di una scritta sull’LCD è gestito scrivendo e cancellando ripetutamente la scritta.

Abbiamo quindi il contatore di intervento intv_counter, che giunto a zero fa scattare l’intervento (che in questo esempio è esplicitato mediante l’accensione del LED, ma voi potete anche collegare due relè, uno per l’intervento per temperatura bassa e uno per temperatura alta). L’intervento è realizzato portando a 1 il flag intv_start.

Il flag intv_start viene continuamente confrontato con il flag intv_state (che memorizza lo stato che ha causato l’allarme: temperatura alta, temperatura bassa o nessun intervento). Quando nel main viene intercettato intv_start==1, si controlla lo stato attuale, se lo stato attuale è uguale a quello settato in precedenza, viene acceso il led a segnalare l’intervento, altrimenti il flag viene resettato. Nel caso in cui lo stato precedente fosse “nessun allarme” e dopo il tempo di intervento tale stato è rimasto, il led viene spento (e qui voi dovrete far staccare entrambi i relè).

Il codice è abbastanza lunghetto, solo in main.c è da 452 righe (però compresi gli abbondanti commenti). Ma le funzioni principali ve le ho illustrate qui, il resto potrete capirlo da soli leggendo il codice.

Se trovate utile questo progetto, quantomeno per “spulciare” parti di codice e idee utili, considerate una donazione in quanto ci abbiamo lavorato su davvero per tanto tempo, cercando di realizzare un progetto davvero completo, utile e molto ricco di funzioni professionali.

Downloads

Nota: i programmi di esempio sono stati sviluppati con una versione precedente dell’Hitec-C Compiler, per cui compilati con la nuova versione, restituiscono errori. Fate riferimento a questo articolo per maggiori informazioni su come adattare i vecchi programmi. Consiglio spassionato se volete davvero imparare a programmare: non utilizzate l’include legacy headers, ma imparate a cambiare i nomi mnemonici.

File di supporto alla undicesima lezione del corso di programmazione picmicro in C (2501 download)
LM35 - Precision Centigrade Temperature Sensor (2754 download)
LM335 - Precision Centigrade Temperature Sensor (Kelvin calibrated) (1917 download)

Se questo articolo ti è piaciuto, condividilo su un social:
Se l'articolo ti è piaciuto o ti è stato utile, potresti dedicare un minuto a leggere questa pagina, dove ho elencato alcune cose che potrebbero farmi contento? Grazie :)