# Libreria para lcd 4x16



## Arol (May 1, 2012)

Hola gente del Foro! 
Necesito un poquito de ayuda. necesito trabajar con un lcd de 4x16, usualmente uso la libreria LCD.C pero es para displays de 2x16. hay alguna forma de modificarla para 4x16? o alguien tiene una libreria para 4x16?
desde ya, muchas gracias!


----------



## hamster (May 1, 2012)

Lo que interesa es el tipo de controlador del LCD, no sé a que libreria LCD te refieres pero probablemente sea para controlador HD44780. Asi que investiga el tipo de controlador de tu LCD y basado en eso, se buscan, modifican o hacen los drivers.


----------



## Arol (May 1, 2012)

Estoy haciendo un proyecto con un PIC18f4550 y necesito usar un LCD de 4x16 o 4x20. hasta ahora solo utilizaba LCD 2x16 y utilizaba la libreria LCD.C pero esta no sirve para un lcd de mas de 2 lineas. por eso necesito una libreria de CCS que sirva para esto


----------



## hamster (May 1, 2012)

Los LCD traen integrado un controlador. En la hoja de datos de ese controlador te indicará como se debe manejar.
Yo he encontrado LCD's de 16x2, 20x2, 4x16, 4x20 (todos con controlador HD44780), basicamente esto quiere decir que la misma libreria debería servirte para todos(revisar accesos a memoria).
Creo que la librería del CCS es para el controlador HD44780, pero en realidad no se que tanta libertad te da este en caso de necesitar modificarla.
Puede ser que el LCD que tienes, posee un controlador distinto al HD44780 y por eso no funciona.
Mira este LCD 4x16 con controlador HD4780:
http://www.masoportunidades.com.ar/...-hd44780-c-lcdproc-disponible-en-buenos-aires
y este otro con SPLC780D or ST7066U:
http://www.newhavendisplay.com/specs/NHD-0416BZ-FL-YBW.pdf


----------



## Arol (May 1, 2012)

Gracias Hamster por tu ayuda, pero ya encontré lo que necesitaba por si le sirve a alguien mas lo pongo aquí. es una librería para manejar LCD de 4x16 o 4x20.


```
// Flex_LCD420.c 

// These pins are for my Microchip PicDem2-Plus board, 
// which I used to test this driver. 
// An external 20x4 LCD is connected to these pins. 
// Change these pins to match your own board's connections. 

#define LCD_DB4   PIN_D4 
#define LCD_DB5   PIN_D5 
#define LCD_DB6   PIN_D6 
#define LCD_DB7   PIN_D7 

#define LCD_RS    PIN_E0 
#define LCD_RW    PIN_E1 
#define LCD_E     PIN_E2 

/* 
// To prove that the driver can be used with random 
// pins, I also tested it with these pins: 
#define LCD_DB4   PIN_D4 
#define LCD_DB5   PIN_B1 
#define LCD_DB6   PIN_C5 
#define LCD_DB7   PIN_B5 

#define LCD_RS    PIN_E2 
#define LCD_RW    PIN_B2 
#define LCD_E     PIN_D6 
*/ 

// If you want only a 6-pin interface to your LCD, then 
// connect the R/W pin on the LCD to ground, and comment 
// out the following line.  Doing so will save one PIC 
// pin, but at the cost of losing the ability to read from 
// the LCD.  It also makes the write time a little longer 
// because a static delay must be used, instead of polling 
// the LCD's busy bit.  Normally a 6-pin interface is only 
// used if you are running out of PIC pins, and you need 
// to use as few as possible for the LCD. 
#define USE_RW_PIN   1      


// These are the line addresses for most 4x20 LCDs. 
#define LCD_LINE_1_ADDRESS 0x00 
#define LCD_LINE_2_ADDRESS 0x40 
#define LCD_LINE_3_ADDRESS 0x14 
#define LCD_LINE_4_ADDRESS 0x54 

// These are the line addresses for LCD's which use 
// the Hitachi HD66712U controller chip. 
/* 
#define LCD_LINE_1_ADDRESS 0x00 
#define LCD_LINE_2_ADDRESS 0x20 
#define LCD_LINE_3_ADDRESS 0x40 
#define LCD_LINE_4_ADDRESS 0x60 
*/ 


//======================================== 

#define lcd_type 2   // 0=5x7, 1=5x10, 2=2 lines(or more) 

int8 lcd_line; 

int8 const LCD_INIT_STRING[4] = 
{ 
 0x20 | (lcd_type << 2),  // Set mode: 4-bit, 2+ lines, 5x8 dots 
 0xc,                     // Display on 
 1,                       // Clear display 
 6                        // Increment cursor 
 }; 
                              

//------------------------------------- 
void lcd_send_nibble(int8 nibble) 
{ 
// Note:  !! converts an integer expression 
// to a boolean (1 or 0). 
 output_bit(LCD_DB4, !!(nibble & 1)); 
 output_bit(LCD_DB5, !!(nibble & 2));  
 output_bit(LCD_DB6, !!(nibble & 4));    
 output_bit(LCD_DB7, !!(nibble & 8));    

 delay_cycles(1); 
 output_high(LCD_E); 
 delay_us(2); 
 output_low(LCD_E); 
} 

//----------------------------------- 
// This sub-routine is only called by lcd_read_byte(). 
// It's not a stand-alone routine.  For example, the 
// R/W signal is set high by lcd_read_byte() before 
// this routine is called.      

#ifdef USE_RW_PIN 
int8 lcd_read_nibble(void) 
{ 
int8 retval; 
// Create bit variables so that we can easily set 
// individual bits in the retval variable. 
#bit retval_0 = retval.0 
#bit retval_1 = retval.1 
#bit retval_2 = retval.2 
#bit retval_3 = retval.3 

retval = 0; 
    
output_high(LCD_E); 
delay_us(1); 

retval_0 = input(LCD_DB4); 
retval_1 = input(LCD_DB5); 
retval_2 = input(LCD_DB6); 
retval_3 = input(LCD_DB7); 
  
output_low(LCD_E); 
delay_us(1); 
    
return(retval);    
}    
#endif 

//--------------------------------------- 
// Read a byte from the LCD and return it. 

#ifdef USE_RW_PIN 
int8 lcd_read_byte(void) 
{ 
int8 low; 
int8 high; 

output_high(LCD_RW); 
delay_cycles(1); 

high = lcd_read_nibble(); 

low = lcd_read_nibble(); 

return( (high<<4) | low); 
} 
#endif 

//---------------------------------------- 
// Send a byte to the LCD. 
void lcd_send_byte(int8 address, int8 n) 
{ 
output_low(LCD_RS); 

#ifdef USE_RW_PIN 
while(bit_test(lcd_read_byte(),7)) ; 
#else 
delay_us(60);  
#endif 

if(address) 
   output_high(LCD_RS); 
else 
   output_low(LCD_RS); 
      
 delay_cycles(1); 

#ifdef USE_RW_PIN 
output_low(LCD_RW); 
delay_cycles(1); 
#endif 

output_low(LCD_E); 

lcd_send_nibble(n >> 4); 
lcd_send_nibble(n & 0xf); 
} 
//---------------------------- 

void lcd_init(void) 
{ 
int8 i; 

lcd_line = 1; 

output_low(LCD_RS); 

#ifdef USE_RW_PIN 
output_low(LCD_RW); 
#endif 

output_low(LCD_E); 

// Some LCDs require 15 ms minimum delay after 
// power-up.  Others require 30 ms.  I'm going 
// to set it to 35 ms, so it should work with 
// all of them. 
delay_ms(35);          

for(i=0 ;i < 3; i++) 
   { 
    lcd_send_nibble(0x03); 
    delay_ms(5); 
   } 

lcd_send_nibble(0x02); 

for(i=0; i < sizeof(LCD_INIT_STRING); i++) 
   { 
    lcd_send_byte(0, LCD_INIT_STRING[i]); 
    
    // If the R/W signal is not used, then 
    // the busy bit can't be polled.  One of 
    // the init commands takes longer than 
    // the hard-coded delay of 50 us, so in 
    // that case, lets just do a 5 ms delay 
    // after all four of them. 
    #ifndef USE_RW_PIN 
    delay_ms(5); 
    #endif 
   } 

} 

//---------------------------- 

void lcd_gotoxy(int8 x, int8 y) 
{ 
int8 address; 


switch(y) 
  { 
   case 1: 
     address = LCD_LINE_1_ADDRESS; 
     break; 

   case 2: 
     address = LCD_LINE_2_ADDRESS; 
     break; 

   case 3: 
     address = LCD_LINE_3_ADDRESS; 
     break; 

   case 4: 
     address = LCD_LINE_4_ADDRESS; 
     break; 

   default: 
     address = LCD_LINE_1_ADDRESS; 
     break; 
      
  } 

address += x-1; 
lcd_send_byte(0, 0x80 | address); 
} 

//----------------------------- 
void lcd_putc(char c) 
{ 
 switch(c) 
   { 
    case '\f': 
      lcd_send_byte(0,1); 
      lcd_line = 1; 
      delay_ms(2); 
      break; 
    
    case '\n': 
       lcd_gotoxy(1, ++lcd_line); 
       break; 
    
    case '\b': 
       lcd_send_byte(0,0x10); 
       break; 
    
    default: 
       lcd_send_byte(1,c); 
       break; 
   } 
} 

//------------------------------ 
#ifdef USE_RW_PIN 
char lcd_getc(int8 x, int8 y) 
{ 
char value; 

lcd_gotoxy(x,y); 

// Wait until busy flag is low. 
while(bit_test(lcd_read_byte(),7));  

output_high(LCD_RS); 
value = lcd_read_byte(); 
output_low(LCD_RS); 

return(value); 
} 
#endif
```


----------



## hamster (May 2, 2012)

muy bien Arol!!!
Aqui dejo una simulación en proteus con un AVR y AVR studio 4 para controlar un LCD 4x20 con controlador HD44780. solo hay que tener en cuenta en donde inicia cada fila(Dirección):
0x00, 0xC0, 0x14, 04
Creo que para simularlo con proteus , necesitan una libreria de componentes, que la verdad, no se de donde la descargué.

```
/***********************
Librerias XLCD de microchip en microcontroladores AVR
***********************/
#include <avr/io.h>
#include "Lcd.h"
#define  F_CPU 1000000UL 
#include "delays.h"

/***********************
Los ciclos de reloj estan definidos en Lcd.h
***********************/
char q[] ="129";
int main(void)
{
OpenXLCD( FOUR_BIT & LINES_5X7);
BusyXLCD();
WriteCmdXLCD(0x0C); //para parpadeo
gotoxyXLCD(1,1);
putrsXLCD("ATmel AVR");

gotoxyXLCD(1,2);
putrsXLCD("AvR studio 4");

gotoxyXLCD(1,3);
putrsXLCD("LCD HD44780");

gotoxyXLCD(10,4);
putrsXLCD("4x20");
while(1){}
return 0;
}
```


----------



## estebanfolcher (Ene 15, 2014)

buenas.

puede que esta sea una pregunta un poco tonta pero compre un lcd de 16x4
y no comprendo como seleccionar las líneas 3 y 4. 
os dejo la hoja de dato a ver si me pueden aclarar  un poco.
mi idea es modificar la librería que tengo para uno de 16x2


----------



## ByAxel (Ene 15, 2014)

estebanfolcher dijo:


> no comprendo como seleccionar las líneas 3 y 4.
> os dejo la hoja de dato a ver si me pueden aclarar  un poco.
> mi idea es modificar la librería que tengo para uno de 16x2


Hola. En princípio la memoria del LCD no está dividida (es una sola) pero para mostrar los datos se separan en 4 segmentos de los cuales los 16 primeros de cada segmento están visibles; generalmente cada segmento tiene 32 bytes pero tambien hay de 64 Bytes.

Puedes usar la función de mover el cursor a una posición determinada como LCD_GotoXY() mencionado más arriba en el mismo post para indicar a que punto o dirección en memoria quieres mover el cursor.

Línea 1, el cursor apunta a la dirección 0x00.
Línea 2, el cursor apunta a la dirección 0x20 si es de 32 bytes o 0x40 si es de 64bytes.

Así para el resto y revisa todo el post que hay códigos de ejemplo.
Saludos.


----------



## estebanfolcher (Ene 15, 2014)

¿Para entenderlo me dices que literalmente el lcd de 16x4  en realidad es un lcd 32x2 y el problema lo resuelvo poniendo tantos espacios hasta saltar a la línea que sigue?

Esta es la que utilizo yo, verás que hay dos subrutinas que te ubican en la línea 1 o 2 

call LCD_PosiciónLinea1
call LCD_posicionLinea2
estas se colocan  al principio de cada línea la 

solución seria para el de 4 líneas 

LCD_PosicionLinea1     
            iorlw    b'10000000'        
            goto    LCD_EnviaComando

LCD_PosicionLinea2      
            movlw    .17 
            call         LCD_PosicionLinea1      

LCD_PosicionLinea3
            iorlw    b'11000000'        
            goto    LCD_EnviaComando

LCD_PosicionLinea4
         movlw     .33
         call          LCD_PosicionLinea3    


```
;**************************** Librería "LCD.INC" ***********************************

; Estas subrutinas permiten realizar las tareas básicas de control de un módulo LCD de 2
; líneas por 16 caracteres, compatible con el modelo LM016L.
;
; El visualizador LCD está conectado al Puerto B del PIC mediante un bus de 4 bits. Las
; conexiones son:
;    -    Las 4 líneas superiores del módulo LCD, pines <DB7:DB4>  se conectan a las 4
;    líneas superiores del Puerto B del PIC, pines <RB7:RB4>.
;    -    Pin RS del LCD a la línea RA0 del PIC.
;    -    Pin R/W del LCD a la línea RA1 del PIC, o a masa.
;    -    Pin Enable del LCD a la línea RA2 del PIC.
;
; Se utilizan llamadas a subrutinas de retardo de tiempo localizadas en la librería RETARDOS.INC.
;
; 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    b'00110000'    
    call    LCD_EscribeLCD        ; Escribe el dato en el LCD.
    call    Retardo_5ms    
    movlw    b'00110000'    
    call    LCD_EscribeLCD
    call    Retardo_200micros
    movlw    b'00110000'    
    call    LCD_EscribeLCD
    movlw    b'00100000'        ; Interface de 4 bits.
    call    LCD_EscribeLCD

; 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

; Subrutina "LCD_EscribeLCD" -----------------------------------------------------------
;
; Envía el dato del registro de trabajo W al bus de dato y produce un pequeño pulso en el pin
; Enable del LCD. Para no alterar el contenido de las líneas de la parte baja del Puerto B que
; no son utilizadas para el LCD (pines RB3:RB0), primero se lee estas líneas y después se
; vuelve a enviar este dato sin cambiarlo.

LCD_EscribeLCD
    andlw    b'11110000'        ; Se queda con el nibble alto del dato que es el
    movwf    LCD_Dato        ; que hay que enviar y lo guarda.
    movf    LCD_BusDatos,W        ; Lee la información actual de la parte baja
    andlw    b'00001111'        ; del Puerto B, que no se debe alterar.
    iorwf    LCD_Dato,F        ; Enviará la parte alta del dato de entrada
                    ; y en la parte baja lo que había antes.
    bsf    STATUS,RP0        ; Acceso al Banco 1.
    movf    PORTB,W        ; Guarda la configuración que tenía antes TRISB.
    movwf    LCD_GuardaTRISB
    movlw    b'00001111'        ; Las 4 líneas inferiores del Puerto B se dejan 
    andwf    PORTB,F            ; como estaban y las 4 superiores como salida.
    bcf    STATUS,RP0        ; Acceso al Banco 0.
;
    movf    LCD_Dato,W        ; Recupera el dato a enviar.
    movwf    LCD_BusDatos        ; Envía el dato al módulo LCD.
    bsf    LCD_PinEnable        ; Permite funcionamiento del LCD mediante un pequeño
    bcf    LCD_PinEnable        ; pulso y termina impidiendo el funcionamiento del LCD.
    bsf    STATUS,RP0        ; Acceso al Banco 1. Restaura el antiguo valor en
    movf    LCD_GuardaTRISB,W    ; la configuración del Puerto B.
    movwf    PORTB            ; Realmente es TRISB.
    bcf    STATUS,RP0        ; Acceso al Banco 0.
    return

; Subrutinas variadas para el control del módulo LCD -----------------------------------------
;
;Los comandos que pueden ser ejecutados son:
;
LCD_CursorIncr                ; Cursor en modo incrementar.
    movlw    b'00000110'
    goto    LCD_EnviaComando
LCD_Linea1                ; Cursor al principio de la Línea 1.
    movlw    b'10000000'        ; Dirección 00h de la DDRAM
    goto    LCD_EnviaComando
LCD_Linea2                ; Cursor al principio de la Línea 2.
    movlw    b'11000000'        ; Dirección 40h de la DDRAM
    goto    LCD_EnviaComando
LCD_PosicionLinea1            ; Cursor a posición de la Línea 1, a partir de la
    iorlw    b'10000000'        ; dirección 00h de la DDRAM más el valor del
    goto    LCD_EnviaComando    ; registro W.
LCD_PosicionLinea2            ; Cursor a posición de la Línea 2, a partir de la
    iorlw    b'11000000'        ; dirección 40h de la DDRAM más el valor del
    goto    LCD_EnviaComando    ; registro W.
LCD_OFF                ; Pantalla apagada.
    movlw    b'00001000'
    goto    LCD_EnviaComando
LCD_CursorON                ; Pantalla encendida y cursor encendido.
    movlw    b'00001110'
    goto    LCD_EnviaComando
LCD_CursorOFF                ; Pantalla encendida y cursor apagado.
    movlw    b'00001100'
    goto    LCD_EnviaComando
LCD_Borra                ; Borra toda la pantalla, memoria DDRAM y pone el 
    movlw    b'00000001'        ; cursor a principio de la línea 1.
    goto    LCD_EnviaComando
LCD_2Lineas4Bits5x7            ; Define la pantalla de 2 líneas, con caracteres
    movlw    b'00101000'        ; de 5x7 puntos y conexión al PIC mediante bus de
;    goto    LCD_EnviaComando    ; 4 bits. 

; Subrutinas "LCD_EnviaComando" y "LCD_Caracter" ------------------------------------
;
; "LCD_EnviaComando". Escribe un comando en el registro del módulo LCD. La palabra de
; comando ha sido entregada a través del registro W.  Trabaja en Modo Comando.
; "LCD_Caracter". Escribe en la memoria DDRAM del LCD el carácter ASCII introducido a
; a través del registro W. Trabaja en Modo Dato.
;
LCD_EnviaComando
    bcf    LCD_PinRS        ; Activa el Modo Comando, poniendo RS=0.
    goto    LCD_Envia
LCD_Caracter
    bsf    LCD_PinRS        ; Activa el "Modo Dato", poniendo RS=1.
    call    LCD_CodigoCGROM    ; Obtiene el código para correcta visualización.
LCD_Envia
    movwf    LCD_GuardaDato        ; Guarda el dato a enviar.
    call    LCD_EscribeLCD        ; Primero envía el nibble alto.
    swapf    LCD_GuardaDato,W    ; Ahora envía el nibble bajo. Para ello pasa el
                    ; nibble bajo del dato a enviar a parte alta del byte.
    call    LCD_EscribeLCD        ; Se envía al visualizador LCD.
    btfss    LCD_PinRS        ; Debe garantizar una correcta escritura manteniendo 
    call    Retardo_2ms        ; 2 ms en modo comando y 50 µs en modo cáracter.
    call    Retardo_50micros
    return    

; Subrutina "LCD_CodigoCGROM" -----------------------------------------------------------
;
; A partir del carácter ASCII número 127 los códigos de los caracteres definidos en la
; tabla CGROM del LM016L no coinciden con los códigos ASCII. Así por ejemplo, el código
; ASCII de la "Ñ" en la tabla CGRAM del LM016L es EEh.
;
; Esta subrutina convierte los códigos ASCII de la "Ñ", "º" y otros, a códigos CGROM para que
; que puedan ser visualizado en el módulo LM016L.
; 
; Entrada:    En (W) el código ASCII del carácter que se desea visualizar.
; Salida:    En (W) el código definido en la tabla CGROM.

LCD_CodigoCGROM
    movwf    LCD_Dato        ; Guarda el valor del carácter y comprueba si es
LCD_EnheMinuscula            ; un carácter especial.
    sublw    'ñ'             ; ¿Es la "ñ"?
    btfss    STATUS,Z
    goto    LCD_EnheMayuscula    ; No es "ñ".
    movlw    b'11101110'        ; Código CGROM de la "ñ".
    movwf    LCD_Dato
    goto    LCD_FinCGROM
LCD_EnheMayuscula
    movf    LCD_Dato,W        ; Recupera el código ASCII de entrada.
    sublw    'Ñ'             ; ¿Es la "Ñ"?
    btfss    STATUS,Z
    goto    LCD_Grado        ; No es "Ñ".
    movlw    b'11101110'        ; Código CGROM de la "ñ". (No hay símbolo para
    movwf    LCD_Dato        ; la "Ñ" mayúscula en la CGROM).
    goto    LCD_FinCGROM    
LCD_Grado
    movf    LCD_Dato,W        ; Recupera el código ASCII de entrada.
    sublw    'º'             ; ¿Es el símbolo "º"?
    btfss    STATUS,Z
    goto    LCD_FinCGROM        ; No es "º".
    movlw    b'11011111'        ; Código CGROM del símbolo "º".
    movwf    LCD_Dato
LCD_FinCGROM
    movf    LCD_Dato,W        ; En (W) el código buscado.
    return

; Subrutina "LCD_DosEspaciosBlancos" y "LCD_LineaBlanco" --------------------------------
;
; Visualiza espacios en blanco.

LCD_LineaEnBlanco
    movlw    LCD_CaracteresPorLinea
    goto    LCD_EnviaBlancos
LCD_UnEspacioBlanco
    movlw    .1
    goto    LCD_EnviaBlancos
LCD_DosEspaciosBlancos
    movlw    .2
    goto    LCD_EnviaBlancos
LCD_TresEspaciosBlancos
    movlw    .3
LCD_EnviaBlancos
    movwf    LCD_Auxiliar1        ; (LCD_Auxiliar1) se utiliza como contador.
LCD_EnviaOtroBlanco    
    movlw    ' '            ; Esto es un espacio en blanco.
    call    LCD_Caracter        ; Visualiza tanto espacios en blanco como se
    decfsz    LCD_Auxiliar1,F        ; haya cargado en (LCD_Auxiliar1).
    goto    LCD_EnviaOtroBlanco
    return

; Subrutinas "LCD_ByteCompleto" y "LCD_Byte" --------------------------------------------
;
; Subrutina "LCD_ByteCompleto", visualiza el byte que almacena el registro W en el
; lugar actual de la pantalla. Por ejemplo, si (W)=b'10101110' visualiza "AE".
;
; Subrutina "LCD_Byte" igual que la anterior, pero en caso de que el nibble alto sea cero 
; visualiza en su lugar un espacio en blanco. Por ejemplo si (W)=b'10101110' visualiza "AE"
; y si (W)=b'00001110', visualiza " E" (un espacio blanco delante).
;
; Utilizan la subrutina "LCD_Nibble" que se analiza más adelante.
;
LCD_Byte
    movwf    LCD_Auxiliar2        ; Guarda el valor de entrada.
    andlw    b'11110000'        ; Analiza si el nibble alto es cero.
    btfss    STATUS,Z        ; Si es cero lo apaga.
    goto    LCD_VisualizaAlto        ; No es cero y lo visualiza.
    movlw    ' '            ; Visualiza un espacio en blanco.
    call    LCD_Caracter
    goto    LCD_VisualizaBajo

LCD_ByteCompleto
    movwf    LCD_Auxiliar2        ; Guarda el valor de entrada.
LCD_VisualizaAlto
    swapf    LCD_Auxiliar2,W        ; Pone el nibble alto en la parte baja.
    call    LCD_Nibble        ; Lo visualiza.
LCD_VisualizaBajo
    movf    LCD_Auxiliar2,W        ; Repite el proceso con el nibble bajo.
;    call    LCD_Nibble        ; Lo visualiza.
;    return

; Subrutina "LCD_Nibble" ----------------------------------------------------------------
;
; Visualiza en el lugar actual de la pantalla, el valor hexadecimal que almacena en el nibble
; bajo del registro W. El nibble alto de W no es tenido en cuenta. Ejemplos:
; - Si (W)=b'01010110', se visualizará "6". 
; - Si (W)=b'10101110', se visualizará "E". 
;
LCD_Nibble
    andlw    b'00001111'        ; Se queda con la parte baja.
    movwf    LCD_Auxiliar1        ; Lo guarda.
    sublw    0x09            ; Comprueba si hay que representarlo con letra.
    btfss    STATUS,C    
    goto    LCD_EnviaByteLetra
    movf    LCD_Auxiliar1,W
    addlw    '0'            ; El número se pasa a carácter ASCII sumándole
    goto     LCD_FinVisualizaDigito    ; el ASCII del cero y lo visualiza.
LCD_EnviaByteLetra
    movf    LCD_Auxiliar1,W
    addlw    'A'-0x0A            ; Sí, por tanto, se le suma el ASCII de la 'A'.
LCD_FinVisualizaDigito
    goto    LCD_Caracter        ; Y visualiza el carácter. Se hace con un "goto"
                    ; para no sobrecargar la pila.
```
PD: Gracias por la respuesta tan rápida.
He leído pero los ejemplos están en leguaje que no sé usar,  yo aprendí en assembler y no me basta resolverlo con la información recibida,  también quiero entenderlo.


----------



## ByAxel (Ene 15, 2014)

estebanfolcher dijo:


> para entenderlo me dices que literalmente el lcd de 16x4  en realidad es un lcd 32x2 y el problema lo resuelvo poniendo tantos espacios asta saltar a la línea que sigue ???


Sin ir tan a fondo puedes revisar el controlador compatible LCD HD44780, y vas a entender mejor como está organizado la memoria DDRAM.

Generalmente pero puede variar:
1ra Línea se muestra 16 posiciones de memoria (visor) pero internamente se extiende a más.
2da Línea se muestra 16 posiciones de memoria (visor) pero internamente inicia al final de la 1ra línea y se extiene a más de 16. Ocurre igual para el resto de líneas.



estebanfolcher dijo:


> PD: gracias por la respuesta tan rápida. e leído pero los ejemplos están en leguaje que no se usar  yo aprendí en assembler


Tu librería en ASM debe de tener una sub-rutina que manipule el cursor o mejor dicho, debe de tener un método para pasar de la primera línea a la segunda, es cosa de manipular dicha rutina.
En el foro hay ejemplos en ASM o también puedes subir la librería para hecharle un ojo.


----------



## estebanfolcher (Ene 15, 2014)

edite el ultimo mensaje echale un vistaso a la librería  y ariba puse lo que seria la modificación y gracias

perdón si el texto en la librería no esta bien ordenado



a ver si a si se ve mejor 




```
;**************************** Librería "LCD_4BIT.INC" ***********************************
;

; Estas subrutinas permiten realizar las tareas básicas de control de un módulo LCD de 2
; líneas por 16 caracteres, compatible con el modelo LM016L.
;
; El visualizador LCD está conectado al Puerto B del PIC mediante un bus de 4 bits. Las
; conexiones son:
;    -	Las 4 líneas superiores del módulo LCD, pines <DB7:DB4>  se conectan a las 4
;	líneas superiores del Puerto B del PIC, pines <RB7:RB4>.
;    -	Pin RS del LCD a la línea RA0 del PIC.
;    -	Pin R/W del LCD a la línea RA1 del PIC, o a masa.
;    -	Pin Enable del LCD a la línea RA2 del PIC.
;
; Se utilizan llamadas a subrutinas de retardo de tiempo localizadas en la librería RETARDOS.INC.
;
; 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	b'00110000'	
	call	LCD_EscribeLCD		; Escribe el dato en el LCD.
	call	Retardo_5ms	
	movlw	b'00110000'	
	call	LCD_EscribeLCD
	call	Retardo_200micros
	movlw	b'00110000'	
	call	LCD_EscribeLCD
	movlw	b'00100000'		; Interface de 4 bits.
	call	LCD_EscribeLCD

; 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

; Subrutina "LCD_EscribeLCD" -----------------------------------------------------------
;
; Envía el dato del registro de trabajo W al bus de dato y produce un pequeño pulso en el pin
; Enable del LCD. Para no alterar el contenido de las líneas de la parte baja del Puerto B que
; no son utilizadas para el LCD (pines RB3:RB0), primero se lee estas líneas y después se
; vuelve a enviar este dato sin cambiarlo.

LCD_EscribeLCD
	andlw	b'11110000'		; Se queda con el nibble alto del dato que es el
	movwf	LCD_Dato		; que hay que enviar y lo guarda.
	movf	LCD_BusDatos,W		; Lee la información actual de la parte baja
	andlw	b'00001111'		; del Puerto B, que no se debe alterar.
	iorwf	LCD_Dato,F		; Enviará la parte alta del dato de entrada
					; y en la parte baja lo que había antes.
	bsf	STATUS,RP0		; Acceso al Banco 1.
	movf	PORTB,W		; Guarda la configuración que tenía antes TRISB.
	movwf	LCD_GuardaTRISB
	movlw	b'00001111'		; Las 4 líneas inferiores del Puerto B se dejan 
	andwf	PORTB,F			; como estaban y las 4 superiores como salida.
	bcf	STATUS,RP0		; Acceso al Banco 0.
;
	movf	LCD_Dato,W		; Recupera el dato a enviar.
	movwf	LCD_BusDatos		; Envía el dato al módulo LCD.
	bsf	LCD_PinEnable		; Permite funcionamiento del LCD mediante un pequeño
	bcf	LCD_PinEnable		; pulso y termina impidiendo el funcionamiento del LCD.
	bsf	STATUS,RP0		; Acceso al Banco 1. Restaura el antiguo valor en
	movf	LCD_GuardaTRISB,W	; la configuración del Puerto B.
	movwf	PORTB			; Realmente es TRISB.
	bcf	STATUS,RP0		; Acceso al Banco 0.
	return

; Subrutinas variadas para el control del módulo LCD -----------------------------------------
;
;Los comandos que pueden ser ejecutados son:
;
LCD_CursorIncr				; Cursor en modo incrementar.
	movlw	b'00000110'
	goto	LCD_EnviaComando
LCD_Linea1				; Cursor al principio de la Línea 1.
	movlw	b'10000000'		; Dirección 00h de la DDRAM
	goto	LCD_EnviaComando
LCD_Linea2				; Cursor al principio de la Línea 2.
	movlw	b'11000000'		; Dirección 40h de la DDRAM
	goto	LCD_EnviaComando
;================================================================
LCD_PosicionLinea1			; Cursor a posición de la Línea 1, a partir de la
	iorlw	b'10000000'		; dirección 00h de la DDRAM más el valor del
	goto	LCD_EnviaComando	; registro W.
LCD_PosicionLinea2			; Cursor a posición de la Línea 2, a partir de la
	iorlw	b'11000000'		; dirección 40h de la DDRAM más el valor del
	goto	LCD_EnviaComando	; registro W.
;=================================================================

; Mi solución 

LCD_PosicionLinea1 
       iorlw b'10000000' 
       goto LCD_EnviaComando

LCD_PosicionLinea2 
       movlw .17 
       call LCD_PosicionLinea1 

LCD_PosicionLinea3
       iorlw b'11000000' 
       goto LCD_EnviaComando

LCD_PosicionLinea4
       movlw .33
       call LCD_PosicionLinea3 
;================================================================
LCD_OFF				; Pantalla apagada.
	movlw	b'00001000'
	goto	LCD_EnviaComando
LCD_CursorON				; Pantalla encendida y cursor encendido.
	movlw	b'00001110'
	goto	LCD_EnviaComando
LCD_CursorOFF				; Pantalla encendida y cursor apagado.
	movlw	b'00001100'
	goto	LCD_EnviaComando
LCD_Borra				; Borra toda la pantalla, memoria DDRAM y pone el 
	movlw	b'00000001'		; cursor a principio de la línea 1.
	goto	LCD_EnviaComando
LCD_2Lineas4Bits5x7			; Define la pantalla de 2 líneas, con caracteres
	movlw	b'00101000'		; de 5x7 puntos y conexión al PIC mediante bus de
;	goto	LCD_EnviaComando	; 4 bits. 

; Subrutinas "LCD_EnviaComando" y "LCD_Caracter" ------------------------------------
;
; "LCD_EnviaComando". Escribe un comando en el registro del módulo LCD. La palabra de
; comando ha sido entregada a través del registro W.  Trabaja en Modo Comando.
; "LCD_Caracter". Escribe en la memoria DDRAM del LCD el carácter ASCII introducido a
; a través del registro W. Trabaja en Modo Dato.
;
LCD_EnviaComando
	bcf	LCD_PinRS		; Activa el Modo Comando, poniendo RS=0.
	goto	LCD_Envia
LCD_Caracter
	bsf	LCD_PinRS		; Activa el "Modo Dato", poniendo RS=1.
	call	LCD_CodigoCGROM	; Obtiene el código para correcta visualización.
LCD_Envia
	movwf	LCD_GuardaDato		; Guarda el dato a enviar.
	call	LCD_EscribeLCD		; Primero envía el nibble alto.
	swapf	LCD_GuardaDato,W	; Ahora envía el nibble bajo. Para ello pasa el
					; nibble bajo del dato a enviar a parte alta del byte.
	call	LCD_EscribeLCD		; Se envía al visualizador LCD.
	btfss	LCD_PinRS		; Debe garantizar una correcta escritura manteniendo 
	call	Retardo_2ms		; 2 ms en modo comando y 50 µs en modo cáracter.
	call	Retardo_50micros
	return	

; Subrutina "LCD_CodigoCGROM" -----------------------------------------------------------
;
; A partir del carácter ASCII número 127 los códigos de los caracteres definidos en la
; tabla CGROM del LM016L no coinciden con los códigos ASCII. Así por ejemplo, el código
; ASCII de la "Ñ" en la tabla CGRAM del LM016L es EEh.
;
; Esta subrutina convierte los códigos ASCII de la "Ñ", "º" y otros, a códigos CGROM para que
; que puedan ser visualizado en el módulo LM016L.
; 
; Entrada:	En (W) el código ASCII del carácter que se desea visualizar.
; Salida:	En (W) el código definido en la tabla CGROM.

LCD_CodigoCGROM
	movwf	LCD_Dato		; Guarda el valor del carácter y comprueba si es
LCD_EnheMinuscula			; un carácter especial.
	sublw	'ñ' 			; ¿Es la "ñ"?
	btfss	STATUS,Z
	goto	LCD_EnheMayuscula	; No es "ñ".
	movlw	b'11101110'		; Código CGROM de la "ñ".
	movwf	LCD_Dato
	goto	LCD_FinCGROM
LCD_EnheMayuscula
	movf	LCD_Dato,W		; Recupera el código ASCII de entrada.
	sublw	'Ñ' 			; ¿Es la "Ñ"?
	btfss	STATUS,Z
	goto	LCD_Grado		; No es "Ñ".
	movlw	b'11101110'		; Código CGROM de la "ñ". (No hay símbolo para
	movwf	LCD_Dato		; la "Ñ" mayúscula en la CGROM).
	goto	LCD_FinCGROM	
LCD_Grado
	movf	LCD_Dato,W		; Recupera el código ASCII de entrada.
	sublw	'º' 			; ¿Es el símbolo "º"?
	btfss	STATUS,Z
	goto	LCD_FinCGROM		; No es "º".
	movlw	b'11011111'		; Código CGROM del símbolo "º".
	movwf	LCD_Dato
LCD_FinCGROM
	movf	LCD_Dato,W		; En (W) el código buscado.
	return

; Subrutina "LCD_DosEspaciosBlancos" y "LCD_LineaBlanco" --------------------------------
;
; Visualiza espacios en blanco.

LCD_LineaEnBlanco
	movlw	LCD_CaracteresPorLinea
	goto	LCD_EnviaBlancos
LCD_UnEspacioBlanco
	movlw	.1
	goto	LCD_EnviaBlancos
LCD_DosEspaciosBlancos
	movlw	.2
	goto	LCD_EnviaBlancos
LCD_TresEspaciosBlancos
	movlw	.3
LCD_EnviaBlancos
	movwf	LCD_Auxiliar1		; (LCD_Auxiliar1) se utiliza como contador.
LCD_EnviaOtroBlanco	
	movlw	' '			; Esto es un espacio en blanco.
	call	LCD_Caracter		; Visualiza tanto espacios en blanco como se
	decfsz	LCD_Auxiliar1,F		; haya cargado en (LCD_Auxiliar1).
	goto	LCD_EnviaOtroBlanco
	return

; Subrutinas "LCD_ByteCompleto" y "LCD_Byte" --------------------------------------------
;
; Subrutina "LCD_ByteCompleto", visualiza el byte que almacena el registro W en el
; lugar actual de la pantalla. Por ejemplo, si (W)=b'10101110' visualiza "AE".
;
; Subrutina "LCD_Byte" igual que la anterior, pero en caso de que el nibble alto sea cero 
; visualiza en su lugar un espacio en blanco. Por ejemplo si (W)=b'10101110' visualiza "AE"
; y si (W)=b'00001110', visualiza " E" (un espacio blanco delante).
;
; Utilizan la subrutina "LCD_Nibble" que se analiza más adelante.
;
LCD_Byte
	movwf	LCD_Auxiliar2		; Guarda el valor de entrada.
	andlw	b'11110000'		; Analiza si el nibble alto es cero.
	btfss	STATUS,Z		; Si es cero lo apaga.
	goto	LCD_VisualizaAlto		; No es cero y lo visualiza.
	movlw	' '			; Visualiza un espacio en blanco.
	call	LCD_Caracter
	goto	LCD_VisualizaBajo

LCD_ByteCompleto
	movwf	LCD_Auxiliar2		; Guarda el valor de entrada.
LCD_VisualizaAlto
	swapf	LCD_Auxiliar2,W		; Pone el nibble alto en la parte baja.
	call	LCD_Nibble		; Lo visualiza.
LCD_VisualizaBajo
	movf	LCD_Auxiliar2,W		; Repite el proceso con el nibble bajo.
;	call	LCD_Nibble		; Lo visualiza.
;	return

; Subrutina "LCD_Nibble" ----------------------------------------------------------------
;
; Visualiza en el lugar actual de la pantalla, el valor hexadecimal que almacena en el nibble
; bajo del registro W. El nibble alto de W no es tenido en cuenta. Ejemplos:
; - Si (W)=b'01010110', se visualizará "6". 
; - Si (W)=b'10101110', se visualizará "E". 
;
LCD_Nibble
	andlw	b'00001111'		; Se queda con la parte baja.
	movwf	LCD_Auxiliar1		; Lo guarda.
	sublw	0x09			; Comprueba si hay que representarlo con letra.
	btfss	STATUS,C	
	goto	LCD_EnviaByteLetra
	movf	LCD_Auxiliar1,W
	addlw	'0'			; El número se pasa a carácter ASCII sumándole
	goto 	LCD_FinVisualizaDigito	; el ASCII del cero y lo visualiza.
LCD_EnviaByteLetra
	movf	LCD_Auxiliar1,W
	addlw	'A'-0x0A			; Sí, por tanto, se le suma el ASCII de la 'A'.
LCD_FinVisualizaDigito
	goto	LCD_Caracter		; Y visualiza el carácter. Se hace con un "goto"
					; para no sobrecargar la pila.
```


----------



## ByAxel (Ene 15, 2014)

Con.

```
LCD_Linea1				; Cursor al principio de la Línea 1.
	movlw	b'10000000'		; Dirección 00h de la DDRAM
	goto	LCD_EnviaComando
LCD_Linea2				; Cursor al principio de la Línea 2.
	movlw	b'11000000'		; Dirección 40h de la DDRAM
	goto	LCD_EnviaComando
```

Indica que el principio de la línea 1 inicia en 00h (0x00) y la 2 en 40h, con esto se sabe que cada línea se extiende hasta 64 caracteres (se ven los 16 primeros) de cada línea.

Siguiendo la lógica cada 64 caracteres, la línea 3 estará en 0x80 y la 4ta en 0xC0 pero también puede ser cada 32 caracteres siendo: 1ra en 00h, 2da en 20h, 3ra en 40h y la 4ta en 60h.

Las direcciones pueden variar, por ejemplo en proteus indica que la 1 en 80h, 2 en C0h, 3 en 90h y 4 en D0h. 
Si has probado tu libreria de 2x16 con el LCD de 4x16 y logra escribir las dos primeras líneas, entonces utiliza el método que propuse primero.

*Edit:*
_Me falto indicar que b'10000000' es un comando para el LCD, que le indica que se va a dezplazar el cursor. Al comando debes de agregar la dirección misma. Por ejemplo b'10000000' + 40h = b'11000000'_

Saludos.


----------



## estebanfolcher (Ene 15, 2014)

claro como el agua gracias


----------

