LCD 4x20



Una forma sencilla de saber lo que pasa en nuestro microcontrolador es usar un LCD, mediante su uso podemos comprobar el estado de los registros internos del micro, verificar el funcionamiento de la electrónica y los programas que hagamos, por ejemplo si queremos comprobar que la lectura de un ADC en nuestro programa es correcta, pues no tenemos más opciones que usar un LCD o el PC para poder visualizar lo que está leyendo el micro.

Un LCD es un dispositivo que muestra caracteres alfanuméricos en una pantalla de varias líneas, todo LCD lleva un microcontrolador interno que se encarga de gobernar su funcionamiento (el más común es el Hitachi 44780), este microcontrolador tiene unos pines para comunicarse con el mundo exterior y así poder realizar las distintas operaciones sobre la pantalla del LCD, lo que hace que manejar la pantalla sea muy sencillo y que merezca la pena añadirlo a cualquier proyecto mientras se realiza.


Resumen LCD.

Esquema de un LCD.



El lcd dispone de los siguientes pines.


Los 3 primeros son de alimentación, usandose el 3 pin para establecer el contraste del LCD en función de su nivel de tensión mediante un divisor resistivo, la conexión es la siguiente:



Pines del  7 al 14: bus de datos, por donde mandaremos la información al lcd.

Pines de control:
El pin 6: Enable habilita (1) o deshabilita (0) el módulo. Encendiendolo y apagandolo le indicamos al lcd que vamos a realizar una operación sobre él y éste pasa a leer el resto de los pines para recibir la orden.
El pin 5: R/W indica si se va a hacer una operación de escritura (0) o lectura (1).
El pin 4: RS selecciona el registro del LCD que vamos a usar, el LCD tiene dos registros de 8 bits, un registro de instrucciones (0) y otro de datos (1), seleccionando el de instrucciones indicamos al lcd que lo que hay en el bus de datos (pines desde 7 a 14) es una instrucción, y seleccionando el registro de datos indicamos al LCD que lo que tenemos en el bus de datos es un carácter cuyo destino es la DDRAM o la CGRAM que son las dos zonas de memoria del LCD.

Con los pines 4 y 5 tenemos las siguientes combinaciones.


00: mandomos un comando al LCD, como puede ser limpiar la pantalla o situar el cursor en cierto punto de la pantalla. En el bus de datos estará el código del comando a realizar.
01: cuando le damos una orden al lcd éste tarda un tiempo en realizarla, por lo que hasta que no termine no le podemos mandar ordenes nuevas, nos indica que está ocupado poniendo el pin 14 a 1 por lo que para poder leer este pin debemos poner RS a 0 y R/W a 1 (leer). Podemos evitar la lectura de este pin haciendo pausas de cierto tiempo cada vez que mandamos una instrucción.
10 y 11: para leer o escribir datos en la DDRAM y CGRAM.

La DDRAM (Data Display RAM) es una zona de memoria donde se almacenan los caracteres que se van a representar en la pantalla, su capacidad es de 80x8 bits = 80 caracteres, 20 por línea. Cada vez que se escribe un dato en ésta se apunta a la siguiente posición.

Posición en memoria de cada carácter en la pantalla. 

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53
14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27
54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67

Las posiciones de los caracteres en la memoria cuando se usan 4 líneas de pantalla no son consecutivas, de la línea 1 pasa a la línea 3, cosa que tendremos que tener en cuenta si queremos escribir caracteres de manera secuencial.

En la CGRAM podemos dibujar nuestros propios caracteres, se pueden almacenar hasta 4 de 5x10 puntos y hasta 8 de 5x8 puntos.

La CGROM genera los caracteres disponibles para representar en la pantalla a partir de un código de 8 bits.

Cuando tengamos un 1 en el bit RS, los 8 bits del bus de datos representarán uno de los caracteres siguientes.



Con un 0 en RS tendremos que mandar uno de los siguientes comandos por los 8 bits del bus de datos.


Entry mode set: valor 0x06 para el desplazamiento normal a derechas.
    
Function Set:
    DL=0 comunicación mediante 4 bits del bus de datos, a 1 mediante 8 bits.
    F=1 Caracteres 5x10 puntos, 0 caracteres 5x8 puntos.
    N= 1 pantalla de 4 líneas, 0 pantalla de 2 líneas.

Podemos tener un bus de datos de 8 bits usando los 8 pines disponibles en el LCD para este fin, o un bus de datos de 4 bits dejando los bits DB0 a DB3 sin conectar, si lo hacemos de esta forma tendremos que mandar cada carácter e instrucción en dos partes. Debido a que tenemos un micro con muchos pines de I/0 se va a usar la configuración de 8 bits que además de ser más rápida hace que la programación sea más fácil. Si tenemos un micro con pocos pines de I/0 necesitaremos un mínimo de 6 pines para escribir en el LCD (RS, E y 4 de datos).

Para inicializar el LCD lo debemos hacer de la siguiente forma mandando las ordenes según nos indica con las opciones de configuración que deseamos.



Tiempos señales escritura.


Programa para manejar un LCD con la placa de Arduino.

Aunque aún no me he leído la documentación que he reuinido del AVR y el compilador voy a ir escribiendo código y ya me iré mirando las cosas según las vaya necesitando, que así leerse cientos de hojas de pdfs y datasheet se hace menos pesado... Éste es el código para escribir en un LCD con la placa de Arduino Mega con WinAVR (si hay una opción libre hay que usarla) que utilizaré en todos los programas siguientes para verificar el correcto funcionamiento de los distintos componentes que forman el proyecto del robot hexápodo.

Todo ésto ya está hecho y hay librerias disponibles para un LCD (y bien hechas..) por lo que no tiene sentido reinventar la rueda, pero el fin no es crear algo que ya está creado, si no aprender mientras se hace. Si el objetivo fuese hacer un robot hexápodo pues podríamos comprarlo con toda la electrónica y programas ya hechos directamente.

El bit de Busy Flag no se usa, en su lugar se mete un retardo después de enviar cada instrucción, lo próximo son las interrupciones para los botones y el ADC para manejar el potenciometro de la placa, y para eso si que hay que leer x_x.



//Funciones para escribir en un lcd.
//8 bits de bus de datos.

#include <avr/io.h>

#define F_CPU 16000000UL
#include <util/delay.h>

#define Bus_Datos PORTA
#define Bus_Control PORTD
#define RS  PORTD0                 //Todos deben de pertenecer al mismo puerto
#define RW  PORTD1
#define E   PORTD2

//Comandos para situar el cursor en inicio de líneas.
#define DDRA_LINEA1 0x80    //1000 0000
#define DDRA_LINEA2 0xC0    //1100 0000
#define DDRA_LINEA3 0x94    //1001 0100
#define DDRA_LINEA4 0xD4    //1101 0100


void inicializar();

//Funciones para escribir en el LCD
void lcd_inicializar();
void lcd_escribir_c(unsigned char caracter);
void lcd_escribir(unsigned char *cadena);
void posicionar_cursor(unsigned char x, unsigned char y);
void lcd_comando(unsigned char comando);



int main(void)
{
    inicializar();
    lcd_inicializar();


    unsigned char linea1[]="   www.jmnlab.com";  //Poner en la Flash de programa las cadenas.
    unsigned char linea2[]=" Proyecto Hexapodo";
    unsigned char linea3[]=" Prueba de LCD 4x20";

   
    lcd_escribir(linea2);         //Escribo una cadena, empieza en línea 1, carácter 1.
    posicionar_cursor(2,1);   //Línea 2, primer carácter.
    lcd_escribir(linea1);
    posicionar_cursor(3,1);
    lcd_escribir(linea3);
    posicionar_cursor(4,5);
    lcd_escribir_c('A');        //Escribir los caracteres uno a uno, comprobación de su función correspondiente.
    lcd_escribir_c('r');
    lcd_escribir_c('d');
    lcd_escribir_c('u');
    lcd_escribir_c('i');
    lcd_escribir_c('n');
    lcd_escribir_c('o');
    lcd_escribir_c(' ');
    lcd_escribir_c('M');
    lcd_escribir_c('e');
    lcd_escribir_c('g');
    lcd_escribir_c('a');

    while(1)
    {
    }
}

inicializar()              //Configurar hardware del pic.
{
   DDRA=0xFF;     //1111 1111
   PORTA=0x00;   
   DDRD=0x07;     //0000 0111
   PORTD=0x00;   
}

void lcd_inicializar() //Se mandan los comandos de configuración según el dibujo.
{
    _delay_ms(100);
                       
    lcd_comando(0x3C);    //Function Set RS0 RW0 0011NF** 0011 1100
    lcd_comando(0x3C);    //Function Set
    lcd_comando(0x0C);    //Display ON/OFF Control RS0 RW0 00001DCB 0000 1100
    lcd_comando(0x01);    //Display Clear RS0 RW0 00000001
    lcd_comando(0x06);    //Entry Mode Set RS0 RW0 000001I/DS 0000 0110
}

void lcd_escribir_c(unsigned char caracter)
{
    Bus_Control |= (1<<RS);        //RS1    Dato
    Bus_Control &= ~(1<<RW);  //RS0
    Bus_Datos = caracter;       
    Bus_Control |= (1<<E);
    _delay_us(100);
    Bus_Control &= ~(1<<E);
    _delay_ms(10);
}

void lcd_comando(unsigned char comando)
{
    Bus_Control &= ~(1<<RS);    //RS0   Comando
    Bus_Control &= ~(1<<RW);   //RW0
    Bus_Datos  = comando;           //Comando
    Bus_Control |= (1<<E);
    _delay_us(100);
    Bus_Control &= ~(1<<E);
    _delay_ms(10);
}

void lcd_escribir(unsigned char *cadena)
{
    unsigned char *inicio = cadena;
    char i=0;

    for(i=0;((inicio[i]!='\0')&&(i<20));i++)
    {
        Bus_Control |= (1<<RS);    //RS0 RW0 caracter
        Bus_Control &= ~(1<<RW);
        Bus_Datos  = inicio[i];       
        Bus_Control |= (1<<E);
        _delay_us(100);
        Bus_Control &= ~(1<<E);
        _delay_ms(3);
    }
}

void posicionar_cursor(unsigned char x, unsigned char y) //línea x 1 a 4, carácter y de 1 a 20.
{                                                                                               
    unsigned char comando=DDRA_LINEA1;
    switch(x)
    {
        case 1:
        {
            comando=DDRA_LINEA1+y-1;
        }break;
        case 2:
        {
            comando=DDRA_LINEA2+y-1;
        }break;
        case 3:
        {
            comando=DDRA_LINEA3+y-1;
        }break;
        case 4:
        {
            comando=DDRA_LINEA4+y-1;
        }break;
        default:break;
    }
        lcd_comando(comando);
}

Imagen del funcionamiento.


Para cualquier comentario (cualquier comentario sobre como se debe programar es bien recibido, ya que no tengo NPI del tema, gracias):

blog comments powered by Disqus