Termometro con display LCD.
|
Vediamo una semplice implementazione di quanto trattato nelle esercitazioni
20-23 del corso C.
Si tratta di un termometro ambiente con sensore MCP9700 e display LCD EA
DOGM081.
I componenti.
Il display.
|
Abbiamo utilizzato come base un display di EA, il modello
DOGM081L-A, una riga di 8 cifre, che non necessita di retro
illuminazione. Questo consente un sensibile risparmio di corrente: il
consumo del display è circa 250uA.
Il controller è ST7036, che ha possibilità di accesso parallelo e
SPI. Qui utilizziamo l'interfaccia seriale.
Si tratta di un Display On-Glass (DOG), estremamente sottile, con uno
schermo molto ampio (14,5 x 51mm) e caratteri di grandi dimensioni
(12mm) facilmente leggibili anche a distanza. |
Analogo è il modello DOGM081W-A che è visibile ottimamente senza retro
illuminazione, ma può anche supportarla (questi display hanno la retro
illuminazione in un modulo separato).
Sono stati scelti questi display per le grandi dimensioni e la retro
illuminazione non obbligatoria.
L'alimentazione.
Il display può funzionare da 3.3V a 5V. Alimentando a batteria, si è scelta
la tensione minore.
Il problema
che si presenta è quello del display, il cui contrasto è regolato da
programma a seconda
della tensione di alimentazione e richiederebbe quindi una funzione del
programma che
verifichi questa tensione ed adegui i parametri del contrasto in modo da
mantenere la massima visibilità.
Peraltro, la tensione minima di funzionamento dichiarata dal controller ST7036
è 2.7V e, anche se a tensione minore il display è ancora operativo, con due elementi AA o
AAA si scende al di sotto di questi valori.
La soluzione proposta è l'aggiunta di uno step-up che permetta
l'alimentazione a 3.3v con 2 elementi alkalini o NiMh oppure, all'opposto, a 5V con un
elemento LiIon.
|
Per stabilizzare la tensione di alimentazione è stato inserito un
modulo DC/DC step up. Si tratta di un piccolo PCB da 11x11mm che
consente di ottenere 3.3V a partire da una tensione di 0.9V.
La scelta del modulo commerciale è giustificata dal suo bassissimo
costo e dalle dimensioni così ridotte da poter essere inserito in una
qualsiasi realizzazione.
Questo modulo è basato su M2108, un boost a tensione di uscita fissa
che richiede un minimo di componenti ed ha un basso autoconsumo. |
Per alimentare il circuito sono state previste due batterie AAA, ma volendo
utilizzare una cella Li-Ion (3.7V) si potrà far lavorare il circuito a 5V
utilizzando lo step up con questa uscita. Il firmware scritto per questa
applicazione consente di utilizzare sia 3.3V che 5V in modo automatico e il circuito dispone di
adattamenti hardware adeguati.
Il microcontroller.
Avendo tra le mani 16F1615, è stato utilizzato questo. Si potrebbe
benissimo impiegare un tipo a 8pin dotato di MSSP, ma trattandosi di un lavoro
didattico, è stato preferito un chip a 14pin che consente anche il debug e la
programmazione on circuit. Ovviamente si può utilizzare ogni PIC con MSSP,
ADC e FVR.
Il sensore
|
Come sensore della temperatura è stato impiegato MCP9700A di Microchip.
E' utilizzabile tra 2.3 e 5.5V; la gamma di misura va da -40°C a +
125°C con precisione migliore di 2°C.
Molto versatile, rende 10mV per grado, il che rende immediata la
lettura con ADC a 8-10-12 bit. Ha un offset di 500mV a 0°C che
consente la misura di temperature negative senza bisogno di una doppia
alimentazione.
E' disponibile in varie versioni e packages. Qui abbiamo usato per
comodità il tipo in TO92. |
Da notare che questo può essere sostituito dal Texas TMP235
che, però, c'è solo in SOT-23 e SOT-SC70.
In base a quanto sopra è stato possibile realizzare un oggetto compatto da
56x38x15mm (senza le batterie).
Lo schema.
L'alimentazione è prevista a batteria.
Il modulo 2108 è disponibile in versioni con tensione di uscita a 3.3 o 5V; il circuito
permette di utilizzare entrambe.
Per 3.3V si potranno utilizzare due elementi AA o AAA e per 5V una LiIon.
ME2108
ha un basso consumo, ma sfortunatamente un ripple elevato.
E' stato necessario
aggiungere un filtro L/C (L1-C8) altrimenti la lettura analogica sarebbe
risultata impossibile. La combinazione che ha dato migliore risultato con le
basse correnti in gioco è L1=1mH (R 3-11ohm) e C8=560uF (4V per Vdd 3.3V e
6.3V per Vdd=5V)
Il modulo 2108 è disponibile in versioni con tensione di uscita a 3.3 o 5V; il circuito
permette di utilizzare entrambe.
Sono consigliati, per la stabilità e l'eliminazione del rumore, due
condensatori C5 e C6.
R1/C4 (100ohm/100nF) è opzionale: va inserito se la sorgente di alimentazione è
rumorosa. Così pure R2 (100-200ohm).
Il PIC è collegato ad una presa ICSP per programmazione e debug on board.
Sono stati usati solo i pin da 1 a 5, dato che il 6 non è impiegato.
Il sensore MCP9700 richiede due condensatori: C7 sull'alimentazione e C3
sull'uscita. Se ci fossero ancora problemi di instabilità sulla lettura
analogica, può essere utile inserire in serie una resistenza da 200ohm
all'alimentazione del sensore.
Il jumper SJ1 indica al firmware se l'alimentazione è 3.3 o 5V, dato che
sono richieste due inizializzazioni con parametri differenti per il display.
Il motivo di questo è dovuto al fatto che il contrasto del display è
regolabile da programma e varia con la tensione di alimentazione.
RA2 rileva lo stato di SJ1. Questo consente di effettuare test con entrambe le tensioni.
Sempre per supportare l'alimentazione diversa, i jumper JP2 e JP3 consentono di
inserire due condensatori se il sistema è alimentato a 3.3V o escluderli se
alimentato a 5V (vedere il foglio dati del display per informazioni più
dettagliate).
Per la connessione SPI è utilizzato il modulo MSSP del PIC. I pin CLK e SI
(non utilizzato) sono quelli di default su RC0 e RC1. L'uscita SDO è rilocata
su RC2.
Il sensore di temperatura è letto da AN3-RA4.
In aggiunta, RC3 (AN7) misura la tensione della batteria ed aggiunge la
lettera b alla fine del display se la tensione di alimentazione è scesa sotto
un livello prefissato.
Il circuito.
Tutti i componenti sono montati su un circuito stampato a faccia
singola.
Il display è istallato su zoccolo alto 8.5mm permettendo a tutti gli altri
componenti di trovare posto al di sotto.
Il circuito stampato, realizzato in meno di un'ora, è a faccia singola,
per cui sono richiesti alcuni ponticelli.
Il
modulo step up è su uno zoccolo per poter fare prove con diverse tensioni.
Per il microcontroller abbiamo utilizzato la versione SMD.
I condensatori sono tutti SMD 0805, escluso C8 che
un elettrolitico a basso esr.
Il circuito stampato è stato stagnato a freddo (Bungard) per facilitare la
saldatura dei componenti SMD.
Ha creato difficoltà la scelta dello zoccolo per il display, in quanto la
maggioranza di questo genere di strip è pensata per l'uso con pin maschi da
0.64mm, mentre il display ha pin molto sottili. Fortunatamente le file
di contatti Harwin M20-7822046 sono risultate adeguate, avendo anche una
altezza di 8.5mm in modo da poter ospitare i componenti sul PCB.
Da notare che MCP9700 è estremamente sensibile alle variazioni della
temperatura, che registra immediatamente. Basta avvicinare una mano al sensore
per aumentare la temperatura circostante e l'indicazione del termometro.
Se si intende misurare la temperatura ambiente occorre posizionare il sensore
in modo che misuri la reale temperatura e non variazioni indotte da contatto,
movimenti improvvisi dell'aria, raggi di sole diretti, elementi caldi, ecc.
Il programma.
Il sorgente in C per XC8 è facilmente leggibile e non presenta problemi.
Aggiungiamo solo qualche commento su alcuni punti.
Il clock del processore è 1MHz per ridurre il consumo.
Selezionando
l'opzione del clock Fosc/4 del bus SPI si avrà un clock a 125kHz. Da osservare
che il display può comunicare in SPI con clock dell'ordine dei MHz.
#define _XTAL_FREQ 1000000 // 1MHz
// SPICLK = Fosc/4 -> 125kHz
Sono definiti tre pin:
#define SPI_RS LATCbits.LATC4
#define SPI_CS LATCbits.LATC5
#define VOLTSEL PORTAbits.RA2
Il segnale RS consente al display di definire se il byte in arrivo è un
comando (RS=0) o un dato (RS=1).
Il segnale CS abilita il display.
Al pin VOLTSEL è collegato il jumper SJ1 : se è aperto, il pull-up integrato mantiene il livello a 1 e
il firmware procede come se l'alimentazione fosse 5V.
Se il jumper è chiuso, si intende che la Vdd è 3.3V. In questo caso viene
escluso il pull-up che consumerebbe solo corrente.
if (VOLTSEL) {LCD_Init5();}
else {LCD_Init3(); WPUA2=0;} // Vdd 3.3V, esclude wpu
Dato che il display ha la
regolazione del contrasto e l'adattamento a tensioni di alimentazioni diverse
controllato da programma, le inizializzazioni a 5V e a 3.3V si differenziano
per il diverso valore da applicare al booster e al contrasto.
Va tenuto presente che il controller ST7036 ha i comandi base compatibili con
HT44780, ma a questi aggiunge numerose altre funzioni, tra sono possibili diversi set di
caratteri, diverse modalità di visualizzazione e il controllo del contrasto
via software, escludendo il classico trimmer esterno.
Durante la sequenza di inizializzazione non sono necessari ritardi tra comando e comando perchè la
funzione LCD_Cmd() inserisce già il
giusto ritardo a seconda del valore trasmesso; l'unico
ritardo necessario è quello di stabilizzazione tra l'arrivo dell'alimentazione e l'inizio delle
operazioni sul display (circa 100ms).
Viene definito un carattere speciale in CGRAM
const char Custom_Char5x8[8] ={0x2,0x7,0x5,0x5,0x7,0x0,0x0,0x0};
Che sarà usato come indicatore di batteria scarica.
Vengono effettuate 16 letture una volta ogni secondo e
presentato il risultato sul display:
// ---------- Lettura e display temperatura
---------------
La lettura della temperatura avviene una volta al secondo.
La lettura della tensione di alimentazione richiede il
cambio del canale ADC e della tensione di riferimento.
ADCON0=0b00011101;
// AN7-RC3, ADC on
FVRCON=0b10000011;
// FVR 4.096
while (!FVRCONbits.FVRRDY){}; // attesa FVR ready
Viene cambiato il riferimento della conversione AD da
1.024 a 4.096 (4mV per bit) per
adattare la lettura ad un ampio spettro di batterie, compresi LiIon.
Il valore risultante è comparato con un limite (qui
circa 2V). Se la tensione è minore viene aggiunta una b all'ultima posizione
del display.
// se Vbat<2V aggiunge simbolo CGRAM come ultimo carattere
if(ADRES<0x01FF){(LCD_Dat(0));} // primo carattere in CGRAM
else {(LCD_ROMstr(" "));}
Si potrà variare facilmente il limite di batteria
scarica a seconda degli elementi usati e anche l'icona da visualizzare.
Il canale del'ADC viene riportato alla lettura del sensore
di temperatura:
// riporta il canale analogico su MCP9700
ADCON0 = 0b00001100; // ANA3(RA4), ADC OFF
Durante la pausa vengono spenti i moduli FVR, ADC e MSSP per ridurre il
consumo di corrente e prolungare la vita della batteria.
Il modulo FVR viene riporatato a 1.024V
Alla fine della pausa si riavviano i moduli e il cursore viene riportato all'inizio della riga.
LCD_Cmd(0x02); // home
Le altre funzioni sono le stesse descritte nelle esercitazioni
del corso C.
Altre espansioni.
Il circuito assorbe circa 1.1mA, il che permette alle batterie di operare
per almeno un mese.
Ridurre ulteriormente la corrente sarà possibile semplicemente:
- mettendo il
processore in sleep durante la pausa tra una misura e la successiva
- oppure
sostituendo l'interruttore con un pulsante sul reset e mantenendo acceso il sistema per
un tempo fisso, per poi spegnerlo in sleep.
Il testo sorgente.
/**********************************************************
Thermo_EA - Lettura della temperatura con MCP9700.
Display LCD
EA DOGM081 (controller ST7036)
con
interfaccia SPI - MSSP
**********************************************************
MPLABX XC8
Target : PIC 8 bit enhanced (16F1615)
*********************************************************/
/* Assegnazioni I/O
PORTA
| RA5 | RA4 | RA3 | RA2 | RA1 | RA0 |
|-----|-----|-----|-----|-----|-----|
| - |9700 | MCLR|Volt | CLK | DAT | ICSP
AN3
PORTC
| RC5 | RC4 | RC3 | RC2 | RC1 | RC0 |
|-----|-----|-----|-----|-----|-----|
| CS | RS | VBAT| SD0 | SDI | SCK | LCD/SPI
AN7
RC4 SPI_RS LCD mode: 0 comando - 1 dati
RC5 SPI_CS chip select periferica
RC2 SPI_SDO out dati
RC1 SPI_SDI in dati
RC0 SPI_SCK clock SPI
**********************************************************/
#include <xc.h>
#define _XTAL_FREQ 1000000 // 1MHz
// SPI clock=FOSC/4 ->125kHz
#define SPI_RS LATCbits.LATC4
#define SPI_CS LATCbits.LATC5
#define VOLTSEL PORTAbits.RA2
unsigned char dummy;
char i;
// carattere per CGRAM
const char Custom_Char5x8[8] ={0x2,0x7,0x5,0x5,0x7,0x0,0x0,0x0};
//-------------- config
-----------------------------------
#pragma config FOSC=INTOSC, PWRTE=OFF, MCLRE=ON, CP=OFF
#pragma config BOREN=OFF, CLKOUTEN=OFF, IESO=ON, FCMEN=ON
#pragma config WRT=OFF, PPS1WAY=ON, ZCD=OFF, PLLEN=OFF
#pragma config STVREN=ON, BORV=LO, LPBOR=OFF, LVP=OFF
#pragma config WDTCPS=WDTCPS1F, WDTE=OFF, WDTCWS=WDTCWSSW
#pragma config WDTCCS=SWC
// Prototipi
-----------------------------------------------
void LCD_Init5(void); // inizializza LCD 5V
void LCD_Init3(void); // inizializza LCD 3V
void LCD_Cmd(char); // invia comando
void LCD_Dat(char); // invia dato
void LCD_Str(char *); // invia stringa
void LCD_ROMstr (const char *str);
void Out9700(int);
void main (void) {
int mcp9700 = 0; // valore letto da 9700
// --------- Clock interno
OSCCON = 0b01011010; // 1 MHz
while (!OSCSTATbits.HFIOFR) // attesa stabilizzazione
{}
// --------- preset I/O
ANSELC=0b001000; // AN7-RC3
analogica
WPUC = 0;
// PORTC no WPU
ANSELA=0b010000; // ANA3-RA4 analog
WPUA = 0b000100; // wpu su RA2
OPTION_REGbits.nWPUEN = 0; // wpu abilitato
SPI_CS = 1;
// CS=1, slave disabilitato
TRISC =0b001010; // RC1 in, RC3
analog.
TRISA =0b111111; // PORTA in
// --------- Setup ADC e FVR
ADCON1 = 0b11110011; // giust. a destra, FRC
ADCON2 = 0;
// no trigger
ADCON0 = 0b00001101; // ANA3(RA4), ADC ON
FVRCON = 0b10000001; // riferimento interno 1.024V
// --------- Setup MSSP
SSPCON1 = 0b00010000; // CKP=1, EN=0, clock Fosc/4 125kHz
SSP1STAT = 0b00000000; // CKE=0, SPEN=0
SSP1CON1bits.SSPEN = 1; // MSPP on
// ---------- Setup PPS
RC0PPS = 0x10; // RC0
CLK out
RC2PPS = 0x11; // RC2
SDO out
// ---------- main loop
if (VOLTSEL) {(LCD_Init5());}
else {LCD_Init3();( WPUA2=0);} //eslude wpu
// ---------- Lettura e display temperatura
---------------
while(1){ // loop continuo
mcp9700 = 0;
for(i=0;i<16;i++){ // 32 letture
__delay_ms(5); // attesa prima conversione
GO = 1; // avvia conversione
mcp9700 += (ADRES-500); // sottrai offset
}
mcp9700 /= 16; // media delle
letture
Out9700(mcp9700); // scrive temperatura
// --------- Lettura tensione batteria
ADCON0=0b00011101; // AN7-RC3, ADC on
FVRCON=0b10000011; // FVR 4.096
while (!FVRCONbits.FVRRDY){}; // attesa FVR ready
__delay_ms(5); // attesa prima
conversione
GO = 1; // avvia conversione
while(GO);
// wait fine conversione
// se Vbat<2V aggiunge b come ultimo carattere
if(ADRES<0x01FF){(LCD_Dat(0));} // primo carattere in CGRAM
else {(LCD_ROMstr(" "));}
// riporta il canale analogico su MCP9700
ADCON0 = 0b00001100; // ANA3(RA4), ADC OFF
// spegne MSSP
SSP1CON1bits.SSPEN = 0;
// spegne FVR
FVRCONbits.FVREN = 0;
__delay_ms(1000); // aggiornamento
ogni secondo
// accende ADC
ADCON0bits.ADON = 1;
// accende MSSP
SSP1CON1bits.SSPEN = 1;
// accendee FVR
FVRCON = 0b10000001; // riferimento interno 1.024V
while (!FVRCONbits.FVRRDY){}; // attesa FVR ready
__delay_ms(50); //
stabilizzazione
LCD_Cmd(0x02); //
cursor home
}
}
//===================== FUNZIONI
==========================
//--------Inizializzazione LCD EA DOGM081 - 5V -----------
void LCD_Init5(){
__delay_ms(100);
LCD_Cmd(0x31); // 8 bit, 1 line, instr. table
LCD_Cmd(0x1C); // BS:1/4,1 line
LCD_Cmd(0x51); // booster on,contrast C5, set C4
LCD_Cmd(0x6A); // set voltage follower and gain
LCD_Cmd(0x74); // set contrast C3,C2,C1
LCD_Cmd(0x30); // switch to instr. table0
LCD_Cmd(0x0C); // display on, no cursor
LCD_Cmd(0x01); // clear & home display
LCD_Cmd(0x06); // cursor incr.
}
//--------Inizializzazione LCD EA DOGM081 - 3V -----------
void LCD_Init3(){
__delay_ms(100);
LCD_Cmd(0x31); // 8 bit, 1 line, instr. table
LCD_Cmd(0x14); // BS:1/4,1 line
LCD_Cmd(0x55); // booster on,contrast C5, set C4
LCD_Cmd(0x6D); // set voltage follower and gain
LCD_Cmd(0x70); // set contrast C3,C2,C1
LCD_Cmd(0x30); // switch to instr. table0
LCD_Cmd(0x0C); // display on, no cursor
LCD_Cmd(0x01); // clear & home display
LCD_Cmd(0x06); // cursor incr.
}
// --------- Visualizza la temperatura -------------------
// Stringa per visualizzare °C
char str3[3] = {0xDF, 'C', '\0'};
void Out9700(int mcp9700){ // Visualizza temperatura
char digit;
if(mcp9700 < 0){
// se negativo
LCD_ROMstr("-");
// visualizza il segno -
mcp9700 = abs(mcp9700); // valore assoluto
}
else LCD_ROMstr(" "); //
se positivo, spazio
digit = mcp9700 / 100;
// cifra decine
if(digit==0)LCD_ROMstr(" "); // sopprime se 0
else LCD_Dat(digit + '0'); // carattere e visualizza
digit = (mcp9700 - digit * 100) / 10; // cifra unità
LCD_Dat(digit + '0'); // carattere e
visualizza
LCD_ROMstr(".");
// punto decimale
digit = mcp9700 % 10; // parte
decimale
LCD_Dat(digit+'0'); //
carattere e visualizza
LCD_ROMstr(str3);
// °Celsius
}
//-------- Invia un comando
void LCD_Cmd (char Cmd) {
SPI_CS = 0;
// seleziona periferica
SPI_RS = 0;
// modo comando
SSP1BUF = Cmd; // invia comando
while (! SSPSTATbits.BF); // attesa fine trasmissione
dummy = SSP1BUF; //
letto per resettare BF
if(Cmd & 0xFC)
// istruzione normale
__delay_us(30); //
attesa 30usec
else
// comandi Clear e Home
__delay_us(1500); // attesa
1,5ms
SPI_CS = 1; // disabilita periferica
}
//-------- Invia un byte dati
void LCD_Dat (char dato) {
SPI_CS = 0;
// seleziona periferica
SPI_RS = 1;
// modo dati
SSP1BUF = dato; // invia
dato
while (!SSP1STATbits.BF); // Attesa fine trasmissione
dummy = SSP1BUF; // letto per
resettare BF
__delay_us(30); //
30usec
SPI_CS = 1;
// disabilita periferica
}
//-------- Uscita stringa
void LCD_Str(char * str){
while(* str) //
fino a fine stringa (00)
LCD_Dat(* str++); //out carattere, puntatore +1
}
//-------- Uscita stringa da ROM
void LCD_ROMstr(const char *str){
while(* str) // fino a
fine stringa (00)
LCD_Dat(* str++); //out carattere, puntatore +1
}
|