Pilotare lo schermo di un Nokia 5110 con Arduino

 

Per aggiungere un po’ di interattività ed autonomia ad un progetto è necessario spesso avere un’interfaccia grafica, che sia un semplice menù, una GUI un po’ più complessa o delle schermate che ci mostrano dei grafici. Per raggiungere questi scopi possiamo utilizzare alcuni schermi di vecchi modelli di cellulari, che hanno un driver per pilotarli all’interno semplice da accedere e utilizzare con i microcontrollori.

Lo schermo di cui parliamo in questo articolo e in altri a seguire è lo schermo del Nokia 5110 delle dimensioni di 84×48 pixel, pilotati dal driver Philips PCD8544. Questo schermo, oltre ad essere economico(si può trovare per un paio di euro lo schermo già sul circuito da EBay, come quello in cima a quest’articolo) è molto semplice da gestire, soprattutto grazie alla libreria che verrà introdotta in questo articolo.

In questa prima parte parleremo di:

  • Hardware: driver e schermo
  • Come utilizzare lo schermo senza libreria
  • La libreria LCD5110: come funziona e che funzioni ci mette a disposizione

Hardware: driver e schermo

Retro dello schermo

Come si può vedere nel retro della scheda troviamo 8 connettori:

1- VCC: alimentazione a 3.3V
2- GND: massa
3- SCE o CE: abilita il driver
4- RST: Pin di reset.
5- D/C o DC: con questo pin si dice al driver come interpretare i dati in arrivo
6- DIN/MOSI: pin dove si passano le informazioni in maniera seriale. 
7- SCLK o CLK: clock della comunicazione seriale.
8- LED/LIGHT: pin per abilitare la retroilluminazione. Occorre collegare il pin a massa aggiungendo una resistenza da circa 200Ohm

 La disposizione e/o il nome del pin possono variare dai diversi modelli. Questa è la disposizione, ad esempio del modello acquistabile dalla Sparkfun.

Esistono due tipi di informazioni che possono essere mandati al driver:

  • I comandi veri e propri.
  • Le informazioni sui pixel da modificare.
Quello che fa capire al driver cosa gli stiamo comunicando è il valore (HIGH o LOW) che diamo al pin D/C: infatti se impostiamo il pin a LOW  interpreterà le informazioni come dei comandi, settando questo pin ad HIGH le interpreterà come pixel da colorare.
Ma come mandiamo le nostre informazioni? Qualcuno si sarà accorto che alcuni nomi di alcuni pin non sono nuovi: infatti, come avevamo parlato in un articolo precedente nel quale si parlava di come utilizzare un 74HC595 per pilotare in modo efficiente un sette segmenti utilizzando la funzione ShiftOut dell’Arduino si faceva riferimento a dei dei pin chiamati proprio SCLK,CE e DATA(qui DIN/MOSI)! 
Questo infatti è proprio il meccanismo che viene utilizzato: mandare del byte di informazione attraverso la funzione ShitOut abbassando e alzando il pin CE prima e dopo di aver chiamato questa funzione.
In effetti, per ottimizzare questo utilizzo invece di ragionare su una griglia di 84X48 pixel lo schermetto lavora su un area di 84 pixel e 6 righe da 8 pixel.
Il datasheet del driver si può trovare qui. All’interno si può trovare la lista completa dei comandi supportati.
 
 Come utilizzare lo schermo senza libreria

Con le poche informazioni che abbiamo siamo già in grado di utilizzare il nostro schermo. Questa parte non verrà approfonditamente trattata e si basa sostanzialmente sul tutorial presente nel sito dell’Arduino alla pagina sul PCD8544, e ne verranno analizzate solo alcune parti, che sono semplici applicazioni di ciò che abbiamo appena scritto.

Come si può vedere i caratteri che possiamo scrivere sullo schermo sono rappresentati da una grande matrice di matrice, nella quale i caratteri sono rappresentati da 5 byte(quindi occupano sullo schermo un’area di 5 pixel in larghezza e 8 in altezza).

La funzione LCD_Write serve per inviare un singolo byte allo schermo LCD e prende come primo parametro un valore HIGH o LOW che verrà applicato al pin DC poco prima di fare uno ShiftOut. Nella parte di inizializzazione del programma si vede che questa funzione viene utilizzata per passare proprio dei comandi utili per impostare ad esempio il contrasto.

Infine un’altra importante funzione è quella che ci consente di spostare il cursore nello schermo chiamata in uno degli esempi gotoXY che mostra proprio come nell’LCD sull’asse X si può utilizzare un qualunque valore tra 0 e 84, mentre per l’asse Y si possono utilizzare solo valori compresi tra 0 e 5 che rappresentano appunto le 6 righe da 8 byte.

La libreria LCD5110: come funziona e che funzioni ci mette a disposizione

La metodologia di utilizzo spiegata poco sopra può essere utile per capire come lo schermo funziona e generare un codice il più compatto possibile, rendendo però il tutto ingestibile appena tentiamo di disegnare qualcosa. 

Un’altra metodologia è quella che utilizza un frame buffer: una matrice che andremo a pulire, aggiornare con la nostra schermata e poi andremo a scrivere sullo schermo. Così si evitano ripetuti accessi e spostamenti del cursore all’interno dello schermo, e si riescono a implementare in maniera più efficiente alcune funzioni per disegnare, poiché l’accesso allo stesso avviene solo dopo che avremo elaborato questa matrice. Questa matrice, però, risiederà sulla memoria RAM

La libreria di cui parleremo è la libreria LCD5110_Graph che si può scaricare dal sito del creatore. Ne esiste una versione “light”(la LCD5110_Basic) che però non mette a disposizione le funzionalità grafiche.

Queste sono alcune le funzioni che ci vengono messe a disposizione:

  • LCD5110(SCK, MOSI, DC, RST, CS): costruttore della classe al quale passiamo il numero dei pin dell’Arduino ai quali attacchiamo i relativi pin dello schermo.
  • InitLCD(): inizializza l’LCD, passando allo schermo alcuni comandi per impostare contrasto o altro
  • update(): scrive il framebuffer sullo schermo. Questa è l’unica funzione(insieme ad invert) che va realmente a scrivere sullo schermo 
  • clrScr(): ripulisce il framebuffer(non scrive niente sullo schermo).
  • invert(mode): abilita(true) o disabilita la modalità invertita.
  • setPixel(x, y): imposta a nero il pixel in posizione x/y.
  • clrPixel(x, y): imposta a bianco il pixel in posizione x/y.
  • invPixel(x, y): inverte il valore del pixel in posizione x/y.
  • invertText(mode): imposta se il testo scritto da print, printNumI, printNumF sono con i colori invertiti o no.
  • print(st, x, y): scrive una stringa(st) a partire dal pixel x/y.
  • printNumI(num, x, y): inserisce il numero intero(num) a partire dalla posizione x/y.
  • printNumF(num, dec, x, y): inserisce il numero float(num) a partire dalla posizione x/y
  • setFont(fontname): imposta il font del testo. Questo font deve essere rappresentato da un’array(fontname).
  • drawBitmap(x, y, sx, sy, data [, flash]): disegna la bitmap sotto forma di array(data) richiamandola dalla flash(funzionalità di default) a partire dalla posizione x/y, per una lunghezza sx e una larghezza sy. Per capire meglio come si possa salvare una variabile(che poi sarà accessibile solo in lettura) nella flash si rimanda al paragrafo sottostante.
  • drawLine(x1, y1, x2, y2): disegna la retta che parte da x1/y1 e termina in x2/y2.
  • drawRect(x1, y1, x2, y2): disegna un rettangolo con angolo superiore sinistro x1/y1 e angolo inferiore destro x2/y2.
  • drawRoundRect(x1, y1, x2, y2): disegna un rettangolo con gli angoli smussati. Questo deve avere una lunghezza/larghezza minima di 5 pixel, altrimenti non verrà disegnato nulla.
  • drawCircle(x, y, radius): disegna un cerchio di centro x/y e di raggio radius.

Utilizzo di PROGMEM

Una cosa da non trascurare quando andiamo a costruire delle GUI con delle immagini o animazioni è l’utilizzo della memoria RAM del nostro micro: essa nell’Arduino UNO è parecchio limitata (2kb) e quindi dobbiamo preservarne il più possibile.

Per strutture dati che non vengono mai modificate come le immagini o animazioni di cui parlavamo(ma anche stringhe dal valore prefissato) si può utilizzare la direttiva PROGMEM che ci viene messa a disposizione che ci consente di imporre di salvare queste strutture dati nella flash e non nella RAM.

La sintassi da utilizzare è la seguente:

dataType variableName[] PROGMEM = {dataInt0, dataInt1, dataInt3…};

Quindi per le immagini utilizzeremo ad esempio:

byte hearth[] PROGMEM = { 0x06, 0x0F, 0x1E, 0x0F, 0x06};

Con la quale riusciamo a disegnare un cuore. Come si può vedere l’immagine viene organizzata in “file di byte”, comode da utilizzare sia con il framebuffer sia con lo ShiftOut. Infatti la libreria LCD5110_Graph nella funzione drawBitmap vuole proprio un’array di byte che, per default, deve essere presente nella flash. Per poter richiamare queste strutture occorrono delle funzioni apposite differenti per ogni tipo di dato utilizzato. Ad esempio per estrarre un byte dalla struttura heart definita poco sopra questa funzione si chiama :

byte value = pgm_read_byte_near(hearth + 2);

Nella quale il primo parametro è un nome di una variabile creata con PROGMEM mentre il secondo è un offset( in questo caso viene estratto il secondo valore). Per una spiegazione più estesa si può far riferimento alla pagina presente sul playground.

Nelle prossime articoli verranno mostrati alcuni utilizzi di questo schermo, da quelli più utili a quelli più “ludici”.

Alla prossima!

Luca Panebianco

Ciao! Mi chiamo Luca e frequento la magistrale in Ingegneria Informatica e dell’Automazione all’Università Politecnica delle Marche. Qui ho apprezzato pian piano la programmazione su microcontrollori oltre che la canonica sul PC. Sono interessato ai vari campi della programmazione su diversi dispositivi.


By Luca Panebianco | novembre 23rd, 2012 | LEAVE A COMMENT