Control de LCD desde cero y en castellano

Hola a todos. Soy nuevo en el foro y quiero colaborar con algo. Estuve casi una semana peleando con mi LCD y no podia hacer que escriba ni una sola letra. No encontraba info precisa que me sea útil. Hasta que dando vueltas por ahi encontre un tutorial de el mundo de riki o algo asi y realmente me ayudo. Aca dejo el link de la pagina, y la traduccion que hice por si alguien quiere leer.

Esta pagina ayuda mucho mucho
http://www.8051projects.net/lcd-interfacing/introduction.php

Primero pongo un programita hecho en "CCS" que escribe una sola letra en el LCD y luego otro que escribe una cadena. Estoy trabajando con un Lcd de dos lineas y 16 caracteres y en modo 8 bit. Estoy viendo como hacerlo con 4 bit, asi que pondre algo luego de que lo pueda hacer funcionar en ese modo.

Programa que muestra una letra en la pantalla, uso un pic 16f887

Código:
#include<16F887.h>
#use delay (clock=4000000)
#fuses INTRC_IO,NOWDT,NOLVP,MCLR,NOPROTECT,NOPUT
#use delay(clock=4000000)
#byte PORTD=0x08 

void LCD_busy(void){
unsigned char i,j;
         for(i=0;i<50;i++)        
            for(j=0;j<255;j++);
}
void seteo(unsigned char var)
{
     PORTD=Var;
     output_low(PIN_E1);  //LCD_rs   = 0;
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L       
     delay_ms(15);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(15);
     LCD_busy();          //Wait for LCD to process the command
}


void LCD_init(void) {
     delay_ms(15);
     delay_ms(15);
     delay_ms(15);
     seteo(0x38);         //function set: 2 Line, 8-bit, 5x7 dots
     delay_ms(15);
     seteo(0x38);         //function set: 2 Line, 8-bit, 5x7 dots
     delay_ms(15);     
     seteo(0x38);         //function set: 2 Line, 8-bit, 5x7 dots
     delay_ms(15);
     seteo(0x39);         
delay_ms(15);
     seteo(0x06);         
delay_ms(15);
     seteo(0x0E);          //Entry mode
     delay_ms(15);
     seteo(0x01);   
delay_ms(15);
     LCD_busy(); 
}
void LCD_command(unsigned char var)
{
     
     PORTD=Var;           //Function set: 2 Line, 8-bit, 5x7 dots
     output_low(PIN_E1);   //LCD_rs   = 0;        //Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L       
     delay_ms(15);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(15);
     LCD_busy();          

}
void LCD_letra(unsigned char var)
{
     PORTD=Var;
     output_high(PIN_E1);    //LCD_rs   = 1;     Selected data register
     output_high(PIN_E2);    //LCD_en   = 1;     Enable H->L       
     delay_ms(15);
     output_low(PIN_E2);     //LCD_en   = 0;
     delay_ms(15);
     LCD_busy();          
     
}

void main() {

set_tris_d(0x00);
set_tris_e(0x00);
LCD_busy();
LCD_init();
LCD_busy();
LCD_letra('R');

}


Ahora pongo el otro programita que escribe la cadena de caracteres en el LCD

Código:
#include<16F887.h>
#use delay (clock=4000000)
#fuses INTRC_IO,NOWDT,NOLVP,MCLR,NOPROTECT,NOPUT
#use delay(clock=4000000)
#byte PORTD=0x08 


void LCD_busy(void){              //Esta rutina la hago con un simple conteo porque tengo conectado el pin de R/W 
unsigned char i,j;                //a masa y por lo tanto no puedo leer del LCD ya que siempre lo tengo en modo escritura
         for(i=0;i<50;i++)        
            for(j=0;j<255;j++);
}
void LCD_seteo(unsigned char var)
{
     PORTD=Var;
     output_low(PIN_E1);  //LCD_rs   = 0;
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L       
     delay_ms(15);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(15);
     LCD_busy();          //Wait for LCD to process the command
}


void LCD_init(void) {
     delay_ms(15);
     delay_ms(15);
     delay_ms(15);
     LCD_seteo(0x38);         //function set: 2 Line, 8-bit, 5x7 dots
     delay_ms(15);
     LCD_seteo(0x38);         //function set: 2 Line, 8-bit, 5x7 dots
     delay_ms(15);     
     LCD_seteo(0x38);         //function set: 2 Line, 8-bit, 5x7 dots
     delay_ms(15);
     LCD_seteo(0x39);         
delay_ms(15);
     LCD_seteo(0x06);         //Entry mode
delay_ms(15);
     LCD_seteo(0x0E);          //Enciende el cursor
     delay_ms(15);
     LCD_seteo(0x01);          //Limpia el display y la DDRAM
delay_ms(15);
     LCD_busy(); 
}
void LCD_command(unsigned char var)
{
     PORTD=Var;           //Function set: 2 Line, 8-bit, 5x7 dots
     output_low(PIN_E1);   //LCD_rs   = 0;        //Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L       
     delay_ms(15);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(15);
     LCD_busy();          

}
void LCD_write_data(unsigned char var)
{
     PORTD=Var;
     output_high(PIN_E1);    //LCD_rs   = 1;     Selected data register
     output_high(PIN_E2);    //LCD_en   = 1;     Enable H->L       
     delay_ms(15);
     output_low(PIN_E2);     //LCD_en   = 0;
     delay_ms(15);
     LCD_busy();          
}

void LCD_cadena(unsigned char output)
{
  PORTD= output;     //Entry mode, auto increment with no shift
  output_high(PIN_E1);    //LCD_rs   = 1;     Selected data register
  output_high(PIN_E2);    //LCD_en   = 1;     Enable H->L       
  delay_ms(15);
  output_low(PIN_E2);     //LCD_en   = 0;
  delay_ms(15);
  LCD_busy();   
}

void LCD_cambia_cursor (unsigned int SC,unsigned int RL) {
  /*
  Moves the cursor and shifts the display without changing the DDRAM contents
  SC  => [Cursor : Display]
        0 => Cursor
        1 => Display
  RL  => [Left : Right]
        0 => Left
        1 => Right
  */
  PORTD = 0x00;
  RL = RL << 2;
  SC = SC << 3;
  PORTD = PORTD + 0x10 + SC + RL;
  LCD_seteo(PORTD);
}
void LCD_ddram_address (unsigned int ddadrs) {
  /*
  ddadrs => DDRAM Address Binary 7 bit
            Line 1 Column 1
            Binary Address : 0B10000000
            HEX Address     : 0X80
 
            Line 1 Column 16
            Binary Address : 0B10001111
            HEX Address     : 0X8F
 
            Line 2 Column 1
            Binary Address : 0B11000000
            HEX Address     : 0XC0
 
            Line 2 Column 16
            Binary Address : 0B11001111
            HEX Address     : 0XCF
            [][][][][][][][][][][][][][][][]
            [][][][][][][][][][][][][][][][]
                 C1    C2    C3    C4    C5    C6    C7    C8    C9    C10   C11   C12   C13   C14   C15   C16
            L1 [0X80][0X81][0X82][0X83][0X84][0X85][0X86][0X87][0X88][0X89][0X8A][0X8B][0X8C][0X8D][0X8E][0X8F]
            L2 [0XC0][0XC1][0XC2][0XC3][0XC4][0XC5][0XC6][0XC7][0XC8][0XC9][0XCA][0XCB][0XCC][0XCD][0XCE][0XCF]
  */
  PORTD = 0x00;
  PORTD = ddadrs;
  LCD_seteo(PORTD);
}

void main() {

set_tris_d(0x00);
set_tris_e(0x00);

  LCD_busy();
  LCD_init();
  LCD_busy();
  LCD_cadena("Hola");
  LCD_busy();
  LCD_ddram_address(0xC0); // funcion que se ubiac en la segunda linea del LCD
  LCD_busy();                    // para seguir escribiendo
  LCD_cadena("Mundo");

}

Tambien tengo parte de una libreria para controlar LCD en .asm por si alguine la quiere.
En los dos programas quizas hay codigo de mas, o tal vez se pueden optimizar los retardos. Como lei que algunas veces los LCD tienen problema con los tiempos de acceso, puse varios retardos, quizas algunos de mas.
Espero sea util.
En el adjunto dejo la traduccion que hice de la pagina si por ahi a alguien le sirve.
 

Adjuntos

  • LCD.rar
    119.6 KB · Visitas: 55
Última edición:
Dejo la parte que faltaba del tutorial para el moodo en 4 bits, hay que seguir estos pasos tal cual y lograran que anda el LCD:

Modo 4 Bits

En el modo de 4 bits el datos se envían en nibbles (medio octeto, cuatro bits), primero se enviar el mayor nibble y luego la parte inferior del byte, es decir el segundo nibble. Para habilitar el modo de 4 bits de la pantalla LCD, tenemos que seguir la secuencia especial de inicialización que le indica al controlador LCD que el usuario ha seleccionado el modo de 4 bits de la operación. Llamamos a esta secuencia especial como reseteando la pantalla LCD.
A continuación los pasos de reinicio de LCD:
· Esperar cerca de 20[ms].
· Enviar el primer valor de inicio (0x30).
· Esperar cerca de 10[ms].
· Enviar el segundo valor de inicio (0x30).
· Esperar cerca de 1[ms].
· Enviar el tercer valor de inicio (0x30).
· Seleccionar el modo de trabajo del bus con (0x30 para 8 bits y 0x20 para 4 bits).
· Esperar 1[ms].

En modo 4 bits se necesitan 6 pines para hacer la interfaz con el LCD. Desde D4 a D7 son los pines de datos, y la habilitación (en) y el selector de registro (rs) son los pines de control del LCD. De esta forma no se usa el PIN R/W ya que siempre se esta escribiendo en el LCD, por esto lo podemos cablear a masa.
Como ya se explico en modo de 4 bits el dato es enviado nibble a nibble, primero se envía el nibble mas alto luego el de menor orden. Esto significa que tanto en las funciones de envió de datos como de comando debemos separar los 4 bits mas altos de los 4 bits mas bajos.
Los pasos mas comunes son:
· Enmascarar los 4-bits mas bajos.
· Enviarlos al puerto LCD.
· Enviar la señal de habilitación (en).
· Enmascarar los 4-bits mas altos.
· Enviarlos al puerto LCD.
· Enviar la señal de habilitación (en).
A continuacion un programa que realiza lo explicado anteriormente, yo tengo cableada la placa ya pero lo probe y anda, si alguien le funciona por favor comente.
Código:
#include<16F887.h>
#use delay (clock=4000000)
#fuses INTRC_IO,NOWDT,NOLVP,MCLR,NOPROTECT,NOPUT
#use delay(clock=4000000)
#byte PORTD=0x08      //uso el puerto D para los datos, RE2 para la señal de habilitacion y RE1 para el rs
void LCD_busy(void){
unsigned char i,j;
         for(i=0;i<50;i++)        
            for(j=0;j<255;j++);
}
/*----------------------------------------- 
   Ejemplo de LCD en modo 4 bit
   rs => Register Select [Command : Data]
        0 => Command
        1 => Data
   rw => Cableado a masa
   en => Enable signal [High to Low]
----------------------------------------- */

void LCD_reset (void) {
     PORTD= 0XFF;
     delay_ms(20);
     PORTD = 0X30;//Data = 34H, EN = 1, RS = 0, First Init
     output_low(PIN_E1);   // rs=0 Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L
     delay_ms(1);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(10);
     PORTD = 0X30;//Data = 34H, EN = 1, RS = 0, Segundo Init
     output_low(PIN_E1);   // rs=0 Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L
     delay_ms(1);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(1);
     PORTD = 0X30;//Data = 34H, EN = 1, RS = 0, Tercero Init
     output_low(PIN_E1);   // rs=0 Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L
     delay_ms(1);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(1);
     PORTD = 0X28;        //Selecciona modo de trabajo, EN = 1, RS = 0, Select Data width (4bit)
     output_low(PIN_E1);   // rs=0 Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L 
     delay_ms(5);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(1);
}
void LCD_write_command (unsigned char var) {
     PORTD=var;     
     PORTD = var & 0XF0;     //Enmascara el nibble mas bajo (4 bits menos significativos) y envia el nibble alto
     output_low(PIN_E1);   // rs=0 Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L 
     delay_ms(5);
     output_low(PIN_E2);   //LCD_en   = 0;
     PORTD = (var << 4) & 0XF0; //corre el dato cuatro lugares para enviar la parte alta del byte, recordar que en este modo solo se conectan D7-D4
     output_low(PIN_E1);   // rs=0 Selected command register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L 
     delay_ms(5);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(1);
}
void LCD_entry_mode_set (unsigned int ID,unsigned int S) {
     /*
     ID => [Decrement : Increment]
           0 => Decrement
           1 => Increment
     S  => accompanies display shift
           0 => OFF
           1 => ON
     */
     PORTD = 0x00;
     ID = ID << 1;
     PORTD = PORTD + 0x04 + ID + S;
     LCD_write_command(PORTD);
}
void LCD_ddram_address (unsigned int ddadrs) {
     PORTD = 0x00;
     PORTD = ddadrs;
     LCD_write_command(PORTD);
}
void LCD_write_data(unsigned int output) {
     /*
     output => Data to be written
     */
     //unsigned int output;//
     PORTD = output & 0XF0;
     output_high(PIN_E1);   // rs=1 Selected data register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L 
     delay_ms(15);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(15);
     PORTD= (output << 4) & 0XF0;
     output_high(PIN_E1);   // rs=1 Selected data register
     output_high(PIN_E2);  //LCD_en   = 1;     Enable H->L 
     delay_ms(15);
     output_low(PIN_E2);   //LCD_en   = 0;
     delay_ms(1);
}
void LCD_init ()
{
     LCD_reset();              // Call LCD reset
     delay_ms(5);
     LCD_write_command(0x01);   //borra la pantalla
     delay_ms(5);
     LCD_write_command(0x0E);    //configura Display ON, curor ON
     delay_ms(5);
     LCD_entry_mode_set (1,0);//Entry mode, auto increment with no shift
     delay_ms(5);
     LCD_ddram_address(0X80);//Line 1 Column 1
 }

void main(void) {
set_tris_d(0x00);
set_tris_e(0x00);
     LCD_init();
     delay_ms(15);
     LCD_write_data("Chau"); 

}

El codigo anda, quizas hay formas mas prolijas de hacerlo, eso ya esta por cada uno, por mi lado respeto el conocido el dicho "equipo que gana...no se toca".
 
No sean flojos... hechen a andar el ensamblador... No se crean... fue broma sin mala intencion.

Aqui esta el codigo en ensamblador para los que les gusta hablar directamente con el micro.

Este codigo corresponde para los AVR.
Tambien lo tengo para operar en multihilo en el AVR y adicionalmente lo tengo para HC12, 8086 y 8051

Esta hecho para trabajar en modo de 4 bits y asi usar un solo puerto para control y datos.

Código:
LDI R18, $35
STS $D0, R18        ;LOCALIDAD DE LA DIRECCION DEL PUERTO LCD 


; envia a pantalla un par de digitos hexagesimales, R20 es el registro con el par de digitos, Z el puerto
printdig2hex:
 PUSH R16
 PUSH R28
 PUSH R29
 PUSH R30
 PUSH R31
 push R20
 push R20
 LDI R16,$00
 lsr R20
 lsr R20
 lsr R20
 lsr R20
 PUSH R30
 PUSH R31
 LDI ZL, LOW(TABLAASCCI) ;EL COMPILADOR HACE UN CORRIMENTO LOGICO A LA LEFT
 LDI ZH, HIGH(TABLAASCCI)
 CLC
 ROL R30
 ROL R31	
 ADD R30,R20
 ADC R31,R16
 LPM R20,Z	
 POP R31
 POP R30
 RCALL wdata
 pop R20
 andi R20,$0f
 PUSH R30
 PUSH R31
 LDI ZL, LOW(TABLAASCCI) ;EL COMPILADOR HACE UN CORRIMENTO LOGICO A LA LEFT
 LDI ZH, HIGH(TABLAASCCI)
 CLC
 ROL R30
 ROL R31
 ADD R30,R20
 ADC R31,R16
 LPM R20,Z	
 POP R31
 POP R30
 RCALL wdata
 pop R20
 POP R31
 POP R30
 POP R29
 POP R28
 POP R16
 ret

; envia a pantalla un par de digitos decimales, R20 es el registro con el par de digitos, Z el puerto
printdig2dec:
 push R20
 push R20
 lsr R20
 lsr R20
 lsr R20
 lsr R20
 ori R20,$30
 RCALL wdata
 pop R20
 andi R20,$0f
 ori R20,$30
 RCALL wdata
 pop R20
 ret


; posiciona el cursor en una localidad de la pantalla, R20 es el registro con la localidad en pantalla y Z el puerto donde esta el display
setposchar:
 push R20
 ORI R20,$80
 RCALL wcom
 pop R20
 ret


; inicializa la pantalla con envio de 4 bits, Z la localidad del a pantalla
pantallaset:
 push R20
 push R21
 push R22
 LDI R21, $04
 RCALL retarshort
 LDI R20,$00
 st Z,R20
 RCALL retarshort
 LDI R20,$20
 ST Z, R20
 RCALL retarshort
 ADD R20, R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 LDI R20,$80
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 LDI R20,$00
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 LDI R20,$60
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 LDI R20,$00
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 ; 0f = cursor + blink   0e = cursor  0c = no cursor
 LDI R20,$E0
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 POP R22
 POP R21
 POP R20
 ret

; borra el display y pone el cursor en la posicion 0, Z la localidad de la pantalla
borradisp:
 push R20
 push R21
 push R22

 LDI R20, $00
 LDI R21, $04
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 LDI R20, $10
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20, R21
 ST Z, R20
 RCALL retarshort
 LDI R20,$00
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20, R21
 ST Z, R20
 RCALL retarshort
 LDI R20, $20
 ST Z, R20
 RCALL retarshort
 add R20, R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 POP R22
 POP R21
 POP R20
 ret

; escribe un caracter en la pantallla, R20 el caracter, Z la localidad dela pantalla
wdata:
 push R20
 push R21
 push R22
 PUSH R20
 push R20

 LDI R21, $04
 LDI R20, $01
 ST Z, R20
 POP R20
 
 andi R20,$f0
 ORI R20, $01 
 ST Z, R20
 RCALL retarshort
 add R20, R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 pop R20         
 LSL R20
 LSL R20
 LSL R20
 LSL R20
 andI R20,$f0
 ORI R20,$01 
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 POP R22
 POP R21
 POP R20
 ret

; envia un comando a la pantalla matricial, R20 el comando, Z la localidad dela pantalla
wcom:
 push R20
 push R21
 push R22
 PUSH R20
 push R20
 
 LDI R21, $04
 LDI R20, $00
 ST Z, R20
 POP R20
 
 andi R20,$f0
 ST Z, R20
 RCALL retarshort
 add R20, R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 pop R20         
 LSL R20
 LSL R20
 LSL R20
 LSL R20
 andI R20,$f0
 ST Z, R20
 RCALL retarshort
 add R20,R21
 ST Z, R20
 RCALL retarshort
 sub R20,R21
 ST Z, R20
 RCALL retarshort
 POP R22
 POP R21
 POP R20
 ret           
           

 
retarshort:
 push R25
 LDI R25,$2F
 retb:
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  nop
  DEC R25
  BRNE retb
 pop R25
 ret        

TABLAASCCI:

	.DB "0123456789ABCDEF"
 
Atrás
Arriba