Jugando con un LCD
1. Introducción:
El lcd es un elemento que se usa como interface. En nuestro caso usaremos un lcd alfanumérico.
Cada lcd integra un controlador que es el encargado de "dibujar" la pantalla. Nosotros le indicaremos a este controlador lo que queremos presentar.
La mayoría de los LCD son compatibles con el HD44780 y en el datasheet encontraremos información sobre la secuencia para comunicarnos y los
diferentes comandos.
Algunas características:
- Comunicación en bus paralelo de 4 u 8 bits.
- Comunicación en serie (uart).
- Comunicación en bus I2C.
- Estos lcd nos permiten guardar en su memoria símbolos que nosotros mismos diseñemos; es decir, tienen reservado un espacio de memoria dónde podremos almacenar un símbolo nuevo si no lo tiene. No entraremos en esto.
2. Conexiones:
Nosotros usaremos el lcd PC2004ARS que usa un bus paralelo de 4 u 8 bits y tiene 4 líneas de 20 caracteres cada una.



Pines:
- VOUT - Ajuste de contraste, para esto usamos RV5 de nuestra placa.
- RS - Selecciona entre instrucciones o datos.
- R/W - Seleccionamos leer o escribir en el lcd.
- E - Pin Enable, si está a nivel alto, el lcd recoge los datos.
- D0 - D7 - Bus para leer o escribir en el lcd. Hay una forma de trabajar usando un bus de 4 bits D4 - D7.
- BLA + BLK - Retroiluminación. Nuestro display no tiene, por lo tanto no lo conectamos.
Nota: Cambian con respecto al esquema de la placa. Fijarse en la serigrafía.
Nota: El display que estoy usando no tiene retroiluminación.
3. Estructura del proyecto:
Desde el Menú "File", vamos a crear un nuevo proyecto (seguimos los pasos descritos con anterioridad).

Creamos el nuevo proyecto y añadimos los siguientes archivos:
- Config.h - Fichero con la configuración de fuses del pic.
- Typedefs.h - Fichero con las definiciones del compilador XC8.
- Lcd.h y lcd.c - Ficheros adaptados de la MLA.
- Main.c - Fichero principal.
4. Adaptando las liberías existentes:
Nos basaremos en las librerías de muestra que tenemos en el siguiente directorio: C:\Program Files
(x86)\Microchip\Mla\v2016_11_07\bsp\picdem_fs_usb. Modificaremos lcd.c; lcd.h se mantiene igual al original:




La diferencia entre estas dos funciones, es el pin RS; para enviar datos lo ponemos a nivel alto y para enviar comandos
lo ponemos a nivel bajo.
4.1. Ejemplo 1:
En este sencillo ejemplo escribimos carácter a carácter, o enviamos una línea de texto.
Nota: Los pines de datos están compartidos con los led. Para deshabilitar los
led, basta con eliminar el jumper "J9" que se encuentra junto a estos.
#include#include "config.h" #include "typedefs.h" #include "lcd.h" #include "xc.h" #define _XTAL_FREQ 48000000 void delay_ms(uint16_t); void main(void) { LCD_Initialize(); delay_ms(100); while (1) { LCD_ClearScreen(); LCD_PutChar('H'); LCD_PutChar('o'); LCD_PutChar('l'); LCD_PutChar('a'); delay_ms(5000); LCD_ClearScreen(); LCD_PutString("Hoy es Martes 11 de Julio de 2017. Hace buen tiempo para ir a la playa.", 70); delay_ms(5000); } } void delay_ms(uint16_t i) { for(uint16_t x=0; x < i; x++) { __delay_ms(1); } }
5. Ampliando las librerías:
Buscamos alcanzar los siguientes objetivos (Nos ayudaremos del datasheet del display):
- Definir los pines independientemente del puerto (la librería anterior, no funciona si los pines de datos pertenecen a diferentes puertos).
- Poder trabajar con bus de 4 bits u 8 bits.
- Definir las columnas y líneas del display (para que haga el salto de línea automáticamente).
- Añadir funciones útiles, como "scroll".
5.1. Adaptando Lcd.h:
/******************************************************************************* * LIBERERIA LCD MODIFICADA * * - Asignación de pines independientes al puerto * - Funcionamiento en bus de 8 y 4 bits * - Definicion del numero de filas y columnas del display * - Algunas funciones como SCROLL *******************************************************************************/ #include#include #include "config.h" //Necesaria porque es donde esta definido el _XTAL_FREQ //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE INICIALIZAR EL SISTEMA // // Configura los pines como salida // Configura el display en el modo correcto //////////////////////////////////////////////////////////////////////////////// void LCD_Initialize(void); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE HACER SCROLL DEL TEXTO // //////////////////////////////////////////////////////////////////////////////// void LCD_ScrollLeft (void); void LCD_ScrollRight (void); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA PONER CURSOR EN UNA POSICION // //////////////////////////////////////////////////////////////////////////////// void LCD_SetCursor ( uint8_t linea, uint8_t columna ); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA PONER CURSOR EN UNA POSICION // //////////////////////////////////////////////////////////////////////////////// void LCD_SetCursor (uint8_t linea, uint8_t columna ); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ESCRIBIR UNA CADENA // //////////////////////////////////////////////////////////////////////////////// void LCD_PutString ( char* inputString , uint16_t length ); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ESCRIBIR CARACTERES // // Recibe caracteres y cambia de linea automaticamente //////////////////////////////////////////////////////////////////////////////// void LCD_PutChar(char); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE BORRAR DISPLAY // // Borra display // Salta a la posicion inicial //////////////////////////////////////////////////////////////////////////////// void LCD_ClearScreen(void); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE HACER VISIBLE O NO EL CURSOR // //////////////////////////////////////////////////////////////////////////////// void LCD_CursorEnable(bool enable); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ENVIAR DATOS AL DISPLAY // // Segun el tipo de configuracion 4bits o 8bits //////////////////////////////////////////////////////////////////////////////// static void LCD_SendData ( char data ); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ENVIAR COMANDOS AL DISPLAY // // Segun el tipo de configuracion 4bits o 8bits //////////////////////////////////////////////////////////////////////////////// static void LCD_SendCommand ( char command , unsigned int delay ); //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE HACER RETARDO uS // //////////////////////////////////////////////////////////////////////////////// static void LCD_Wait_us ( unsigned int delay );
5.2. Adaptando Lcd.c:
/******************************************************************************* * LIBERERIA LCD MODIFICADA * * - Asignación de pines independientes al puerto * - Funcionamiento en bus de 8 y 4 bits * - Definicion del numero de filas y columnas del display * - Algunas funciones como SCROLL *******************************************************************************/ #include#include "lcd.h" #include //TIEMPOS DE RETARDO PARA EJECUCION SEGUN OPERACION #define LCD_HS_OPERT 50 //Operacion rapida > 43uS #define LCD_NS_INSTR 1400 //Operacion tipica > 1.35ms #define LCD_INI_TIME 30000 //Operacion de arranque > 30ms //INDICAMOS LOS CARACTERES POR LINEA QUE TIENE NUESTRO DISPLAY #define LCD_MAX_COLUMN 20 //INDICAMOS LAS LINEAS QUE TIENE NUESTRO DISPLAY (Las direcciones están en el datasheet del lcd) #define LCD_MAX_ROW 4 #define LCD_COMMAND_ROW_0_HOME 0x80 #define LCD_COMMAND_ROW_1_HOME 0xC0 #define LCD_COMMAND_ROW_2_HOME 0x94 #define LCD_COMMAND_ROW_3_HOME 0xD4 //COMANDOS QUE ACEPTA EL CONTROLADOR HD44780 #define LCD_COMMAND_CLEAR_SCREEN 0x01 //Borra la pantalla. Tiempo ejecucion: 1.64ms #define LCD_COMMAND_RETURN_HOME 0x02 //Coloca el cursor al inicio. Tiempo ejecucion: 1.64ms #define LCD_COMMAND_ENTER_DATA_MODE 0x06 //Autoincremento a la derecha. Tiempo ejecucion: 40us #define LCD_COMMAND_CURSOR_OFF 0x0C //Cursor no visible. Tiempo ejecucion: 40us #define LCD_COMMAND_CURSOR_ON 0x0F //Cursor visible. Tiempo ejecucion: 40us #define LCD_COMMAND_MOVE_CURSOR_LEFT 0x10 //Desplaza cursor a la izquierda. Tiempo ejecucion: 40us #define LCD_COMMAND_MOVE_CURSOR_RIGHT 0x14 //Desplaza cursor a la derecha. Tiempo ejecucion: 40us #define LCD_COMMAND_SET_MODE_8_BIT 0x38 //8 bits, 2 lineas. Tiempo ejecucion: 40us #define LCD_COMMAND_SET_MODE_4_BIT 0x28 //4 bits, 2 lineas. Tiempo ejecucion: 40us // DEFINIMOS LOS PINES QUE USAREMOS PARA COMUNICARNOS CON EL DISPLAY //////////PINES DE CONTROL //EN: Al pasar de 1 a 0, el controlador lee el bus ya sea para control o datos //RW: Cuando esta a 0 escribimos en el lcd. Cuando esta a 1 leemos del lcd. Si vamos a // escribir siempre, podemos conectarla a gnd. //RS: Cuando esta a 0 indica comando. Cuando esta a 1 indica texto #define LCD_RS_pin LATAbits.LATA5 #define LCD_RS_tris TRISAbits.TRISA5 #define LCD_RW_pin LATAbits.LATA4 #define LCD_RW_tris TRISAbits.TRISA4 #define LCD_E_pin LATAbits.LATA3 #define LCD_E_tris TRISAbits.TRISA3 //////////PINES DE DATOS #define LCD_MODE_4 true //Indica si usamos el modo 4 pines o 8 pines de datos // En modo 4 bits usamos los pines de mayor peso (D4-D5-D6-D7)) // Para transferir los 8 bits usando un bus de 4 bits, primero transferimos los bits // mas altos del dato (D4-D7) y luego los mas bajos (D0-D3). Hacemos dos envios de 4 bits. #define LCD_D0_pin LATDbits.LATD0 #define LCD_D0_tris TRISDbits.TRISD0 #define LCD_D1_pin LATDbits.LATD1 #define LCD_D1_tris TRISDbits.TRISD1 #define LCD_D2_pin LATDbits.LATD2 #define LCD_D2_tris TRISDbits.TRISD2 #define LCD_D3_pin LATDbits.LATD3 #define LCD_D3_tris TRISDbits.TRISD3 #define LCD_D4_pin LATDbits.LATD4 #define LCD_D4_tris TRISDbits.TRISD4 #define LCD_D5_pin LATDbits.LATD5 #define LCD_D5_tris TRISDbits.TRISD5 #define LCD_D6_pin LATDbits.LATD6 #define LCD_D6_tris TRISDbits.TRISD6 #define LCD_D7_pin LATDbits.LATD7 #define LCD_D7_tris TRISDbits.TRISD7 /* Private variables ************************************************/ static uint8_t row ; static uint8_t column ; //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE INICIALIZAR EL SISTEMA // // Configura los pines como salida // Configura el display en el modo correcto //////////////////////////////////////////////////////////////////////////////// void LCD_Initialize(void) { LCD_RS_tris = 0; //Pines de control como salidas LCD_RW_tris = 0; LCD_E_tris = 0; LCD_RW_pin = 0; //Inicializamos los pines de control LCD_RS_pin = 0; LCD_E_pin = 0; if (!LCD_MODE_4) { LCD_D0_tris = 0; //Configuramos los pines como salidas LCD_D1_tris = 0; LCD_D2_tris = 0; LCD_D3_tris = 0; LCD_D4_tris = 0; LCD_D5_tris = 0; LCD_D6_tris = 0; LCD_D7_tris = 0; LCD_D0_pin = 0; //Ponemos los pines a nivel bajo LCD_D1_pin = 0; LCD_D2_pin = 0; LCD_D3_pin = 0; LCD_D4_pin = 0; LCD_D5_pin = 0; LCD_D6_pin = 0; LCD_D7_pin = 0; LCD_E_pin = 1; //Inicializamos display en modo 8 bits LCD_Wait_us ( LCD_INI_TIME ) ; LCD_SendCommand ( LCD_COMMAND_SET_MODE_8_BIT, LCD_HS_OPERT + LCD_INI_TIME ) ; LCD_SendCommand ( LCD_COMMAND_CURSOR_OFF, LCD_HS_OPERT ) ; LCD_SendCommand ( LCD_COMMAND_ENTER_DATA_MODE, LCD_NS_INSTR ) ; } else { LCD_D4_tris = 0; //Configuramos los pines como salidas LCD_D5_tris = 0; LCD_D6_tris = 0; LCD_D7_tris = 0; LCD_D4_pin = 0; //Ponemos los pines a nivel bajo LCD_D5_pin = 0; LCD_D6_pin = 0; LCD_D7_pin = 0; LCD_E_pin = 1; //Inicializamos display en modo 4 bits LCD_Wait_us ( LCD_INI_TIME ) ; LCD_D4_pin = 1; LCD_D5_pin = 1; LCD_E_pin = 1; LCD_Wait_us ( 50 ) ; LCD_E_pin = 0; LCD_Wait_us ( 5000 ) ; LCD_E_pin = 1; LCD_Wait_us ( 50 ) ; LCD_E_pin = 0; LCD_Wait_us ( 100 ); LCD_E_pin = 1; LCD_Wait_us ( 50 ) ; LCD_E_pin = 0; LCD_Wait_us ( 5000 ) ; LCD_D4_pin = 0; LCD_E_pin = 1; LCD_Wait_us ( 50 ) ; LCD_E_pin = 0; LCD_Wait_us ( 100 ) ; LCD_SendCommand ( LCD_COMMAND_SET_MODE_4_BIT, LCD_HS_OPERT); LCD_SendCommand ( 0x0C, LCD_HS_OPERT); LCD_SendCommand ( 0x01, LCD_HS_OPERT); LCD_SendCommand ( 0x07, LCD_HS_OPERT); } LCD_ClearScreen ( ) ; } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE HACER SCROLL DEL TEXTO // //////////////////////////////////////////////////////////////////////////////// void LCD_ScrollLeft (void) { LCD_SendCommand (0x10 | 0x08 | 0x00, LCD_HS_OPERT); } void LCD_ScrollRight (void) { LCD_SendCommand (0x10 | 0x08 | 0x04, LCD_HS_OPERT); } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA PONER CURSOR EN UNA POSICION // //////////////////////////////////////////////////////////////////////////////// void LCD_SetCursor (uint8_t linea, uint8_t columna ) { if (linea == 0) linea = LCD_COMMAND_ROW_0_HOME; else if (linea == 1) linea = LCD_COMMAND_ROW_1_HOME; else if (linea == 2) linea = LCD_COMMAND_ROW_2_HOME; else linea = LCD_COMMAND_ROW_3_HOME; if (columna > LCD_MAX_COLUMN) columna = LCD_MAX_COLUMN; row = linea; column = columna; LCD_SendCommand (0x80 | (row+column), LCD_HS_OPERT); } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ESCRIBIR UNA CADENA // //////////////////////////////////////////////////////////////////////////////// void LCD_PutString ( char* inputString , uint16_t length ) { while (length--) { switch (*inputString) { case 0x00: return ; default: LCD_PutChar ( *inputString++ ) ; break ; } } } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ESCRIBIR CARACTERES // // Recibe caracteres y cambia de linea automaticamente //////////////////////////////////////////////////////////////////////////////// void LCD_PutChar ( char inputCharacter ) { if (column == LCD_MAX_COLUMN) { column = 0 ; if (row == 0 && LCD_MAX_ROW > 1) { LCD_SendCommand ( LCD_COMMAND_ROW_1_HOME , LCD_NS_INSTR ) ; row = 1 ; } else if (row == 1 && LCD_MAX_ROW > 2) { LCD_SendCommand ( LCD_COMMAND_ROW_2_HOME , LCD_NS_INSTR ) ; row = 2 ; } else if (row == 2 && LCD_MAX_ROW > 3) { LCD_SendCommand ( LCD_COMMAND_ROW_3_HOME , LCD_NS_INSTR ) ; row = 3 ; } else { LCD_SendCommand ( LCD_COMMAND_ROW_0_HOME , LCD_NS_INSTR ) ; row = 0 ; } } LCD_SendData ( inputCharacter ) ; column++ ; } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE BORRAR DISPLAY // // Borra display // Salta a la posicion inicial //////////////////////////////////////////////////////////////////////////////// void LCD_ClearScreen ( void ) { LCD_SendCommand ( LCD_COMMAND_CLEAR_SCREEN , LCD_NS_INSTR ) ; LCD_SendCommand ( LCD_COMMAND_RETURN_HOME , LCD_NS_INSTR ) ; row = 0 ; column = 0 ; } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE HACER VISIBLE O NO EL CURSOR // //////////////////////////////////////////////////////////////////////////////// void LCD_CursorEnable ( bool enable ) { if (enable == true) { LCD_SendCommand ( LCD_COMMAND_CURSOR_ON , LCD_NS_INSTR ) ; } else { LCD_SendCommand ( LCD_COMMAND_CURSOR_OFF , LCD_NS_INSTR ) ; } } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ENVIAR DATOS AL DISPLAY // // Segun el tipo de configuracion 4bits o 8bits //////////////////////////////////////////////////////////////////////////////// static void LCD_SendData ( char data ) { LCD_RW_pin = 0; LCD_RS_pin = 1; //LATD = (LATD & 0xFF00) | data; if (!LCD_MODE_4) { LCD_D0_pin = 0x01 & data>>0; //Vamos escribiendo en cada pin el valor de posicion que le corresponde LCD_D1_pin = 0x01 & data>>1; LCD_D2_pin = 0x01 & data>>2; LCD_D3_pin = 0x01 & data>>3; LCD_D4_pin = 0x01 & data>>4; LCD_D5_pin = 0x01 & data>>5; LCD_D6_pin = 0x01 & data>>6; LCD_D7_pin = 0x01 & data>>7; } else { LCD_D4_pin = 0x01 & data>>4; LCD_D5_pin = 0x01 & data>>5; LCD_D6_pin = 0x01 & data>>6; LCD_D7_pin = 0x01 & data>>7; LCD_E_pin = 1; LCD_Wait_us ( 50 ) ; LCD_E_pin = 0; LCD_D4_pin = 0x01 & data>>0; //Vamos escribiendo en cada pin el valor de posicion que le corresponde LCD_D5_pin = 0x01 & data>>1; LCD_D6_pin = 0x01 & data>>2; LCD_D7_pin = 0x01 & data>>3; } LCD_E_pin = 1; Nop ( ) ; Nop ( ) ; Nop ( ) ; LCD_E_pin = 0; LCD_RS_pin = 0; LCD_Wait_us (LCD_HS_OPERT ) ; } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE ENVIAR COMANDOS AL DISPLAY // // Segun el tipo de configuracion 4bits o 8bits //////////////////////////////////////////////////////////////////////////////// static void LCD_SendCommand ( char command , unsigned int delay ) { LCD_RW_pin = 0; LCD_RS_pin = 0; //LATD = (LATD & 0xFF00) | command; if (!LCD_MODE_4) { LCD_D0_pin = 0x01 & command>>0; //Vamos escribiendo en cada pin el valor de posicion que le corresponde LCD_D1_pin = 0x01 & command>>1; LCD_D2_pin = 0x01 & command>>2; LCD_D3_pin = 0x01 & command>>3; LCD_D4_pin = 0x01 & command>>4; LCD_D5_pin = 0x01 & command>>5; LCD_D6_pin = 0x01 & command>>6; LCD_D7_pin = 0x01 & command>>7; } else { LCD_D4_pin = 0x01 & command>>4; LCD_D5_pin = 0x01 & command>>5; LCD_D6_pin = 0x01 & command>>6; LCD_D7_pin = 0x01 & command>>7; LCD_E_pin = 1; LCD_Wait_us ( 50 ) ; LCD_E_pin = 0; LCD_D4_pin = 0x01 & command>>0; //Vamos escribiendo en cada pin el valor de posicion que le corresponde LCD_D5_pin = 0x01 & command>>1; LCD_D6_pin = 0x01 & command>>2; LCD_D7_pin = 0x01 & command>>3; } LCD_E_pin = 1; Nop ( ) ; Nop ( ) ; Nop ( ) ; LCD_E_pin = 0; LCD_E_pin = 0; LCD_Wait_us ( delay ) ; } //////////////////////////////////////////////////////////////////////////////// // FUNCION ENCARGADA DE HACER RETARDO uS // //////////////////////////////////////////////////////////////////////////////// static void LCD_Wait_us ( unsigned int delay ) { for ( int x = 0; x < delay; x ++ ) { __delay_us(1); } }
5.3. Ejemplo 2: Usando las funciones avanzadas
Este ejemplo, simplemente va escribiendo texto en el display y hace uso de algunas funciones como "LCD_SetCursor()":
/* * File: main.c * Author: Eduardo * * Created on 11 de julio de 2017, 8:29 */ #include#include "config.h" #include "typedefs.h" #include "lcd.h" #include "xc.h" //#define _XTAL_FREQ 48000000 // se define la frecuencia FOSC -- Hay que tener en cuenta los preescalers usados void delay_ms(uint16_t); uint8_t a = 0; uint8_t value = 0; uint8_t numLed = 0; void main(void) { LCD_Initialize(); delay_ms(100); while (1) { LCD_ClearScreen(); LCD_PutChar('H'); LCD_PutChar('o'); LCD_PutChar('l'); LCD_PutChar('a'); delay_ms(2000); LCD_SetCursor (0,0); LCD_PutChar('A'); LCD_PutChar('d'); LCD_PutChar('i'); LCD_PutChar('o'); LCD_PutChar('s'); delay_ms(2000); LCD_ClearScreen(); LCD_PutString("Martes 11 de Julio de 2017", 26); delay_ms(3000); //LCD_ClearScreen(); LCD_SetCursor (1,0); LCD_PutString("Hoy es Martes 11 de Julio de 2017. Hace buen tiempo para ir a la playa", 70); delay_ms(3000); LCD_SetCursor (0,0); LCD_PutString("Hoy es Martes 11 de ", 20); LCD_SetCursor (1,0); LCD_PutString("Julio de 2017. Hace ", 20 ); LCD_SetCursor (2,0); LCD_PutString("buen tiempo para ir ", 20 ); LCD_SetCursor (3,0); LCD_PutString("a la playa", 10 ); delay_ms(3000); while (true) { LCD_ScrollRight(); delay_ms(500); } LCD_ClearScreen(); } } void delay_ms(uint16_t i) { for ( uint16_t x=0; x < i; x++ ) { __delay_ms(1); } }
- LCD funcionando:
6. Descargas: