Come utilizzare un display lcd con Netduino

I display lcd a matrice permettono di realizzare circuiti che non hanno bisogno della presenza del pc per rappresentare i dati elaborati dal Netduino. Se pensiamo ad esempio al classico sensore di temperatura, possiamo rappresentare il valore letto dalla sonda direttamente sul display piuttosto che inviarlo al pc, rendendo il sistema indipendente.

Essi sono diventati dispositivi evoluti che utilizzano un microcontrollore capace di eseguire diverse operazioni come l’attivazione o meno del cursore, la possibilità di shiftare i caratteri del display a destra o sinistra e addirittura leggere i dati presenti sul display (permettendo l’interazione con l’utente).

Display 16x2 20x2 20x4

Lo schema elettrico per questo esperimento è il seguente:

Collegare Netduino a diplay LCD 16x2I pin da 0 a 7 del Netduino sono collegati ai pin data (dal pin7 al pin 14) del display, il pin 8 del Netduino è collegato al pin 4 (RS) del Display, il pin9  del Netduino è collegato al pin6 (E) del display. I pin 1 e 16(Gnd) del display sono collegati a massa mentre i pin 2 e 15(Vcc) sono collegati alla +5Vdc del Netduino. Il pin 3(Vo) del display è collegato al potenziometro da 10Kohm che viene impiegato per gestire la luminosità dei caratteri del display. Il pin 5(R/W) del display è collegato a massa perchè le operazioni che eseguo sul display, in questo esperimento, sono di scrittura.

La tabella illustra le funzioni dei pin che compongono l’interfaccia di collegamento:

Numero PinDescrizione
1GND collegamento a massa dell’alimentazione del display
2VCC Alimentazione del display tipicamente 5Vdc
3Vo Gestisce la luminosita dei pixel del display
4RS Specifica se vogliamo caricare un dato(Livello Alto) o un comando (Livello basso)
5R/W Specifica se vogliamo scrivere o leggere dal display
6E Necessario per far leggere al display il comando o il dato presente sulle linee DB
7Data bit 0
8Data bit 1
9Data bit 2
10Data bit 3
11Data bit 4
12Data bit 5
13Data bit 6
14Data bit 7
15Vcc Alimentazione per la retroilluminazione del display
16Gnd Massa per la retroilluminazione del display

Come sempre la prima cosa utile è leggere il datasheet del display. In generale il principio di funzionamento è standardizzato, quindi è possibile utilizzare diversi display nello stesso modo.

La parte che ci interessa è quella relativa al diagramma dei segnali che permettono di lavorare con l’lcd, la figura seguente mostra come inviare un dato o un comando al dispositivo:

Diagramma flusso scritura bye lcd

Nel mio caso il pin5(R/W) del display è tenuto sempre a livello logico basso (scrittura). Il segnale RS può cambiare a seconda se vogliamo inviare un comando (Livello Basso) oppure un dato (Livello Alto). Il segnale E serve ad abilitare il display alla lettura del dato/comando presente sui pin data.

Il flusso di lavoro viene semplificato da questo diagramma:

Diagramma di flusso di lavoro

La prima fase consiste nella inizializzazione del display, altrimenti non potremmo visualizzare nessun carattere; questa fase specifica diversi parametri e funzionalità, di seguito l’elenco dei comandi:

Comandi display

Notiamo che per inviare i comandi la linea RS deve sempre essere Bassa, essa diventa alta solo per leggere e scrivere dati.

Dopo aver realizzato il circuito passiamo al codice C# definendo i pin digitali come uscite:

OutputPort Data0 = new OutputPort(Pins.GPIO_PIN_D0, false);
OutputPort Data1 = new OutputPort(Pins.GPIO_PIN_D1, false);
OutputPort Data2 = new OutputPort(Pins.GPIO_PIN_D2, false);
OutputPort Data3 = new OutputPort(Pins.GPIO_PIN_D3, false);
OutputPort Data4 = new OutputPort(Pins.GPIO_PIN_D4, false);
OutputPort Data5 = new OutputPort(Pins.GPIO_PIN_D5, false);
OutputPort Data6 = new OutputPort(Pins.GPIO_PIN_D6, false);
OutputPort Data7 = new OutputPort(Pins.GPIO_PIN_D7, false);

OutputPort RS = new OutputPort(Pins.GPIO_PIN_D8, false);
OutputPort E = new OutputPort(Pins.GPIO_PIN_D9, false);

 

Il primo comando d’inizializzazione attiva il display (Display ON), il cursore(Cursor ON) e il blinking(Blinking ON),

//mette il pin4 del display a livello basso per inviare
//un comando
RS.Write(false);

Data0.Write(true); //Blink ON
Data1.Write(true); //Cursor ON
Data2.Write(true); //Display ON
Data3.Write(true); //Flag Display ON/OFF
Data4.Write(false);
Data5.Write(false);
Data6.Write(false);
Data7.Write(false);

//Esegui il comando sul display
E.Write(true);
Thread.Sleep(1);
E.Write(false);

 

ora impostiamo il numero di righe da utilizzare, il tipo di interfaccia e la risoluzione della matrice:

//mette il pin4 del display a livello basso per inviare
//un comando
RS.Write(false);

//Data0.Write(true); //Non usato
//Data1.Write(true); //Non usato
Data2.Write(true); //5x10 Dots ON
Data3.Write(true); //2 Lines ON
Data4.Write(true); //8bit interface ON
Data5.Write(true); //Flag Set Function
Data6.Write(false);
Data7.Write(false);

//Esegui il comando sul display
E.Write(true);
Thread.Sleep(1);
E.Write(false);

 

gli ultimi comandi che possiamo inviare sono un Return Home per posizionare il cursore nella prima riga e colonna e un CLearDisplay per pulire l’lcd da altri caratteri precedentemente inviati

//mette il pin4 del display a livello basso per inviare
//un comando
RS.Write(false);

//Posiziona il cursore nella prima linea e colonna
//Data0.Write(true); //Non usato
Data1.Write(true); //Flag Retun Home
Data2.Write(false);
Data3.Write(false);
Data4.Write(false);
Data5.Write(false);
Data6.Write(false);
Data7.Write(false);

//Esegui il comando sul display
E.Write(true);
Thread.Sleep(1);
E.Write(false);

//Pulisci lo schermo
Data0.Write(true); //Flag Clear Display
Data1.Write(false);
Data2.Write(false);
Data3.Write(false);
Data4.Write(false);
Data5.Write(false);
Data6.Write(false);
Data7.Write(false);

//Esegui il comando sul display
E.Write(true);
Thread.Sleep(1);
E.Write(false);

 

Questi comandi di inizializzazione sono sufficienti per permettere al display di accettare dati da visualizzare. Proviamo subito a scrivere la lettere A. La procedura è identica all’invio di un comando, l’unica modifica al codice riguarda il l’oggetto RS che ora pone il pin8 del Netduino (pin4 del display) a livello Alto:

//mette il pin4 del display a livello ALTO per inviare
//un dato (carattere ascii) al display
RS.Write(true);

//Scrive sul display nella prima riga e nella prima
//colonna il carattere A (00110001 in binario)
//Ulteriori caratteri possono essere recuperati dalla
//tabella Ascii
Data0.Write(true);
Data1.Write(false);
Data2.Write(false);
Data3.Write(false);
Data4.Write(false);
Data5.Write(false);
Data6.Write(true);
Data7.Write(false);

//Carica il dato sul display
E.Write(true);
Thread.Sleep(1);
E.Write(false);

 

Il display visualizza la lettera A e sposta il cursore a destra. Ogni volta che carichiamo un dato, il cursore viene spostato a destra.

Con il comando Set Function ho abilitato tutte e due le righe disponibili, per scrivere sulla seconda riga dobbiamo impostare l’indirizzo della riga in base alla tabella seguente:

Questa tabella mostra gli indirizzi di tutte le celle del display, per spostare il cursore dove vogliamo iniziare a (seconda linea, prima colonna), dobbiamo inviare al display il comando SET DD RAM ADDRESS.

Ho realizzato una funzione apposita che permette di posizionare il cursore nella cella voluta, passandogli come parametro l’indirizzo della cella.
La funzione è la seguente:

public void SettaRigaColonna(Byte NumRiga)
{
Byte Bit = 0;

//Seleziona comando
RS.Write(false);

Bit = (Byte)((NumRiga >> 0) & 0x01);
if (Bit == 1)
Data0.Write(true);
else
Data0.Write(false);

Bit = (Byte)((NumRiga >> 1) & 0x01);
if (Bit == 1)
Data1.Write(true);
else
Data1.Write(false);

Bit = (Byte)((NumRiga >> 2) & 0x01);
if (Bit == 1)
Data2.Write(true);
else
Data2.Write(false);

Bit = (Byte)((NumRiga >> 3) & 0x01);
if (Bit == 1)
Data3.Write(true);
else
Data3.Write(false);

Bit = (Byte)((NumRiga >> 4) & 0x01);
if (Bit == 1)
Data4.Write(true);
else
Data4.Write(false);

Bit = (Byte)((NumRiga >> 5) & 0x01);
if (Bit == 1)
Data5.Write(true);
else
Data5.Write(false);

Bit = (Byte)((NumRiga >> 6) & 0x01);
if (Bit == 1)
Data6.Write(true);
else
Data6.Write(false);

Data7.Write(true);

//Esegui il comando sul display
E.Write(true);
Thread.Sleep(1);
E.Write(false);
}

 

Per scrivere nella seconda riga dobbiamo passare alla funzione l’indirizzo 0x40

//Posiziona il cursore nella seconda riga prima colonna
SettaRigaColonna(0x40);

 //mette il pin4 del display a livello ALTO per inviare
 //un dato (carattere ascii) al display

RS.Write(true);
//Scrive sul display nella prima riga e nella prima
//colonna il carattere A (00110001 in binario)
 //Ulteriori caratteri possono essere recuperati dalla
 //tabella Ascii
Data0.Write(true);
 Data1.Write(false);
 Data2.Write(false);
 Data3.Write(false);
 Data4.Write(false);
 Data5.Write(false);
 Data6.Write(true);
 Data7.Write(false);

 //Carica il dato sul display
 E.Write(true);
 Thread.Sleep(1);
 E.Write(false);

 

In questo caso il carattere A viene caricato nella seconda riga, prima colonna.

Per approfondire tutte le funzionalità del display è consigliabile continuare a fare delle prove sperimentando con il codice C#. Nel file allegato è presente il progetto C# che ho scritto per questo esperimento che permette di sperimentare con il display.