        /*-----------------------------------------------*/
        /*        PIC16F84 LCD Debugging Terminal        */
        /*        Dual Line LCD 16x2                     */
        /*        Design by  M. Asim Khan                */
        /*        Dated  01.november.2000                */
        /*-----------------------------------------------*/

        /*-----------------------------------------------*/
        /*   LCD Operating Modes                         */
        /*                                               */
        /*   Mode Jumper Open   --> Normal Ascii mode    */
        /*   Mode Jumper Closed --> Hex Display mode     */
        /*                                               */
        /*   LCD Commands (Ascii mode)                   */
        /*                                               */
        /*   Clear display                 0x0C          */
        /*   Go to start of second line    0x0A          */
        /*   Go to start of first line     0x0D          */
        /*   Move back one position        0x08          */
        /*   Turn on LED                   0x0E          */
        /*   Turn off LED                  0x0F          */
        /*-----------------------------------------------*/


#include    <16F84.H>

#FUSES      XT,NOPROTECT,NOWDT
#ID         CHECKSUM

typedef unsigned int        uc ;

            /*----- Constant Definitions -----*/

                                // for hex mode
#define EOL1            5       // Lcd end of line 1 (16 char/line)
#define EOL2            10      // Lcd end of line 2

#define LCD_TYPE        2       // 0=5x7, 1=5x10, 2=2 lines
#define LCD_LINE_TWO    0x40    // LCD RAM address for the second line
#define LCD_WRITE       0x01    // ddr port-b lcd write
#define LCD_READ        0xF1    // ddr port-b lcd read


                                // ports bit definitions
#define RXD             PIN_B0
#define LCD_RS          PIN_B1
#define LCD_RW          PIN_B2
#define LCD_EN          PIN_B3

#define MODE_SW         PIN_A3
#define LED_D1          PIN_A4


#USE                FAST_IO(A)
#USE                FAST_IO(B)
#USE                DELAY(CLOCK=3579545)
#USE                RS232(BAUD=9600, XMIT=RXD, RCV=RXD)


            /*----- Functions Prototypes -----*/

void        Initialize (void) ;
uc          LcdReadByte (void) ;
void        LcdSendNibble (uc n) ;
void        LcdSendByte (uc address, uc n) ;
void        LcdGotoXY (uc x, uc y) ;
void        LcdPutc (uc c) ;


            /*----- Global Variables -----*/

#byte   Port_A = 0x05
#byte   Port_B = 0x06

uc  const LCD_INIT_BYTES[4] = {0x20 | (LCD_TYPE << 2), 0x0C, 1, 6} ;

uc  const HEX_ASCII[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
                           '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'} ;

uc      Cursor ;

/*------------------------------*/
void    main (void)
{
 uc     k ;

 Initialize() ;

 LcdPutc (0x0C) ;
 LcdPutc ("Ready...") ;

 while (TRUE)
   {
    k = getc() ;                            // get serial data

    if (input (MODE_SW))                    // check mode switch status
      LcdPutc (k) ;
    else
      {
       LcdPutc (HEX_ASCII[(k >> 4)]) ;      // output hex data
       LcdPutc (HEX_ASCII[(k & 0x0F)]) ;
       LcdPutc (' ') ;

       Cursor++ ;

       if (Cursor == EOL1)
         LcdGotoXY (1, 2) ;                 // select second line

       else if (Cursor == EOL2)
         {
          Cursor = 0 ;
          LcdGotoXY (1, 1) ;                // select first line
         }
      }
   }
}

/*------------------------------*/
void    Initialize()
{
 uc     i ;

 Port_A = 0xFF ;
 set_tris_a (0xE8) ;          // set port-a bits direction

 Port_B = 0xF1 ;
 set_tris_b (0x01) ;          // set port-b bits direction

 Cursor = 0 ;

 delay_ms (100) ;

 for (i=1 ; i <= 3 ; ++i)
    {
     LcdSendNibble (3) ;
     delay_ms (5) ;
    }

 LcdSendNibble (2) ;

 for (i=0 ; i <= 3 ; ++i)
    LcdSendByte (0, LCD_INIT_BYTES[i]) ;
}

/*------------------------------*/
uc      LcdReadByte (void)
{
 uc     low, high ;

 set_tris_b (LCD_READ) ;
 output_high (LCD_RW) ;
 delay_cycles (1) ;

 output_high (LCD_EN) ;
 delay_cycles (1) ;
 high = (Port_B & 0xF0) ;

 output_low (LCD_EN) ;
 delay_cycles (1) ;
 output_high (LCD_EN) ;
 delay_us (1) ;
 low = (Port_B >> 4) ;

 output_low (LCD_EN) ;
 set_tris_b (LCD_WRITE) ;

 return (high | low) ;
}

/*------------------------------*/
void    LcdSendNibble (uc n)
{
 Port_B &= 0x0F ;
 Port_B |= (n << 4) ;
 delay_cycles (1) ;

 output_high (LCD_EN) ;
 delay_us (2) ;
 output_low (LCD_EN) ;
}

/*------------------------------*/
void    LcdSendByte (uc address, uc n)
{
 output_low (LCD_RS) ;

 while (bit_test(LcdReadByte(), 7)) ;

 output_low (LCD_RS) ;
 if (address)
   output_high (LCD_RS) ;

 delay_cycles (1) ;

 output_low (LCD_RW) ;
 delay_cycles (1) ;

 output_low (LCD_EN) ;

 LcdSendNibble (n >> 4) ;

 LcdSendNibble (n & 0x0F) ;
}

/*------------------------------*/
void    LcdGotoXY (uc x, uc y)
{
 uc     address ;

 if (y != 1)
   address = LCD_LINE_TWO ;
 else
   address = 0 ;

 address += x-1 ;

 LcdSendByte (0, 0x80 | address) ;
}

/*------------------------------*/
void    LcdPutc (uc c)
{
 switch (c)
   {
    case 0x0C :
               LcdSendByte (0, 1) ;
               delay_ms (2) ;
               break ;

    case 0x0A :
               LcdGotoXY (1, 2) ;
               break ;

    case 0x0D :
               LcdGotoXY (1, 1) ;
               break ;

    case 0x08 :
               LcdSendByte (0, 0x10) ;
               break ;

    case 0x0E :
               output_low (LED_D1) ;
               break ;

    case 0x0F :
               output_high (LED_D1) ;
               break ;

    default   :
               LcdSendByte (1, c) ;
               break ;
   }
}
