LCD y su inicializacion

Que tal foro tengo un problema con el manejo de un LCD, igual este funciona esta bien calado el programa y todo nomas es que a veces ocupo resetear el micro varias veces hasta que se vea bien ya de ahi nunca mas ocupo resetearlo, no se a que se deba esto igual les dejo como lo estoy inicializando para ver que error le ven o que detalle o consejo lo que sea gracias de antemano.

Código:
[CODE]
#define LCD P2           //definición al puerto a conectar el LCD

/*************** Cómo cablearlo? el puerto elegido ***************/
/*************** debe ser de 8 bits siguiendo lo   ***************/
/*************** siguiente:                        ***************/
/*************** LCD=DB7,DB6,DB5,DB4,NC,E,RS,RW    ***************/
/*************** donde DB7 es el más significativo ***************/

unsigned char temp;
unsigned char DB ;

void enable()
{
  temp=DB&0xF0;   //Filtramos el nibble mas significativo
  LCD=temp|0x04;  //Activamos el enable
  retardo_ms(3);  //Retardo para asegurar el envio del dato al LCD
  LCD=LCD&0xF0;	  //Desactivamos el enable
  retardo_ms(3);  //Retardo para asegurar el apagado del enable
  temp=DB<<4;     //Recorremos el dato 4 lugares
  temp=temp&0xF0; //Filtramos el dato ahora el nibble menos significativo
  LCD=temp|0x04;  //Activamos el enable
  retardo_ms(3);  //Retardo para asegurar el envio del dato al LCD
  LCD=LCD&0xF0;	  //Desactivamos el enable
  retardo_ms(3);  //Retardo para asegurar el apagado del enable

}
void function_set()
{
  DB=0x28;        //0x28 para modo 4 bits o 0x38 modo 8 bits
  enable();
}
void display_on_off()
{
  DB=0x0F;        // 0x0C para apagar el cursor 0x0F para encenderlo
  enable();
}
void display_clear()
{
  DB=0x01;        // Para limpiar la pantalla
  enable();							
}
void inicializacion_LCD()
{
	LCD=0;            //Inicia con un valor de cero el puerto
	retardo_ms(50);   //Aplicamos un retardo de 50mS para darle tiempo de encender al LCD
	function_set();   //Elegimos el modo a usar a 4 u 8 bits
	display_on_off(); //Elegimos el tipo de cursor y si este estara presente o no
	display_clear();  //Limpia la pantalla de cualquier ruido inicial
}
[/CODE]
 
Veo que lo estas usando en 4 bits, pero tu inicializacion creo que esta mal, fijate que 1ero tenes que dejarlo como 8bits y recien despues si hacer funcion_set, osea:

Código:
...
void dato_lcd_4bit(u8 dato)
{
	set_bits(&PORTD,lcd_rs);									//Rs=1 - modo dato
	clear_bits(&PORTD,(0x0f<<lcd_d4));							//Borro los 4 bits de datos
	set_bits(&PORTD,(((dato&0xf0)>>4)<<lcd_d4));						//Mando la parte alta del dato
	set_bits(&PORTD,lcd_enable); clear_bits(&PORTD,lcd_enable); // Flanco descendente para mandar la parte alta
	delay_us(100);												//Espero 100uS 
	clear_bits(&PORTD,(0x0f<<lcd_d4));							//Borro los 4 bits de datos
	set_bits(&PORTD,((dato&0x0f)<<lcd_d4));						//Mando la parte baja del dato
	set_bits(&PORTD,lcd_enable); clear_bits(&PORTD,lcd_enable); // Flanco descendente para mandar la parte baja
	delay_us(100);
}

void comando_lcd_4bit(u8 comando)
{
	clear_bits(&PORTD,lcd_rs);									//Rs=0 - modo comando
	clear_bits(&PORTD,(0x0f<<lcd_d4));							//Borro los 4 bits de datos
	set_bits(&PORTD,(((comando&0xf0)>>4)<<lcd_d4));					//Mando la parte alta del comando
	set_bits(&PORTD,lcd_enable); clear_bits(&PORTD,lcd_enable); // Flanco descendente para mandar la parte alta
	delay_us(100);												//Espero 100uS 
	clear_bits(&PORTD,(0x0f<<lcd_d4));							//Borro los 4 bits de datos
	set_bits(&PORTD,((comando&0x0f)<<lcd_d4));					//Mando la parte baja del comando
	set_bits(&PORTD,lcd_enable); clear_bits(&PORTD,lcd_enable); // Flanco descendente para mandar la parte baja
	delay_us(100);
}

void comando_lcd_4bit_simple(u8 comando)
{
	clear_bits(&PORTD,lcd_rs);									//Rs=0 - modo comando
	clear_bits(&PORTD,(0x0f<<lcd_d4));							//Borro los 4 bits de datos
	set_bits(&PORTD,(((comando&0xf0)>>4)<<lcd_d4));				//Mando la parte alta del comando
	set_bits(&PORTD,lcd_enable); clear_bits(&PORTD,lcd_enable); // Flanco descendente para mandar la parte alta
	delay_us(100);												//Espero 100uS 
}

void inicia_lcd_4bits()
{
	clear_bits(&PORTD,lcd_enable);	//LCD enable=0
	delay_ms(30);
	[B]comando_lcd_4bit_simple(0x30);
	delay_ms(8);
	comando_lcd_4bit_simple(0x30);
	comando_lcd_4bit_simple(0x30);
	[/B][COLOR="Red"]comando_lcd_4bit_simple(0x20);[/COLOR]	//Paso a modo 4bits
	[COLOR="Blue"]comando_lcd_4bit(0x28);[/COLOR]			//Habilito las 2 lineas
	comando_lcd_4bit(0x0C);			//Display ON, sin cursor
	comando_lcd_4bit(0x06);			//Se auto-incrementa la dirección del cursor
	comando_lcd_4bit(0x01);			//Clear display
	delay_ms(4);
}
...

Este codigo es para un Atmega8, fijate mas que nada la inicializacion.

Tanto delay, como set/clear bit son funciones declaradas en el codigo, solamente pensalas como un retardo y para setear/borrar bits en un puerto.

En negro lo que creo que te faltaria en tu codigo, y en rojo hago el cambio a 4bits y en azul lo que haria tu funcion "funcion_set".
 
Y si así fuera, que sentido tendría entonces hacerlo de 4 bits? Según yo el uso de 4 bits es para utilizar la pantalla con un solo puerto pero pues según lo explica cosmefulanito04 creo que entonces no habría diferencia alguna.
 
Saludos y gracias, pero si coincido con Earl pero creo que no tendria sentido por q como envias el modo a 8 bits con 4 bits cableados tengo entendido q cuando recibe el modo 4 bits (0x28), recibe el 2 y ya en ese instante ya funciona a cuatros bits separando los datos en nibble's no se si estoy mal igual gracias por la ayuda :)
 
Es que malinterpretaron la respuesta, si bien tenes que comenzar a usar el LCD en modo 8bits, eso no implica la necesidad de usar 8 lineas, sino 4.

Fijense en la inicializacion en modo 8 bit:



Ahora vean la inicializacion en modo 4 bit:



Ves que empieza en modo de 8 bits, pero fijate tambien en la inicializacion del modo de 8 bits, en las 1eras instrucciones el nibble de abajo no importa (esta en *), eso es porque contempla la inicializacion con solo 4 lineas fisicas, es por eso que en las 3 1eras instrucciones el nibble bajo no importa su valor.

- En Rojo tenes el LCD funcionando en modo de 8 bits (por eso hay que mandar 0x3 en DB7-DB4)
- En Azul recien empieza a funcionar en modo de 4 bits (aca ya se empieza a mandar 0x2 en DB7-DB4), y para completar un comando hay que mandar 2 nibbles.

Por ultimo te dejo la tabla de intrucciones, fijate que cuando DB5=1 estas configurando el modo del LCD que es lo que haces en los 1eros pasos.



Si te interesa, subo el pdf del controlador HD44780U que es donde saque estos graficos.

Editado:

Para que lo relaciones con el codigo que puse yo, fijate que la funcion "comando_lcd_4bit_simple(..)" esta pensada solamente para esos 3 1eros pasos, y lo que hace es solamente enviar el nibble alto, el bajo directamente no lo mando, ya que solo importa "DB7-DB4".
 
Última edición:
por que lo inicializa en 8 bits y no directamente en 4 cosmefulanito04 ? y por que manda 3 veces el mismo codigo para inicializarlo, no es necesario simplemente inicializarlo con 4 bits una sola vez y ya?, segun este pdf es como yo te digo, esto me tiene un poco confundido, alguien me explica porfavor?

PD: lo del pdf esta en la ultima pagina lo que les digo, en un diagrama de flujos
 

Adjuntos

  • LCD.PDF
    142.3 KB · Visitas: 17
Si te bajas la hojas de datos del controlador "HD44780" (ahí te lo subo) que es el integrado que se encarga de manejar los LCD típicos (hay otros controladores, pero se supone que son compatibles con el original que es el que menciono), vas a encontrar los flujos que mencione en la página 45/46 del pdf.

Ahí deja bien en claro que las cuatro primeras instrucciones deben ser tratadas como si fueran de 8bits, es decir, obviamente no es necesario que las conexiones físicas existan, pero si es necesario que el soft no divida en nibbles esas cuatro instrucciones.

A partir de la quinta instrucción, luego que la cuarta configuró el tipo de datos como 4 bits, a partir de ahí es necesario dividir el byte en nibbles.

Ejemplo de código:

PHP:
void inicializacion_LCD()
{
        ....
        comando_lcd_4bit_simple(0x30);
        delay_ms(80);
	comando_lcd_4bit_simple(0x30);
	comando_lcd_4bit_simple(0x30);
	comando_lcd_4bit_simple(0x20);	//Paso a modo 4bits
	comando_lcd_4bit(0x28);			//Habilito las 2 lineas
	comando_lcd_4bit(0x0C);			//Display ON, sin cursor
	comando_lcd_4bit(0x06);			//Se auto-incrementa la dirección del cursor
	comando_lcd_4bit(0x01);			//Clear display
        ....
}

Donde "comando_lcd_4bit_simple" solo envía el nibble alto del byte y "comando_lcd_4bit" manda primero el nibble alto y luego el bajo.
 

Adjuntos

  • HD44780U.PDF
    278.4 KB · Visitas: 10
Última edición:
Ah ok. Entonces esa rutina de mandar 4 veces el mismo código para inicializar simplemente es porque así lo hizo el fabricante, ok.
Pero ¿será esto igual para todos los LCD? Por ejemplo yo me compré uno de 2x16, (el tipico)
El problema es que no encuentro en ningún lugar de la faz de la tierra el datasheet, y lo único que dice en la parte de atras es esto TOP1602B02 VER:01 y nada mas, ni siquiera puedo ver el microcontrolador por que está totalmente cubierto con algún tipo de protector algo negro y redondo.

La cuestión es que hay un ejemplo de librería del libro PIC16F84 que solo lo utiliza 3 veces ese código (aunque en el diagrama de flujos de ese libro indique 4) al principio en la parte de LCD_Inicializa.
Mira, no lo voy a copiar todo porque está largo. (Lo puse en verde):


Código:
; ZONA DE DATOS *********************************************************************

    CBLOCK
    LCD_Dato
    LCD_GuardaDato
    LCD_GuardaTRISB
    LCD_Auxiliar1
    LCD_Auxiliar2
    ENDC

LCD_CaracteresPorLinea    EQU    .16            ; Número de caracteres por línea de la pantalla.

#DEFINE  LCD_PinRS    PORTA,0
#DEFINE  LCD_PinRW    PORTA,1
#DEFINE  LCD_PinEnable    PORTA,2
#DEFINE  LCD_BusDatos    PORTB

; Subrutina "LCD_Inicializa" ------------------------------------------------------------
;
; Inicialización del módulo LCD: Configura funciones del LCD, produce reset por software,
; borra memoria y enciende pantalla. El fabricante especifica que para garantizar la
; configuración inicial hay que hacerla como sigue:
;
LCD_Inicializa
    bsf        STATUS,RP0                ; Configura las líneas conectadas al pines RS,
    bcf        LCD_PinRS                ; R/W y E.
    bcf        LCD_PinEnable
    bcf        LCD_PinRW
    bcf        STATUS,RP0
    bcf        LCD_PinRW                ; En caso de que esté conectado le indica
                                    ; que se va a escribir en el LCD.
    bcf        LCD_PinEnable            ; Impide funcionamiento del LCD poniendo E=0.
    bcf     LCD_PinRS                ; Activa el Modo Comando poniendo RS=0.
    call    Retardo_20ms
    movlw    [COLOR=Green][B]b'00110000'[/B][/COLOR]
    call    LCD_EscribeLCD            ; Escribe el dato en el LCD.
    call    Retardo_5ms    
    movlw    [COLOR=green][B]b'00110000'[/B][/COLOR]
    call    LCD_EscribeLCD
    call    Retardo_200micros
    movlw    [COLOR=green][B]b'00110000'[/B][/COLOR]    
    call    LCD_EscribeLCD
    call    Retardo_20micros        ; Este retardo es necesario para simular en PROTEUS.
    movlw    b'00100000'                ; Interface de 4 bits.
    call    LCD_EscribeLCD
    call    Retardo_20micros        ; Este retardo es necesario para simular en PROTEUS.
    
; Ahora configura el resto de los parámetros: 

    call    LCD_2Lineas4Bits5x7        ; LCD de 2 líneas y caracteres de 5x7 puntos.
    call    LCD_Borra                ; Pantalla encendida y limpia. Cursor al principio
    call    LCD_CursorOFF            ; de la línea 1. Cursor apagado.
    call    LCD_CursorIncr            ; Cursor en modo incrementar.
    return
 
Última edición por un moderador:
El controlador ese es un standard de este tipo de LCD, es decir que esa inicialización debe funcionar.

Por ej. en los LCD que usé, la única referencia que encontré fueron sus características eléctricas, corriente de backlight, tensión del diodo de backlight, alimentación de la parte lógica, etc.

Volviendo a tu duda, fijate que tu inicialización hace exactamente lo mismo que la mía:

PHP:
movlw	b'00110000' ; 8bits
....
movlw	b'00110000' ; 8bits
....
movlw	b'00110000' ; 8bits
....
movlw	b'00100000' ; Pasa a 4 bits, pero en modo 8bits, es decir solo manda el nibble superior, no se molesta en enviar el nibble inferior.

Esas tres primeras instrucciones seguro que es de redundancia, para evitar que el LCD no tome el inicio de la inicialización.

Después por ej. los tiempos que yo usé fueron alargados a propósito para asegurarse de que el controlador tome bien c/instrucción (aumentar el margen de "seguridad").
 
a que te refieres con eso? acaso no era necesario repetir ese codigo 4 veces?, y gracias por las respuestas y atencion

Es una suposición lo que digo, es posible que sin esas 3 repeticiones del comando y mandandolo solo una vez el LCD empiece la inicialización, pero te arriesgas a que si el transitorio de tu fuente de alimentación ni bien arranca es muuuuy lento, no tome bien ese primer comando 0x30 (o b'00110000') y el LCD no inicialice bien.

En otras palabras, si el fabricante hace incampié en ese flujo, ¿para qué obviarlo? él sin duda sabe más sobre su producto que nosotros y si recomienda repetir 3 veces ese comando con esos tiempos mínimos de separación, simplemente hay que hacerlo para evitar problemas a futuro ;) .
 
Atrás
Arriba