/*********************************************************************
* FileName:        LCD Module.c
* Processor:       PIC18F4520
* Compiler:        MPLAB C18 v.3.06 
*
*  This file implements the commands necessary to use the LCD Module
*  For wiring instructions for using an LCD with your project see the LCD Module.h file.
*
* Author               Date    Comment
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Naveen Raj        6/9/03      Original MPAM (Microchip Application Maestro) code
* David Fisher		9/24/08		Modified for ME430 use on PICDEM 2 boards
********************************************************************/

#include "LCD Module.h"
char _vXLCDreg =0;          //Used as a flag to check if from XLCDInit()

// Prototypes added by DSF 5/18/08 as well as functions at the end of .c file
void XLCDDelay15ms (void);
void XLCDDelay4ms (void);
void XLCD_Delay500ns(void);
void XLCDDelay(void);

/*********************************************************************
 * Function         : void XLCDInit(void)
 * PreCondition     : None
 * Input            : None
 * Output           : None
 * Side Effects     : None
 * Overview         : LCD is intialized
 * Note             : This function will work with all Hitachi HD447780
 *                    LCD controller.
 ********************************************************************/
void XLCDInit(void)
{
	// Add by DSF 9/26/08
	//  You need these three lines for the PICDEM 2 Board only
	//  If you are using an external LCD wire you can get back RD7 for IO
	//   by deleting these three lines of code	
	LCD_PWR = 1;
	TRISD = 0x00;
	LCD_PWR=1;   // to power up the LCD
	
	
/*This par of the code is initialization by instruction*/
_vXLCDreg=1; 

//PORT initialization  
    XLCD_DATAPORT_TRIS  &= 0xf0;
    XLCD_DATAPORT &= 0xf0;

//control port initialization
XLCD_RSPIN_TRIS =0;                         //make control ports output
XLCD_ENPIN_TRIS =0;
#ifndef XLCD_RW_GROUND
XLCD_RWPIN_TRIS =0;                         //if RW pin grounded
#endif

XLCD_RSPIN  =0;                             //clear control ports
XLCD_ENPIN  =0;
#ifndef XLCD_RW_GROUND
XLCD_RWPIN=0;                               //if RW pin grounded
#endif

//initialization by instruction
XLCDDelay15ms(); 
    XLCD_DATAPORT       &= 0xf0;    // Clear lower port
    XLCD_DATAPORT   |= 0b00000011;  // Function set cmd(4-bit interface)
    XLCD_ENPIN = 1;                 // Clock the cmd in
    XLCD_Delay500ns();   
    XLCD_ENPIN = 0;
    XLCDDelay4ms();
    XLCD_DATAPORT       &= 0xf0;    // Clear lower port
    XLCD_DATAPORT   |= 0b00000011;  // Function set cmd(4-bit interface)
    XLCD_ENPIN = 1;                 // Clock the cmd in
    XLCD_Delay500ns();   
    XLCD_ENPIN = 0;
    XLCDDelay4ms(); 
    XLCD_DATAPORT       &= 0xf0;    // Clear lower port
    XLCD_DATAPORT   |= 0b00000011;  // Function set cmd(4-bit interface)
    XLCD_ENPIN = 1;                 // Clock the cmd in
    XLCD_Delay500ns();   
    XLCD_ENPIN = 0;
    
    XLCDDelay4ms();    
    #ifdef XLCD_UPPER               // Upper nibble interface
    XLCD_DATAPORT   &= 0x0f;        // Clear upper port
    XLCD_DATAPORT   |= 0b00100000;
    #else                           // Lower nibble interface
    XLCD_DATAPORT       &= 0xf0;    // Clear lower port
    XLCD_DATAPORT   |= 0b00000010;  // Function set cmd(4-bit interface)
    #endif

    XLCD_ENPIN = 1;                 // Clock the cmd in
    XLCD_Delay500ns();   
    XLCD_ENPIN = 0;

//-----------------------------------------------------------------------
//function set command "0 0 1 DL N F X X"
//-----------------------------------------------------------------------
        #ifdef XLCD_FONT5x8
        XLCDCommand(0b00101000);    //if 2Line 5x8
        #else
        XLCDCommand(0b00101100);    //if 2Line 5x10
        #endif
    XLCDCommand(0b00001000);        //display off
    XLCDCommand(0b00000001);        //display clear
/////////////////////////////////////////////////////////////////////////////////
//Entry mode setting
////////////////////////////////////////////////////////////////////////////////
//Entry mode command " 0 0 0 0 0 1 ID S "
//ID =0 no cursor increment during read and write
//ID =1 cursor increment during read and write
//S =0 no display during read and write
//S =1 display shift 
       
#ifdef XLCD_CURSOR_INCREMENT
    #ifdef XLCD_DISPLAY_SHIFT
    XLCDCommand(0b00000111);    //if cursor inc and display shift
    #endif
    #ifdef XLCD_DISPLAY_NOSHIFT
    XLCDCommand(0b00000110);    //if cursor inc and no display shift
    #endif
#endif

#ifdef  XLCD_CURSOR_NOINCREMENT
    #ifdef XLCD_DISPLAY_SHIFT
    XLCDCommand(0b00000101);    //if no cursor increment, but with display shift
    #endif
    #ifdef XLCD_DISPLAY_NOSHIFT
    XLCDCommand(0b00000100);    //if no cursor increment, and no display shift
    #endif
#endif 
///////////////////////////////////////////////////////////////////////////////////
//Display on off ,Blink ,cursor command set 
// ///////////////////////////////////////////////////////////////////////////////
//"0 0 0 0 1 D C B "
//D=1 dislay on, C=1 cursor on, B=1 blink on

#ifdef  XLCD_DISPLAYON
    #ifdef XLCD_CURSORON
        #ifdef XLCD_BLINKON
        XLCDCommand(0b00001111);    //display on cursor on blink on
        #else
        XLCDCommand(0b00001110);    //display on cursor on blink off
        #endif
    #endif    
   
    #ifdef XLCD_CURSOROFF
        #ifdef XLCD_BLINKON
        //XLCDCommand(0b00001001);    //display on cursor off blink on
        #else
        XLCDCommand(0b00001100);    // display on cursor off blink off
        #endif
    #endif
#endif


#ifdef  XLCD_DISPLAYOFF
    XLCDCommand(0b00001000);        //display off
#endif            
_vXLCDreg=0;
// end of initialization
      return;
}

/*********************************************************************
 * Function         : void XLCDCommand(unsigned char cmd)
 * PreCondition     : None
 * Input            : cmd - Command to be set to LCD.
 * Output           : None
 * Side Effects     : None
 * Overview         : None
 * Note             : None
 ********************************************************************/
void XLCDCommand(unsigned char cmd)
{
if(_vXLCDreg==1)                            //if  called from XLCDinit routine is always Blocking
{
    #ifdef  XLCD_DELAYMODE
    XLCDDelay();
    #endif
    #ifdef  XLCD_READBFMODE
    XLCDIsBusy();
    #endif
}


if(_vXLCDreg==0)                            //if not called from XLCDinit routine
{                                           //if NON Block the user need to call XLCDIsBusy 
#ifdef  XLCD_BLOCK                          //and check the w reg status to check if the
    #ifdef  XLCD_DELAYMODE                  //module is free
    XLCDDelay();
    #endif
    #ifdef  XLCD_READBFMODE
    XLCDIsBusy();
    #endif
#endif
}

XLCD_RSPIN=0;
XLCD_ENPIN=0;
#ifndef XLCD_RW_GROUND
XLCD_RWPIN=0;
#endif


        XLCD_DATAPORT &=0xF0;               //clear port
        XLCD_DATAPORT |=((cmd>>4)&0x0f);
        XLCD_ENPIN = 1;                     // Clock the cmd in
        XLCD_Delay500ns();
        XLCD_ENPIN = 0;
        
        XLCD_DATAPORT &= 0xF0;              //clear port
        XLCD_DATAPORT |= cmd&0x0f ;	        //shift left 4 times
        XLCD_ENPIN = 1;
        XLCD_Delay500ns();
        XLCD_ENPIN = 0;
	
    return;
}
/*********************************************************************
 * Function         :XLCDPut()
 * PreCondition     :None
 * Input            :cmd - Command to be set to LCD.
 * Output           :None
 * Side Effects     :None
 * Overview         :None
 * Note             :None
 ********************************************************************/
void XLCDPut(char data)
{
#ifdef  XLCD_BLOCK
    #ifdef  XLCD_DELAYMODE
    XLCDDelay();
    #endif
    #ifdef  XLCD_READBFMODE
    XLCDIsBusy();
    #endif
#endif


#ifndef XLCD_RW_GROUND
XLCD_RWPIN=0;
#endif
XLCD_RSPIN=1;
XLCD_ENPIN=0;
#ifdef XLCD_8BIT
    XLCD_DATAPORT=data;
    XLCD_ENPIN = 1;
    XLCD_Delay500ns();
    XLCD_ENPIN = 0;
#endif
#ifdef XLCD_4BIT
    #ifdef XLCD_UPPER
        XLCD_DATAPORT &=0x0f;               //clear port
        XLCD_DATAPORT |= data&0xf0;         //write upper nibble to port
        XLCD_ENPIN = 1;                     // Clock the cmd in
        XLCD_Delay500ns();
        XLCD_ENPIN = 0;
        
        XLCD_DATAPORT &= 0x0f;              //clear port
        XLCD_DATAPORT |= (data<<4)&0xf0;	//shift left 4 times
        XLCD_ENPIN = 1;
        XLCD_Delay500ns();
        XLCD_ENPIN = 0;
    #endif
    #ifdef XLCD_LOWER
        XLCD_DATAPORT &=0xF0;               //clear port
        XLCD_DATAPORT |=((data>>4)&0x0f);
        XLCD_ENPIN = 1;                     // Clock the cmd in
        XLCD_Delay500ns();
        XLCD_ENPIN = 0;
        
        XLCD_DATAPORT &= 0xF0;              //clear port
        XLCD_DATAPORT |= data&0x0f ;	    //shift left 4 times
        XLCD_ENPIN = 1;
        XLCD_Delay500ns();
        XLCD_ENPIN = 0;
    #endif  



#endif

   return;
}



#ifndef XLCD_RW_GROUND    //need not compile any read command if RWpin grounded

/*********************************************************************
 * Function         :char XLCDIsBusy(void)
 * PreCondition     :None
 * Input            :None
 * Output           :non-zero if LCD controller is ready to accept new
 *                   data or commandzero otherwise.
 * Side Effects     :None
 * Overview         :None
 * Note             :None
 ********************************************************************/
char XLCDIsBusy(void)
{
XLCD_RSPIN=0;
XLCD_RWPIN=1;
XLCD_ENPIN=0;

#ifdef XLCD_8BIT
    XLCD_DATAPORT_TRIS=0xFF;                //make port input    
    XLCD_DATAPORT=0;      
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    
    if(_vXLCDreg==1)                        //will execute only if  called from XLCDInit
    { 
    while(XLCD_DATAPORT&0x80);
    XLCD_ENPIN=0;
    XLCD_DATAPORT_TRIS=0x00;                //make port output
    return;
    }  
      
    #ifdef  XLCD_BLOCK  
    if(_vXLCDreg==0)                        // will execute only if not called from XLCDInit
    {    
    while(XLCD_DATAPORT&0x80);
    XLCD_ENPIN=0;
    XLCD_DATAPORT_TRIS=0x00;                //make port input
    return;
    }
    #endif
    
    
    #ifdef  XLCD_NONBLOCK
    if(_vXLCDreg==0)                        //will execute only if not called from XLCDInit
    {
        if(XLCD_DATAPORT&0x80)              //Read bit 7 (busy bit)
        {   
        XLCD_ENPIN=0;                   
        XLCD_DATAPORT_TRIS=0x00;            //make port op
        return 1;                           //Return TRUE
        }       
        else                                
        {
        XLCD_ENPIN=0;
        XLCD_DATAPORT_TRIS=0x00;            //make port op
        return 0;                           //Return FALSE
        }      
    }
    #endif           
#endif           


#ifdef XLCD_4BIT 
    
    #ifdef  XLCD_UPPER   
    XLCD_DATAPORT_TRIS|=0xF0;               //make upper port input
    XLCD_DATAPORT   &=0x0F;
    XLCD_ENPIN=1;
    XLCD_Delay500ns();    
    if(_vXLCDreg==1)                        // will execute only if  called from XLCDInit
    { 
        while(XLCD_DATAPORT&0x80);
        XLCD_ENPIN=0;
        XLCD_Delay500ns();
        XLCD_ENPIN=1;
        XLCD_Delay500ns();
        XLCD_ENPIN=0;
        XLCD_DATAPORT_TRIS&=0x0F;           //make upper port output
        return;
    } 
    
    #ifdef  XLCD_BLOCK    
    if(_vXLCDreg==0)                        // When not called from the XLCDInit
    { 
        while(XLCD_DATAPORT&0x80);
        XLCD_ENPIN=0;
        XLCD_Delay500ns();
        XLCD_ENPIN=1;
        XLCD_Delay500ns();
        XLCD_ENPIN=0;
        XLCD_DATAPORT_TRIS&=0x0F;           //make upper port output
        return;
    }    
    #endif

    #ifdef  XLCD_NONBLOCK    
    if(_vXLCDreg==0)                        // will execute only if not called from XLCDInit
    {
    if(XLCD_DATAPORT&0x80)                  
        {  
        XLCD_ENPIN=0;                                 
        XLCD_Delay500ns();
        XLCD_ENPIN=1;
        XLCD_Delay500ns();
        XLCD_ENPIN=0;
        XLCD_DATAPORT_TRIS&=0x0F;           //make port output
        return 1;                           //Return TRUE
        }       
    else                                
        {
        XLCD_ENPIN=0; 
        XLCD_Delay500ns();                  // If high
        XLCD_ENPIN=1;
        XLCD_Delay500ns();
        XLCD_ENPIN=0;
        XLCD_DATAPORT_TRIS&=0x0F;           //make port input
        return 0;                           // Return FALSE
        }      

    }
    #endif     
    #endif      
    
    
    #ifdef  XLCD_LOWER
    XLCD_DATAPORT_TRIS|=0x0F;               //make lower port input
    XLCD_DATAPORT   &=0xF0; 
    XLCD_ENPIN=1;
    XLCD_Delay500ns();    
    if(_vXLCDreg==1)                        // will execute only if  called from XLCDInit
        { 
        while(XLCD_DATAPORT&0x08);
        XLCD_ENPIN=0;
        XLCD_Delay500ns();
        XLCD_ENPIN=1;
        XLCD_Delay500ns();
        XLCD_ENPIN=0;
        XLCD_DATAPORT_TRIS&=0xF0;           //make port output
        return;
        }      
    #ifdef XLCD_BLOCK 
    if(_vXLCDreg==0)                        //will execute only if  called from XLCDInit
        { 
        while(XLCD_DATAPORT&0x08);
        XLCD_ENPIN=0;
        XLCD_Delay500ns();
        XLCD_ENPIN=1;
        XLCD_Delay500ns();
        XLCD_ENPIN=0;
        XLCD_DATAPORT_TRIS&=0xF0;           //make port output
        return;
        } 
    #endif
  
    #ifdef XLCD_NONBLOCK 
    if(_vXLCDreg==0)                        // will execute only if  called from XLCDInit
        { 
            if(XLCD_DATAPORT&0x08)                  
            {  
            XLCD_ENPIN=0;                               
            XLCD_Delay500ns();
            XLCD_ENPIN=1;
            XLCD_Delay500ns();
            XLCD_ENPIN=0;
            XLCD_DATAPORT_TRIS=0x00;        //make port input
            return 1;                       // Return TRUE
            }        
        else                                
            {
            XLCD_ENPIN=0; 
            XLCD_Delay500ns();              
            XLCD_ENPIN=1;
            XLCD_Delay500ns();
            XLCD_ENPIN=0;
            XLCD_DATAPORT_TRIS=0x00;        //make port input
            return 0;                       // Return FALSE
            }      

        } 
    #endif
    #endif 
#endif 

}

/*********************************************************************
 * Function         :unsigned char XLCDGetAddr(void)
 * PreCondition     :None
 * Input            :None
 * Output           :Current address byte from LCD
 * Side Effects     :None
 * Overview         :None
 * Note             :The address is read from the character generator
 *                   RAM or display RAM depending on current setup.
 ********************************************************************/

unsigned char XLCDGetAddr(void)

{
char addr =0;
#ifdef  XLCD_BLOCK
    #ifdef  XLCD_DELAYMODE
    XLCDDelay();
    #endif
    #ifdef  XLCD_READBFMODE
    XLCDIsBusy();
    #endif
#endif

XLCD_RSPIN=0;
XLCD_RWPIN=1;
XLCD_ENPIN=0;

#ifdef XLCD_8BIT
    XLCD_DATAPORT_TRIS=0xFF;                //make port input        
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    addr=XLCD_DATAPORT;  
    XLCD_ENPIN=0;
    XLCD_DATAPORT_TRIS=0x00;                //make port input
    return(addr&0x7F);     
#endif        
#ifdef XLCD_4BIT    
    #ifdef  XLCD_UPPER 
    XLCD_DATAPORT_TRIS|=0xF0;               //make upper port input      
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    addr=XLCD_DATAPORT&0xF0;
    XLCD_ENPIN=0;;
    XLCD_Delay500ns();   
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    addr|=(XLCD_DATAPORT>>4)&0x0F;
    XLCD_ENPIN=0;    
    XLCD_DATAPORT_TRIS&=0x0F;               //make upper port output
    return(addr&0x7F);
    #endif  
          
    #ifdef  XLCD_LOWER
    XLCD_DATAPORT_TRIS|=0x0F;               //make lower port input  
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    addr=(XLCD_DATAPORT<<4)&0xF0;
    XLCD_ENPIN=0;    
    XLCD_Delay500ns();
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    addr|=XLCD_DATAPORT&0x0F;
    XLCD_ENPIN=0;    
    XLCD_DATAPORT_TRIS&=0xF0;               //make port output
    return(addr&0x7F);
    #endif  
 
#endif  

}


/*********************************************************************
 * Function         :char XLCDGet(void)
 * PreCondition     :None    
 * Input            :None
 * Output           :Current data byte from LCD
 * Side Effects     :None
 * Overview         :None
 * Note             :The data is read from the character generator
 *                   RAM or display RAM depending on current setup.
 ********************************************************************/
char XLCDGet(void)
{
    char data=0;
#ifdef  XLCD_BLOCK
    #ifdef  XLCD_DELAYMODE
    XLCDDelay();
    #endif
    #ifdef  XLCD_READBFMODE
    XLCDIsBusy();
    #endif
#endif
    XLCD_RSPIN=1;
    XLCD_RWPIN=1;
    XLCD_ENPIN=0;

#ifdef  XLCD_8BIT
    XLCD_DATAPORT_TRIS=0xFF;
    XLCD_ENPIN=1;
    XLCD_Delay500ns(); 
    data=XLCD_DATAPORT;
    XLCD_ENPIN=0;
    XLCD_DATAPORT_TRIS=0x00;
    return(data);
#endif
#ifdef  XLCD_4BIT
    #ifdef  XLCD_UPPER
    XLCD_DATAPORT_TRIS |=0xf0;              //make upper input     
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    data = XLCD_DATAPORT&0xf0;              //Read the upper nibble of data
    XLCD_ENPIN=0;
    XLCD_Delay500ns();
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    data |= ((XLCD_DATAPORT>>4)&0x0f);      //Read the upper nibble of data
    XLCD_ENPIN=0;
    XLCD_DATAPORT_TRIS &=0x0f;              //make output
    return(data);
    #endif
    
    #ifdef  XLCD_LOWER
    XLCD_DATAPORT_TRIS |=0x0F;              //make  input     
    XLCD_ENPIN=1;
    XLCD_Delay500ns();
    data = (XLCD_DATAPORT<<4)&0xf0;         //Read the upper nibble of data
    XLCD_ENPIN=0;
    XLCD_Delay500ns();
    XLCD_ENPIN=1;
    XLCD_Delay500ns();;
    data |= XLCD_DATAPORT&0x0f;             //Read the upper nibble of data
    XLCD_ENPIN=0;
    XLCD_DATAPORT_TRIS &=0xf0;              //make output
    return(data);
    #endif
#endif    
    

}
	
#endif		//end of #ifndef XLCD_RW_GROUND(all read commands)



/*********************************************************************
 * Function         :XLCDPutRomString(rom char *string)
 * PreCondition     :None    
 * Input            :None
 * Output           :Displays string in Program memory
 * Side Effects     :None
 * Overview         :None
 * Note             :is lways blocking till the string is written fully
 ********************************************************************/

void XLCDPutRomString(rom char *string)
{
     while(*string)                         // Write data to LCD up to null
    {    
        #ifdef  XLCD_NONBLOCK
        while(XLCDIsBusy());
        #endif 
        XLCDPut(*string);                   // Write character to LCD
        string++;                           // Increment buffer
    }
    return;
}
/*********************************************************************
 * Function         :XLCDPutRomString(rom char *string)
 * PreCondition     :None    
 * Input            :None
 * Output           :Displays string in Program memory
 * Side Effects     :None
 * Overview         :None
 * Note             :is lways blocking till the string is written fully
 ********************************************************************/
void XLCDPutRamString(char *string)
{
    while(*string)                          // Write data to LCD up to null
    {       
        #ifdef  XLCD_NONBLOCK
        while(XLCDIsBusy());
        #endif
               
        XLCDPut(*string);                   // Write character to LCD
        string++;                           // Increment buffer
    }
    return;
}

// Timing Functions
//   Note: If you ever want to use a frequency that is NOT 4 MHz you will need to go into the LCD Module.c file (this file)
//   and modify these functions (found below).  They are hard coded to cause specific time delays and expect a 4 MHz
//   clock signal.  They are easy to change but if you aren't at 4 MHz they won't do want they say.

/*The user  is require to write this 15 milli second delay in his routine,this delay */
/*is required as it is used in XLCDInit() which is used to initialize the LCD module*/
void XLCDDelay15ms (void)
{
    // Want 15ms using 4 MHz
    // 15 000 instructions
    Delay1KTCYx(15);
}

/*The user  is require to write this 4 milli second delay in his routine,this  delay */
/*is required as it is used in XLCDInit() which is used to initialize the LCD module*/
void XLCDDelay4ms (void)
{
    // Want 4ms using 4 MHz
    // 4 000 instructions
    Delay1KTCYx(4);
}

/*The user  is require to write this 500 nano second in his routine  this  delay */
/*is required as it is used in all read and write commaands in the XLCD routines*/
void XLCD_Delay500ns(void)
{
    Nop();
    Nop();
    Nop();
}

/*The user  is require to write this XLCDDelay() in his routine,this is required to write if */
/*the mode selected is by delay , it is used in all XLCD read and write commands of this routine */
void XLCDDelay(void)
{
	// We are using the blocking delay mode (it's the easiest so we need this)
    // Want at least 2 ms using 4 MHz (DSF picked 2 ms, longer is safer)
    // 2 000 instructions
    Delay1KTCYx(2);
}