Confesso di essere affascinato e stimolato dai display LCD/TFT.
Mi piace studiarli e provare a farli funzionare, anche se spesso non so poi che farmene.
Ne ho acquistati parecchi, quasi tutti dalla Cina, famosa per non rilasciare documentazione oppure fornire misteriosi documenti
in quasi-Inglo-Cinese, praticamente inutili.
I miei possedimenti vanno dai semplici LCD 1x16 su fino a TFT con diverse dimensioni e risoluzioni, da 1.44" del
Nokia 5510 con 84x68 pixels ai 3.5" a 480x320 pixels dell'ultimo modulo su cui ho lavorato.
I moduli pre-assemblati che si acquistano sul WEB hanno controllori tra i più vari e sempre diversi;
solo per citarni alcuni sui quali ho messo le mani:
|
|
|
|
|
|
|
|
|
|
|
|
Per fortuna in rete di trova praticamente di tutto per questi dispositivi, da librerie specifiche a
quelle (quasi) universali come la UTFT di Henning Karlsen.
Gran parte di queste librerie sono per sistemi 'evoluti' che non conosco e uso (esempio: le MCU STM) oppure per
gli omnipresenti Arduino e derivati, che non mi interessano, anzi...
Per carità, non ho nulla contro Arduino, anzi gli sono riconoscente per quante appassionati a fatto avvicinare al mondo dell'elettronica e della programmazione.
Solo che non è l'approccio giusto per me.
Io sono assolutamente fedele ai PIC di Microchip, prima ai PIC16, ora ai PIC18, con qualche puntatina ai PIC24/32.
La mia scelta, come detto in altre parti, è di farmi 'tutto da me', almeno per quanto possibile, sennò che divertimento c'è; oltretutto
se scrivo la mia libreria la capisco (forse!) e posso metterci le mani in qualsiasi momento.
Le mie librerie rappresentano anche un ottimo strumento di verifica dei miei progressi nella programmazione, una piccola cronistoria
dello studio di nuovi linguaggi e metodologie di sviluppo. Per questo, ho librerie scritte in pseudo Basic, in Assembler per
arrivare ora al C; ci ho messo anni per decidermi a studiarlo, ma ora incomincio a muovermi.
Quello che segue vuole essere un semplice pseudo-tutorial, con qualche modesta indicazione su piccoli trucchi (tricks)
introdotti con l'esperienza per migliorare le prestazioni, in genere non eccezionali dato l'HW a disposizione.
I codici dei Driver che ho deciso di pubblicare
li trovate alla pagina TFT Drivers,
che contiene anche il resoconto di un divertente elenco di errori di programmazione, utile per capire quanto sia facile sbagliare.
Le routine maggiormente utilizzate, e quindi critiche per le prestazioni, sono la funzione 'Dot (o Pixel) e
la funzione Box, richiamata anche da Clear_Screen.
Dot è usata per il disegno di linee (non perpendicolari), cerchi e per il testo. Queste applicazioni sono
di norma ridotte in dimensioni (pochi pixel) per cui la velocità di esecuzione di Dot è importante, ma non critica.
Box è invece usata per aree di grandi dimensioni (fino allo schermo intero) per cui impatta parecchio
sulla velocità globale.
Qui si può intervenire in maniera poco 'pulita' ma molto efficiente, accettando un modesto compromesso.
Di norma, per molti controller, la procedura di riempimento di aree può essere schematizzata in questo pseudo-codice:
Il passo critico è ovviamente il 3; il codice generato dal compilatore per eseguire il ciclo di scrittura richiede
un tempo superiore (spesso di parecchio) al quello di effettiva scrittura del dato.
Il primo intervento è di pre-calcolare il numero di pixel da disegnare ed eseguire un solo ciclo di ripetizioni,
invece dei classici <Loop Y - Loop X>.
Ancor meglio, il loop deve avvenire dal valore massimo (+1) a 0, con decremento della variabile di controllo; questo,
rispetto all'incremento da 0 al massimo consente un guadagno di velocità anche del 20% in funzione del tipo di contatore.
Per il ciclo si può utilizzare un FOR-NEXT oppure un WHILE; quest'ultimo è più veloce.
In sostanza, si ottiene questo codice (pseudo codice senza abilitazioni di scrittura) :
void Box(short X0, short Y0, short X1, short Y1, short colore) { unsigned long maxCnt; maxCnt = (long) ((Y1-Y0+1) * (X1-X0+1) + 1); //numero di pixel da disegnare Set_Window_Area(X0,Y0,X1,Y1); //predispone l'area di stampa Write_Color(colore); //invia sulle porte d'uscita il valore del colore while(maxCnt--) { ToggleWrite() //es. pin_write=0; ritardo; pin_write=1; } }
E qui viene il bello!
Con display medio-grandi (superiori a 280 pixel in una direzione), il numero totale di pixel è superiore a 64K.
Questo comporta l'uso di una variabile maxCnt di tipo Long a 32bit; l'incremento/decremento
di questo tipo è impegnativo per il compilatore.
La soluzione è semplice: ridurre la lunghezza del ciclo sotto il limite dei 64K per poter utilizzare una variabile
Short a 16bit.
In base alle dimensioni del display, questo comporta dividere maxCnt per 2 o 4 (o più) e contemporaneamente
ripetere l'istruzione di scrittura per 2 o 4 (o piò) volte.
La contropartita è che non si potranno disgnare rettangoli con lato minimo inferiore al divisore utilizzato, ma non
credo che questo sia un problema pratico (posso usare DrawPixel in questi casi).
L'esempio che segue prevede la divisione per 4 (x2 in ogni dimensione):
void Box(short X0, short Y0, short X1, short Y1, short colore) { unsigned short maxCnt; maxCnt = ((Y1-Y0+1)>>1) * ((X1-X0+1)>>1) + 1; //numero di pixel da disegnare Set_Window_Area(X0,Y0,X1,Y1); //predispone l'area di stampa Write_Color(colore); //invia sulle porte d'uscita il valore del colore while(maxCnt--) { ToggleWrite() //es. pin_write=0; ritardo; pin_write=1; ToggleWrite() //es. pin_write=0; ritardo; pin_write=1; ToggleWrite() //es. pin_write=0; ritardo; pin_write=1; ToggleWrite() //es. pin_write=0; ritardo; pin_write=1; } }
Con questo banale trucco ottendo la riduzione del tempo impiegato fino al 13% del
tempo originale.
In sintesi, dalle mie sperimentazioni, per la pulizia schermo di un display da 320x240 (76,800 pixel),
con fattore di divisione per 16, sono
passato da 101 msec a 13 msec; un bel guadagno di velocità! Il refresh dello schermo è pressochè inavvertibile.
I codici pubblicati sono scritti in C (XC8 in ambiente MPLABX) in versione Free, quindi senza le ulteriori ottimizzazioni
dell'Omniscent Code Generator (OCG) della versione a pagamento.
La domanda che sorge spontanea è: quanto devo essere fiducioso in XC8 Free che, oltre a produrre un codice 'enorme' rispetto
a quanto sarebbe lecito attendersi, appare così poco efficiente nell'ottimizzare il codice?