Pantalla de Cristal Líquido (LCD 2×16), conexión al microcontrolador

No siempre los display de 7 segmentos van a cumplir la tarea de presentar una información completa, puede que en ciertas ocasiones necesitemos mostrar mensajes en texto o caracteres especiales. Una opción que tenemos son las pantallas de cristal líquido o LCD por sus siglas en inglés (Liquid Crystal Display).

Estas pantallas son relativamente económicas y son fáciles de manejar, a continuación explicaremos como se conecta y se programa el microcontrolador para poderse usar de un modo básico, esto con el fin de no extendernos mucho, sin embargo al final dejamos los enlaces a otros tutoriales donde explicamos más a fondo el funcionamiento de este dispositivo además de otros ejemplos de uso.

El LCD posee 3 pines para control y 8 para datos, según los bits que pongamos en los pines de control podemos, a través de los pines de datos, enviar información de configuración o para imprimir en pantalla.

Los pines de control son: E, RS y R/W.

E (Enable): Con un 1 habilita el paso de datos entre el controlador y el LCD, con un 0 lo inhabilita.

RS (Register Select): Nos permite escoger a que registro del LCD vamos a escribir información, con un 1 en RS, enviamos un dato para mostrar en pantalla, con un 0 enviamos un dato de configuración.

R/W (Read / Write): Según el estado en que se encuentre, permitirá escribir en el LCD o leer la información que contiene, con 1 lee lo que hay en el LCD con 0 puede escribir en él.

 

Si aún no conoces el funcionamiento del LCD, te invitamos a seguir los enlaces al final para familiarizarte con el componente.

 

En el siguiente grafico vemos el circuito, con este ejemplo explicamos las funciones que usamos para controlar el LCD desde el microcontrolador, así que solo enviaremos un único mensaje estático.

Este es el código debidamente documentado al final explicamos algunas nuevas líneas y operadores que aparecen:

#include <xc.h>

#pragma config FOSC = XT        // Oscilador con cristal de cuarzo de 4MHz
                                //conectado en los pines 15 y 16
#pragma config WDTE = OFF       // Perro guardián (WDT deshabilitado)
#pragma config MCLRE = ON       // Master clear habilitado (pin reset)

#define _XTAL_FREQ 4000000      // Oscilador a 4MHz

// Definimos macros
#define Enable PORTAbits.RA0    // Nombramos el pin RA0 como Enable
#define RS PORTAbits.RA1        // Nombramos el pin RA1 como RS
#define Datos PORTB             // Nombramos todo el puerto B como Datos

// Declaramos la funciones que vamos a usar
void inicio_lcd(void);                      // Inicialización del LCD
void config_lcd(void);                      // Configuración del LCD
void irapos_lcd(unsigned char posicion);    // Función para ir a una posición
                                            // dentro del LCD.
void escribir_lcd(unsigned char caracter);  // Escribir un carácter ASCII en el LCD
void cadena_lcd(char *cadena);              // Escribir una cadena de caracteres en el LCD

void main(){
    TRISA = 0xF0;                  // Puerto A salida
    TRISB = 0x00;                  // Puerto B salida
    ANSELA = 0x00;                 // Puerto A Digital
    ANSELB = 0x00;                 // Puerto B Digital
    PORTA = 0x00;                  // Limpiamos el Puerto A
    PORTB = 0x00;                  // Limpiamos el Puerto B

    // Usamos la función para inicializar y configurar el LCD
    inicio_lcd();

    while(1){                   // Inicio del código a ejecutar constantemente

        irapos_lcd(0);                  // Nos ubicamos en la primera línea posición 0
        cadena_lcd("  koalab.tech");    // Enviamos el mensaje a la primera línea
        irapos_lcd(64);                 // Nos ubicamos en la segunda línea posición 0
        cadena_lcd(" LCD PIC16F1827 "); // Enviamos el mensaje a la segunda línea

        while(1){
                               // hacemos un loop infinito, pues ya enviamos el mensaje
        }
    }
}

// Funciones
// Inicialización del LCD
void inicio_lcd(void){
    
    // Llevar el cursor a la posición inicial //
    Datos = 2;                  // Enviamos el código correspondiente al puerto
    config_lcd();               // Llamamos función de configuración que habilita
                                // y envía la configuración al LCD

    // Seleccionamos bus a 8 bits, trabajo a dos líneas y tamaño de carácter 5x7 //
    Datos = 56;                 // Enviamos el código correspondiente al puerto
    config_lcd();               // Llamamos función de configuración que habilita
                                // y envía la configuración al LCD

    // Encendido del display, encendido del cursor con parpadeo //
    Datos = 15;                 // Enviamos el código correspondiente al puerto
    config_lcd();               // Llamamos función de configuración que habilita
                                // y envía la configuración al LCD

    // Dirección del movimiento del cursor hacia la derecha //
    Datos = 6;                  // Enviamos el código correspondiente al puerto
    config_lcd();               // Llamamos función de configuración que habilita
                                // y envía la configuración al LCD

    // Borrado del display //
    Datos = 1;                  // Enviamos el código correspondiente al puerto
    config_lcd();               // Llamamos función de configuración que habilita
                                // y envía la configuración al LCD
}

// Configuración del LCD
void config_lcd(void){
    RS = 0;                     // Con RS en 0 le indicamos al LCD que le vamos a enviar un dato de configuración
    Enable = 1;                 // Con Enable en 1 habilitamos la comunicación con el LCD
    __delay_ms(2);              // Puesto que la duración máxima de cualquier operación en el LCD
                                // tarda 1,64ms le damos un retardo de 2ms.
    Enable = 0;                 // Una vez pasan los 2ms cerramos la comunicación escribiendo un 0 en Enable
}

// Ir a una posición en el LCD
void irapos_lcd(unsigned char posicion){        // Esta función recibe un parametro con la posición
// donde se ubicará el cursor
    RS = 0;                                   	// Con RS en 0 le indicamos al LCD que le vamos a
						// enviar un dato de configuración

    escribir_lcd(128 + posicion);                   // Llamamos la función de escribir en el LCD al tiempo
                                                    //que le enviamos el comando de ubicación en la
                                                    // memoria DDRAM del LCD sumándole el dato de donde
                                                    // ira posicionado
}

// Escribir en el LCD
void escribir_lcd(unsigned char caracter){      // Esta función recibe un parámetro con el carácter a
// escribir en el LCD
    Datos = (caracter);                         // Enviamos el carácter a el puerto
    Enable = 1;                                 // Activamos la comunicación con el LCD
    __delay_ms(2);                              // Retardo para realizar la tarea en el LCD
    Enable = 0;                                 // Finalizamos la conexión al LCD
}

// Escribir cadena de caracteres
void cadena_lcd(char *cadena){           // Esta función escribe una cadena de caracteres en el LCD
    RS = 1;                                     // Con RS = 1 le indicamos al LCD que vamos a enviar             
                                                // un dato para que lo muestre
    while(*cadena){                             // Mientras hayan caracteres en la variable cadena
        escribir_lcd(*cadena++);                // Enviamos el siguiente carácter
    }
}

En el anterior código encontramos nuevas líneas, con palabras u operadores que aún no habíamos usado.

// Definimos macros
#define Enable PORTAbits.RA0    // Nombramos el pin RA0 como Enable
#define RS PORTAbits.RA1        // Nombramos el pin RA1 como RS
#define Datos PORTB             // Nombramos todo el puerto B como Datos

Un macro es una secuencia de instrucciones, las cuales se incluyen en el código cada vez que es invocado por su nombre. Hay que tener en cuenta que un macro es diferente a una función, aunque su definición sea similar.

#define es una equivalencia, es decir, que cada que escribamos dentro del código la palabra con la que nombremos la secuencia de instrucciones, el programa cargará en ese punto tal secuencia. Esto es de gran ayuda a la hora de realizar algún cambio, dado que con solo editar la secuencia de instrucciones estaremos modificando el resto sin riesgo de equivocarnos u omitir partes.

Un macro #define se declara de esta manera:

Se antepone la palabra #define, seguida del nombre que le vamos a asignar, finalizando en las instrucciones.

#define NOMBRE_MACRO SECUENCIA_DE_INSTRUCCIONES

Es de esta manera que en nuestro código donde escribimos “Enable”, estamos insertando el código PORTAbits.RA0, o sea, el bit cero del puerto A que esta está conectado al pin Enable del LCD. Con la palabra “RS” insertamos PORTAbits.RA1, bit 1 del puerto A conectado al pin RS del LCD y por ultimo “Datos” queda asignado a todo el puerto B, el cual está conectado desde RB0 a RB7 a los pines D0 a D7 del LCD respectivamente.

Recuerda que al final dejamos los enlaces a los artículos donde profundizamos en el uso del LCD.

Al final encontramos un operador nuevo que se antepone al nombre de una variable:

// Escribir cadena de caracteres
void cadena_lcd(char *cadena){           // Esta función escribe una cadena de caracteres en el LCD
    RS = 1;                                     // Con RS = 1 le indicamos al LCD que vamos a enviar
// un dato para que lo muestre
    while(*cadena){                             // Mientras hayan caracteres en la variable cadena
        escribir_lcd(*cadena++);                // Enviamos el siguiente carácter
    }
}

Dentro de la función “Escribir cadena de caracteres”, vemos un asterisco (*) que se ubica antes de la variable cadena, símbolo del asterisco no funciona como multiplicador sino que es un apuntador.

El tema de los apuntadores se explica a mayor profundidad un artículo dedicado a ellos, pero básicamente este símbolo de apuntador nos ayuda a recorrer la palabra, carácter a carácter e ir enviando uno a uno al LCD hasta que este finalice, es por eso que a la función “cadena_lcd()”, le podemos pasar como parámetro un texto, para este caso de 16 caracteres que es el máximo que se pueden representar en la pantalla.

En los siguientes enlaces encontraras un manual completo del funcionamiento del LCD y otros ejemplos de cómo usarlo: