Anem a afegir una pantalla LCD al nostre microcontrolador. Les pantalles LCD porten normalment un circuit integrat que les controla (un altre microcontrolador) que permet que nosaltres li enviem la configuració i els caràcters a mostrar. Si no hi hagués aquest circuit integrat, nosaltres hauríem de controlar tots els punts (píxels) de la pantalla i ens faltarien ports.
Els controladors de pantalla es poden comunicar amb el microcontrolador en sèrie o en paral·lel.
La comunicació paral·lel requereix un conjunt de quatre o vuit bits de dades i entre dos i quatre bits de control; per tant haurem de reservar entre sis i dotze potes dels ports del microcontrolador. Té l'avantatge que els bits s'envien tots d'un cop i, per tant, la programació és més senzilla.
La comunicació sèrie es fa enviant els bits un darrere l'altre i, per tant, cal gestionar el canal de comunicació. Amb comunicació sèrie la programació és més complexa però es necessiten molt poques potes (normalment dues o tres) per a la comunicació.
Aquest apartat es basa en les pantalles sèrie de 16 per 2 de l'empresa Sparkfun. En el cas de les pantalles, més important que el full de característiques de la pantalla és el full de característiques del controlador. Podeu trobar la taula de caràcters que porta implementats a la pàgina 17 d'aquest full de característiques.
Per connectar la pantalla amb el microcontrolador, cal fer les següents connexions:
| Pantalla | Microcontrolador | |
| Nom | Nom | Pota |
| VDD (+5 V) | Vdd (+5 V) | 1 |
| GND | Vss (+5 V) | 20 |
| RX | TX (RB7) | 10 |
Per enviar dades a la pantalla ens caldrà una transmissió sèrie a 9600 bauds.
La pantalla ja està preconfigurada de fàbrica. Si ens estan bé els valors per defecte ja podem començar a enviar-li caràcters que ens apareixeran a la primera columna de la primera filera i posicions successives.
Atenció: en alguns casos hem detectat problemes causats perquè s'envien coses (instruccions de configuració o text) a la pantalla abans que aquesta s'acabi d'inicialitzar. Això és més freqüent si s'alimenta la placa directament ja que quan l'alimentem amb el programador, aquest reinicialitza el microcontrolador sense deixar d'alimentar la pantalla. Si es detecta un mal funcionament de la pantalla, és recomanable posar un retard, per exemple de dos segons, a l'inici del programa, abans d'enviar res a la pantalla.
El controlador de la pantalla té una memòria per als caràcters que es visualitzen. Cada posició de la pantalla correspon a un byte a la memòria però no tots els bytes de la memòria es visualitzen.
La primera línia de la pantalla comença a l'adreça 0. En pantalles de dues línies, la segona línia comença a la 64 (decimal). Si la pantalla té més línies, la tercera començarà a 20 i la quarta a 84. En una pantalla de 16 caràcters per línia es visualitzaran a la primera línia les posicions 0 a 15.
La taula de caràcters és, en principi, la dels codis ASCII. Per tant per als caràcters normals podem treballar directament en ASCII. Algunes de les adreces que en els codis ASCII no hi són (més de 7Fh) o algunes de les que corresponen a caràcters de control contenen símbols que el fabricant de la pantalla ha decidit posar-hi.
L'enviament de caràcters és molt senzill, un cop hem configurat la comunicació sèrie. La funció EnviaL ens permet enviar un sol caràcter. En el següent tros de programa veiem la configuració sèrie i com s'envia un caràcter. A l' exemple PS hi tenim un programa complet.
... #include <xc.h> // Carrega el fitxer de funcions necessari per al compilador XC8 #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz
// Definició de les funcions que farem servir void EnviaL(char Caracter); // Envia un caràcter
void main (void) {
ANSEL = 0b00000101; // Configura AN0 i AN2 com entrada analògica
ANSELH = 0; // Desactiva les altres entrades analògiques
TRISC = 0; // Tot el port C és de sortida
TRISB = 0; // Tot el port B és de sortida
__delay_ms(2000); // Esperem que arrenqui la pantalla
TXSTAbits.BRGH = 1; // Configuració de velocitat
BAUDCTLbits.BRG16 = 0; // Paràmetre de velocitat de 8 bits
SPBRG = 25; // Velocitat de 9600 baud
TXSTAbits.SYNC = 0; // Comunicació asíncrona
TXSTAbits.TX9 = 0; // Comunicació de 8 bits
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
EnviaL('P'); // Lletra
...
}
void EnviaL(char Caracter) {
TXREG = Caracter; // Agafa el caràcter i l'envia
__delay_ms(1); // Donem temps
while (PIR1bits.TXIF == 0) // Esperem que s'acabi d'enviar
; // No fem res
}
...
La funció EnviaT ens permet enviar un text constant, posat entre cometes. A l' exemple PS hi tenim un programa complet.
... #include <xc.h> // Carrega el fitxer de funcions necessari per al compilador XC8 #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz
// Definició de les funcions que farem servir void EnviaT(const char* text); // Envia un text
void main (void) {
ANSEL = 0b00000101; // Configura AN0 i AN2 com entrada analògica
ANSELH = 0; // Desactiva les altres entrades analògiques
TRISC = 0; // Tot el port C és de sortida
TRISB = 0; // Tot el port B és de sortida
__delay_ms(2000); // Esperem que arrenqui la pantalla
TXSTAbits.BRGH = 1; // Configuració de velocitat
BAUDCTLbits.BRG16 = 0; // Paràmetre de velocitat de 8 bits
SPBRG = 25; // Velocitat de 9600 baud
TXSTAbits.SYNC = 0; // Comunicació asíncrona
TXSTAbits.TX9 = 0; // Comunicació de 8 bits
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
EnviaT("kit"); // Envia un text
...
}
void EnviaT(const char* text) {
for (unsigned char i = 0; text[i] != '\0'; i++) {
EnviaL(text[i]); // Envia caràcter a caràcter
}
}
...
En aquest cas el paràmetre de la funció, de fet, no és el text, sinó un punter al seu primer caràcter. Els textos guardats d'aquesta manera tenen un byte més que el número de caràcters (la paraula "kit" ocupa quatre bytes). Aquest darrer byte és sempre un zero (caràcter '\0') i això és el que fem servir per detectar el final del text.
El mateix es podria fer guardant el text en una variable i enviant aquesta variable. Per exemple:
... #include <xc.h> // Carrega el fitxer de funcions necessari per al compilador XC8 #define _XTAL_FREQ 4000000 // La freqüència del rellotge és 4 MHz
char txt[] ="kit";
// Definició de les funcions que farem servir void EnviaT(const char* text); // Envia un text
void main (void) {
ANSEL = 0b00000101; // Configura AN0 i AN2 com entrada analògica
ANSELH = 0; // Desactiva les altres entrades analògiques
TRISC = 0; // Tot el port C és de sortida
TRISB = 0; // Tot el port B és de sortida
__delay_ms(2000); // Esperem que arrenqui la pantalla
TXSTAbits.BRGH = 1; // Configuració de velocitat
BAUDCTLbits.BRG16 = 0; // Paràmetre de velocitat de 8 bits
SPBRG = 25; // Velocitat de 9600 baud
TXSTAbits.SYNC = 0; // Comunicació asíncrona
TXSTAbits.TX9 = 0; // Comunicació de 8 bits
RCSTAbits.SPEN = 1; // Activa comunicació sèrie
TXSTAbits.TXEN = 1; // Activa comunicació
EnviaT(txt); // Envia un text
...
}
void EnviaT(const char* text) {
for (unsigned char i = 0; text[i] != '\0'; i++) {
EnviaL(text[i]); // Envia caràcter a caràcter
}
}
...
Cal tenir present, però, que el contingut de la variable txt no es pot modificar al llarg del programa.
Hi ha uns quants caràcters que permeten canviar les característiques de la pantalla o de la visualització. Cal enviar un caràcter de control FE (254 decimal) seguit del codi de control que ens fa falta. Els codis estan a la taula següent:
| Primer caràcter de control | Segon caràcter de control | Funció | ||
| decimal | hexadecimal | decimal | hexadecimal | |
| 254 | FE | 1 | 01 | Esborra la pantalla i posa el cursor a l'inici |
| 254 | FE | 20 | 14 | Mou el cursor una posició a la dreta |
| 254 | FE | 16 | 10 | Mou el cursor una posició a l'esquerra |
| 254 | FE | 28 | 1C | Desplaça a la dreta |
| 254 | FE | 24 | 18 | Desplaça a l'esquerra |
| 254 | FE | 12 | 0C | Activa la visualització |
| 254 | FE | 8 | 08 | Desactiva la visualització |
| 254 | FE | 14 | 0E | Activa la visualització del cursor subratllat |
| 254 | FE | 13 | 0D | Activa la visualització del cursor intermitent |
| 254 | FE | 12 | 0C | Desactiva la visualització del cursor |
Les adreces de la memòria són de set bits (0 a 6). Per dir-li al controlador a quina adreça volem escriure cal enviar-li un caràcter de control FE (254 decimal) seguit del valor de l'adreça però amb el bit 7 a 1. Així per demanar-li que es posi a la primera posició (adreça 0, o sigui 00000000) li enviarem el caràcter de control seguit de 10000000. Un cop li hem dit la posició i hem escrit un caràcter, el següent es posarà al costat. Per tant només hem de canviar l'adreça quan la posició no sigui consecutiva. A continuació hi ha un tros de programa com a exemple:
// Definició de les funcions que farem servir void EnviaL(char Caracter); // Envia un caràcter void Esborra(void); // Esborra la pantalla i posa el cursor a l'inici void Cursor(char Filera, char Columna); // Posiciona el cursor (filera 1 a 2 i columna 1 a 32, segons pantalla)
... Cursor(2, 1); // Posició ...
void EnviaL(char Caracter) {
TXREG = Caracter; // Agafa el caràcter i l'envia
__delay_ms(1); // Donem temps
while (PIR1bits.TXIF == 0) // Esperem que s'acabi d'enviar
; // No fem res
}
void Esborra(void) {
EnviaL(254); // Caràcter de control
EnviaL(1); // Esborra la pantalla i posa el cursor a l'inici
}
void Cursor(char Filera, char Columna) {
char Posicio = 0; // Variable per a calcular la posició
if (Filera == 2) {
Posicio = 64; // La primera columna de la segona fila és 64;
}
if (Columna > 0 && Columna < 33) { // Comprovem que sigui un valor raonable
Posicio = Posicio + Columna; // Sumem les adreces
Posicio = Posicio - 1; // Restem 1 perquè numera des de 0
}
Posicio = Posicio + 128; // Posa el bit de posicionat a 1
EnviaL(254); // Control de la posició del cursor
EnviaL(Posicio); // Canvia el cursor de lloc
}
Quan s'han d'enviar paraules o altres textos predefinits el programa es pot tornar força llarg si es va posant cada caràcter com a paràmetre quan es crida la funció EnviaL. Pot ser una bona idea guardar els textos a la memòria de programa en un vector i fer una funció que els agafi seqüencialment i els enviï a la pantalla. Com a referència, es pot consultar l'exemple MP.
Per escriure valors a la pantalla caldrà passar-los a format BCD (per tenir-los separats en xifres) i convertir a ASCII cada una de les xifres.
La pantalla pot mostrar fins a 8 caràcters definits per l'usuari. Aquests caràcters són adreçables amb els codis ASCII del 0 (00h) al 7 (07h).
Per crear els caràcters cal enviar una seqüencia de vuit bytes amb el patró corresponent. Els caràcters són de 5 per 8 píxels (punts) o sigui que hem d'enviar vuit valors en els que els tres primers bits (5, 6 i 7) són 0 i els altres cinc (0 a 5) són 1 si volem el punt activat i 0 si el volem desactivat. La següent figura mostra els 8 bytes que caldrien per crear la lletra Ω.

Els caràcters especials es guarden a una altra part de la memòria de la pantalla. El primer caràcter s'ha de colocar a les adreces 00h a 07h, el segon a les 08h a 0Fh i així successivament. El vuitè anirà de la 38h a la 3Fh.
El vuitè byte normalment ha d'estar a zero ja que allí és on ha d'aparèixer el subratllat del cursor. És recomanable que estigui a zero perquè en alguns programes hem detectat caràcters anòmals a la pantalla si no ho estaven.
Les adreces de la memòria on es guarden els caràcters especials són de sis bits (0 a 5). Per dir-li al controlador a quina adreça volem escriure cal enviar-li un caràcter de control (254, o sigui FEh) seguit del valor de l'adreça però amb el bit 7 a 0 i el 6 a 1. Així per demanar-li que es posi a la primera posició (adreça 00h, 00000000) li enviarem el caràcter de control 01000000. Un cop li hem dit la posició i hem escrit un byte, el següent es posarà al costat. Per tant només hem de canviar l'adreça quan la posició no sigui consecutiva.
A continuació hi ha un tros de programa com a exemple:
// Definició de les funcions que farem servir void EnviaL(char Caracter); // Envia un caràcter void Esborra(void); // Esborra la pantalla i posa el cursor a l'inici void Cursor(char Filera, char Columna); // Posiciona el cursor (filera 1 a 2 i columna 1 a 32, segons pantalla) void DefCarac(char Numero, char Fileres[8]); // Crea un caràcter definit per l'usuari
char Omega[8] = {0b00000000, 0b00001110, 0b00010001, 0b00010001, 0b00001010, 0b00001010, 0b00011011, 0b00000000};
... DefCarac(0, Omega); // Defineix caràcter ... Cursor(1, 1); // Posició EnviaL(0); // Omega ... }
void EnviaL(char Caracter) {
TXREG = Caracter; // Agafa el caràcter i l'envia
__delay_ms(1); // Donem temps
while (PIR1bits.TXIF == 0) // Esperem que s'acabi d'enviar
; // No fem res
}
void Esborra(void) {
EnviaL(254); // Caràcter de control
EnviaL(1); // Esborra la pantalla i posa el cursor a l'inici
}
void Cursor(char Filera, char Columna) {
char Posicio = 0; // Variable per a calcular la posició
if (Filera == 2) {
Posicio = 64; // La primera columna de la segona fila és 64;
}
if (Columna > 0 && Columna < 33) { // Comprovem que sigui un valor raonable
Posicio = Posicio + Columna; // Sumem les adreces
Posicio = Posicio - 1; // Restem 1 perquè numera des de 0
}
Posicio = Posicio + 128; // Posa el bit de posicionat a 1
EnviaL(254); // Control de la posició del cursor
EnviaL(Posicio); // Canvia el cursor de lloc
}
void DefCarac(char Numero, char Fileres[8]) {
char Posicio = 0; // Variable per a calcular la posició
if (Numero < 8) { // Comprovem que sigui un valor raonable
Posicio = Numero * 8; // Calculem l'adreça de memòria
}
Posicio = Posicio + 64; // Posa el bit de caràcter especial a 1
EnviaL(254); // Control de la posició del cursor
EnviaL(Posicio); // Canvia el cursor de lloc
for (signed char k = 0; k < 8; k++){
EnviaL(Fileres[k]); // Canvia el cursor de lloc
}
}
En aquest exemple hem fet servir EnviaL(0) per enviar la lletra omega, indicant el codi ASCII 0. Podríem haver fet servir també EnviaL('\0') amb el mateix resultat. En el cas d'enviar text, podem enviar el caràcter especial guardat a la posició 1 així:
...
EnviaT("k\1it"); // Envia un text
...
}
i funcionaria. Però, en canvi, no podem enviar el caràcter especial 0 d'aquesta manera:
...
EnviaT("k\0it"); // Envia un text
...
}
perquè '\0' és com es codifica el final del text. Si fem això, rebrem només la k i no la resta de caràcters. Només es poden posar en forma de text els caràcters especials de l'1 al 7, no el 0. No obstant, si necessitem menys de vuit caràcters especials, podem guardar-los a partir de la posició 1 i ja no tenim cap problema. En cas de necessitar el 0, caldrà enviar-lo com a caràcter, no dins un text.
En algunes versions de la pantalla, no en totes, es pot variar la seva lluminositat. Per canviar la lluminositat de la pantalla s'envia el caràcter de control 7Ch (124 decimal) seguit d'un valor decimal entre 128 i 157; per tant, tenim 30 possibles valors de la lluminositat. A la taula següent tenim alguns valors de lluminositat aproximats:
| Caràcter de control | Valor | Lluminositat | ||
| decimal | hexadecimal | decimal | hexadecimal | |
| 124 | 7C | 128 | 80 | 0 % (apagat) |
| 124 | 7C | 136 | 88 | 25 % |
| 124 | 7C | 143 | 8F | 50 % |
| 124 | 7C | 151 | 97 | 75 % |
| 124 | 7C | 157 | 9D | 100 % |
El controlador de la pantalla porta configurat, de fàbrica, el tipus de pantalla al qual està connectat. Aquesta configuració està guardada en una memòria no volàtil i, per tant, és permanent. Esporàdicament, però, pot succeir que aquesta configuració es malmeti. En aquest cas, podem fer servir les següents comandes per ajustar els paràmetres apropiats:
| Primer caràcter de control | Segon caràcter de control | Funció | ||
| decimal | hexadecimal | decimal | hexadecimal | |
| 124 | 7C | 3 | 03 | Amplada de vint caràcters |
| 124 | 7C | 4 | 04 | Amplada de setze caràcters |
| 124 | 7C | 5 | 05 | Quatre línies |
| 124 | 7C | 6 | 06 | Dues línies |
En algunes aplicacions pot ser necessari modificar la velocitat del canal sèrie que fem servir per enviar els caràcters a la pantalla. Cal tenir present que immediatament després de rebre el segon caràcter de la comanda es canviarà la velocitat i, per tant, els següents caràcters s'hauran d'enviar a la nova velocitat seleccionada. Par a canviar la velocitat podem fer servir les següents comandes:
| Primer caràcter de control | Segon caràcter de control | Funció | ||
| decimal | hexadecimal | decimal | hexadecimal | |
| 124 | 7C | 11 | 0B | 2400 bits per segon (baud) |
| 124 | 7C | 12 | 0C | 4800 bits per segon (baud) |
| 124 | 7C | 13 | 0D | 9600 bits per segon (baud) |
| 124 | 7C | 14 | 0E | 14400 bits per segon (baud) |
| 124 | 7C | 15 | 0F | 19200 bits per segon (baud) |
| 124 | 7C | 16 | 10 | 38400 bits per segon (baud) |
| 18 | 12 | Reinicia a 9600 bits per segon ★ | ||
★ Aquesta comanda està pensada per a aquells casos en els quals desconeixem quina és la velocitat que hi ha configurada en un moment donat. Enviant el caràcter 12h (18 en decimal) durant els 500 ms posteriors a l'alimentació de la pantalla es produirà un reinici de la velocitat a 9600 bits per segon. Aquest caràcter s'ha d'enviar a 9600 baud, independentment de quina sigui la velocitat que hi ha configurada. És recomanable tornar a reiniciar la pantalla després de fer servir aquesta comanda.

Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.