/**************************************************************************
*    Libreria para manejo de LCD en 4 u 8 bits para compilador HiTech     *
***************************************************************************

***************************************************************************
* Basado en el mapa de memoria de los display WinStar y los comandos del  *
* HD44780.																  *
* Sirve para display 16x1, 16x2 y 16x4									  *
***************************************************************************


***************************************************************************
*																		  *
* Antes de incluir este archivo en nuestro programa hay que definir		  *
* algunos parametros. Estos son											  *
*																		  *
* _XTAL_FREQ		(es necesario para los delay utilizados internamente) *
* LcdDataPort		(el puerto que se utilizara para los datos)			  *
* LcdControlPort	(el puerto que se utilizara para los bits de control) *
* LcdRWBit			(el numero de bit que se utilizara para RW)			  *
* LcdRSBit			(el numero de bit que se utilizara para RS)			  *
* LcdENBit			(el numero de bit que se utilizara para EN)			  *
*																		  *
* En el caso de querer usar protocolo de 4 Bits tambien hay que definir	  *
*																		  *
* LcdD4				(el numero de bit que sera D4 hacia el display)		  *
*																		  *
*																		  *
*   LcdD4 sólo puede ser 0 o 4. Si es 0 se utiliza el nibble bajo de	  *
* LcdDataPort para enviar los datos al display. Si es 1 se utiliza 		  *
* el nibble alto.														  *
*   Si no está definido LcdD4, se usa protocolo de 8 bits. En este caso   *
* se usan los 8 bits de LcdDataPort para enviar los datos al display.     *
*																		  *
***************************************************************************


***************************************************************************
*																		  *
*  En esta libreria se encuentran las siguientes funciones				  *
*																		  *
* InitLcd()					(inicializa del display)					  *
* ClearLcd()  				(borra el display y se posiciona en el inicio)*
* GotoxyLcd(char,char)		(posiciona el cursor en el lugar indicado)	  *
* LcdOn()					(enciende el display)						  *
* LcdOff()					(apaga el display)							  *
* SetCursorLcd(char,char)	(configura el cursor)						  *
* WriteCharLcd(char)		(envia un caracter al display)				  *
* WriteStrLcd(char*)		(envia un String al display)				  *
*																		  *
*  El parametro de la funcion anterior puede ser un puntero a un String   *
* o el propio String.													  *
*																		  *
* ReadBytLcd()				(devuelve el byte leido en la posicion actual)*
* WriteByteLcd(char,char)	(envia un byte al display)					  *
*																		  *
*  El segundo parametro de la funcion anterior puede ser 0 o 1 y es el    *
* valor que va a tomar el pin RS.										  *
*  Por eso, esta funcion puede ser utilizada para enviar una instruccion  * 
* o un caracter al display.								  				  *
*																		  *
***************************************************************************

***************************************************************************
*																		  *
*  Luego de utilizar las funciones, el puerto utilizado para LcdDataPort  *
* retorna a su estado original. 										  *
*  Es decir que el registro TRIS vuelve a configurarse como estaba antes  *
* de utilizar las funciones. Y sobre el puerto vuelve a aparecer el mismo *
* dato que había antes de utilizar las funciones para el display          *
* 																		  *
***************************************************************************

* Ejemplo de configuracion y utilizacion

  #define _XTAL_FREQ 10000000	// Cristal 10MHz
  #define LcdControlPort PORTC	// Uso el puerto A para los bits de control
  #define LcdRWBit 0			// El bit 0 del puerto A para RW
  #define LcdRSBit 1			// El bit 0 del puerto A para RS
  #define LcdENBit 1			// El bit 0 del puerto A para EN
  #define LcdDataPort  PORTB	// Uso el puerto B para los datos
  //#define LcdD4 4				// Solo utilizar para protocolo de 4 Bits

#include "SuperLcd.C"			// Incluimos nuestra libreria

void main(void)
{
  const char * pantalla = "Mundo";

  .....
  .....
  .....
  InitLcd();
  WriteStrLcd ("Hola");			// escribe en la primer linea la palabra Hola
  GotoxyLcd (1,2);
  WriteStrLcd (pantalla);		// escribe en la segunda linea la palabra apuntada por la variable pantalla
}

*/

#if defined (LcdControlPort) && defined (LcdRWBit) && defined (LcdRSBit) && defined (LcdENBit) &&\
    defined (LcdDataPort)
  #if defined (LcdD4)			//Define el bit de menos peso del nibble (puede ser 0 o 4)
    #define Protocolo4Bits
    #if (LcdD4 != 0) && (LcdD4 != 4)
      #error Mal definido D4 del LCD
	#endif
  #endif

  unsigned char Mascara;
  volatile bit TRISLcdRW @ (unsigned)&LcdControlPort*8 +0x80*8 +LcdRWBit;
  volatile bit TRISLcdRS @ (unsigned)&LcdControlPort*8 +0x80*8 +LcdRSBit;
  volatile bit TRISLcdEN @ (unsigned)&LcdControlPort*8 +0x80*8 +LcdENBit;
  volatile bit LcdRW @ (unsigned)&LcdControlPort*8 +LcdRWBit;
  volatile bit LcdRS @ (unsigned)&LcdControlPort*8 +LcdRSBit;
  volatile bit LcdEN @ (unsigned)&LcdControlPort*8 +LcdENBit;
  volatile char TRISLcdData @ (unsigned)&LcdDataPort + 0x80;


  unsigned char ReadByteLcd(void)
  {
	unsigned char high, low;
	unsigned char TRISAnterior;
	unsigned char DatoAnterior;
	unsigned char DatoLeido;

	DatoAnterior = LcdDataPort;
	TRISAnterior = TRISLcdData;
	#ifdef Protocolo4Bits
	  #if (LcdD4 == 0)
	    TRISLcdData |= 0x0F;
	  #elif (LcdD4 == 4)
		TRISLcdData |= 0xF0;
	  #endif
	#else
	  TRISLcdData = 0xFF;
	#endif
	LcdRS = 0;
    LcdRW = 1;
	LcdEN = 1;
	__delay_us (1);
	#ifdef Protocolo4Bits
	  high = LcdDataPort;
	  LcdEN = 0;
	  __delay_us (1);
	  LcdEN = 1;
	  __delay_us (1);
	  low = LcdDataPort;
	  LcdEN = 0;
	  #if (LcdD4 == 0)
	    high = high<<4;
		low = low & 0x0F;  
	  #elif (LcdD4 == 4)
	    high = high & 0xF0;
		low = low>>4;	
	  #endif
	  DatoLeido = high + low;
	#else
	  DatoLeido = LcdDataPort;
	  LcdEN = 0;
	#endif
	TRISLcdData = TRISAnterior;
	LcdDataPort = DatoAnterior;
	return DatoLeido;
  }

  void WriteNibbleLcd (unsigned char Data)
  {
	#ifdef Protocolo4Bits
      #if (LcdD4 == 0) 
	    LcdDataPort = (LcdDataPort & 0xF0) + (Data>>4);
      #elif (LcdD4 == 4) 
	    LcdDataPort = (LcdDataPort & 0x0F) + (Data & 0xF0);
	  #endif
    #else
	  LcdDataPort = Data;
    #endif
	LcdEN = 1;
    __delay_us(2);
    LcdEN = 0;
  }

  void WriteByteLcd(unsigned char Data, unsigned char dir)
  {
    unsigned char TRISAnterior;
    unsigned char DatoAnterior;

    DatoAnterior = LcdDataPort;
	TRISAnterior = TRISLcdData;    
    TRISLcdData &= Mascara;
	while ((ReadByteLcd() & 128) == 128);
	__delay_us(10);
	LcdRS = (bit)dir;
	LcdRW = 0;
	#ifdef Protocolo4Bits			//Si uso 4 bits separa el byte en 2
	  WriteNibbleLcd(Data);
	  WriteNibbleLcd(Data<<4);
    #else
      WriteNibbleLcd(Data);			//Si uso 8 bits envio el byte entero
	#endif
    TRISLcdData = TRISAnterior;
    LcdDataPort = DatoAnterior;
  }

  void WriteStrLcd (const char *palabra)
  {
	while (*palabra)
		WriteByteLcd ((*palabra++),1);
  }

  void WriteCharLcd (char letra)
  {
	WriteByteLcd (letra,1);
  }

  void GotoxyLcd (unsigned char x, unsigned char y)
  {
	if (y == 1) y = 0;
	else
	  if (y == 2) y = 0x40;
	  else
  		if (y == 3) y = 0x10;
		else
		  if (y == 4) y = 0x50;
	x--;
	WriteByteLcd (((y+x)|0x80),0);
  }

  void ClearLcd (void)
  {
	WriteByteLcd (0x01,0);
  }

  void SetCursorLcd (unsigned char On, unsigned char Blink)
  {
	unsigned char salida;

	salida = 0b11<<1;
	salida = (salida|(bit)On)<<1;
	salida = salida|(bit)Blink;
	WriteByteLcd (salida,0);
  }

  void LcdOn (void)
  {
	WriteByteLcd (0x0E,0);
  }

  void LcdOff (void)
  {
	WriteByteLcd (0x08,0);
  }

  void InitLcd(unsigned char forma)
  {
    unsigned char TRISAnterior;
	unsigned char DatoAnterior;
	unsigned char Contador;

    #ifdef Protocolo4Bits
      #if (LcdD4 == 0)
	    Mascara = 0xF0;	
      #elif (LcdD4 == 4)
	    Mascara = 0x0F;
	  #endif
	#else
      Mascara = 0;
	#endif
    TRISLcdRW = 0;
	TRISLcdRS = 0;
	TRISLcdEN = 0;
    LcdEN = 0;
	TRISAnterior = TRISLcdData;
	DatoAnterior = LcdDataPort;	
	LcdRW = 0;
    LcdRS = 0;
	TRISLcdData &= Mascara;
	__delay_ms (15);
	for (Contador= 3; Contador > 0; Contador--)
	{
	  WriteNibbleLcd (0x30);
	  __delay_ms (5);
	}
    #ifdef Protocolo4Bits
	  WriteNibbleLcd (0x20);			//indica que se escribe en 2 nibbles de 4 bits
	  WriteByteLcd (0x28,0);
    #else
	  WriteByteLcd (0x38,0);
	#endif
	ClearLcd();
	WriteByteLcd (0x06,0);
	WriteByteLcd (0x0E,0);
	TRISLcdData = TRISAnterior;
	LcdDataPort = DatoAnterior;
  }

#else
  #error Faltan definir parametros
#endif