Progetti - PIC

 

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
 }




 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 20/06/24.