/*********************************************************************
 *                 Flowcode LCD Display Component Code
 *
 * File: PIC_LCD.c
 *
 * (c) 2009 Matrix Multimedia Ltd.
 * http://www.matrixmultimedia.com
 *
 * Software License Agreement
 *
 * The software supplied herewith by Matrix Multimedia Ltd (the
 * “Company”) for its Flowcode graphical programming language is
 * intended and supplied to you, the Company’s customer, for use
 * solely and exclusively on the Company's products. The software
 * is owned by the Company, and is protected under applicable
 * copyright laws. All rights are reserved. Any use in violation
 * of the foregoing restrictions may subject the user to criminal
 * sanctions under applicable laws, as well as to civil liability
 * for the breach of the terms and conditions of this licence.
 *
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 * Changelog:
 *
 *  date  | by | description
 * -------+----+-----------------------------------------------------
 * 300307 | ST | Created
 * 150807 | ST | Added a missing delay to <RawSend> function
 * 100608 | BR | Modified <Cursor> addressing on 4-line displays
 * 170209 | BR | Modified <Cursor> addressing on 16/20 MX_UINT8 4 line displays
 * 250209 | BR | Modified <Start> to allow for a more reliable startup
 * 120309 | BR | Added ClearLine and ScrollDisplay functions.
 * 180309 | BR | Changed delays to be WDT compatible
 * 130509 | BR | Added % definitions to DefinesCode section
 * 230311 | BR | Changed code to work for hitech and boostc without ifdefs
 * 051011 | BR | Modified the component to fit in with the CAL
 * 011211 | BR | Updated FC_CAL_Bit functions
 * 130812 | BR + MW | Added a fix to allow LCD to work when using high speed 8-bit PICs >= 32MHz
 * 091112 | LM | above needed for 19.66MHz 16F1937
 * 160113 | BR | Fixed a bug where a 16F877A does not know what a LAT register is!
 ********************************************************************/

/*********************************************************************

 Return & parameter types:
   void
   MX_UINT8
   MX_SINT16
   MX_STRING

 Pin directions:
   0 = OUTPUT
   2 = INPUT
   3 = BIDIRECTIONAL

**********************************************************************

[Settings]
CLSID={02A7087C-4D0C-4AA4-9522-82A47E88BDC8}
IsAnalogue=0
MultipleAllowed=1
Description=Alphanumeric LCD Flowcode Component

**********************************************************************

[Port]
DesiredPort=1
PortMandatory=1
SamePortAsID=-1

[Pins]
Count=0
DesiredPin=0
BitMandatory=1

[PinPort]
# PORTA = 0
# PORTB = 1
# PORTC = 2
# PORTD = 3
# PORTE = 4

[PinDesiredBit]

[PinDirection]
# DIR_INPUT = 2
# DIR_OUTPUT = 0
# DIR_BIDIRECTIONAL = 3

[PinMustUsePort]
# boolean

[SamePortAsPinX]
# -1 = any pin
# otherwise, the pin number

[PinMustUsePin]
# boolean

[PinValue]
# boolean (0 = off, 1 = on)

**********************************************************************

NOTE: For simulation to work, these macros _MUST_ remain at the indicated index:
   1=RawSend
   2=Start
   3=Clear
   4=PrintASCII
   5=Command
   6=Cursor
   7=PrintNumber
   8=PrintString
   9=ScrollDisplay
   10=ClearLine


[MacroNames]
Count=11
1=RawSend
2=Start
3=Clear
4=PrintASCII
5=Command
6=Cursor
7=PrintNumber
8=PrintString
9=ScrollDisplay
10=ClearLine
11=RAM_Write

[MacroReturns]
1=void
2=void
3=void
4=void
5=void
6=void
7=void
8=void
9=void
10=void
11=void

[MacroIsPrivate]
1=1
2=0
3=0
4=0
5=0
6=0
7=0
8=0
9=0
10=0
11=0

[MacroParameters_RawSend]
Count=2
1=in
2=mask

[MacroParamTypes_RawSend]
1=MX_UINT8
2=MX_UINT8

[MacroParameters_Start]
Count=0

[MacroParamTypes_Start]


[MacroParameters_Clear]
Count=0

[MacroParamTypes_Clear]


[MacroParameters_PrintASCII]
Count=1
1=Character

[MacroParamTypes_PrintASCII]
1=MX_UINT8


[MacroParameters_Command]
Count=1
1=in

[MacroParamTypes_Command]
1=MX_UINT8


[MacroParameters_Cursor]
Count=2
1=x
2=y

[MacroParamTypes_Cursor]
1=MX_UINT8
2=MX_UINT8


[MacroParameters_PrintNumber]
Count=1
1=Number

[MacroParamTypes_PrintNumber]
1=MX_SINT16


[MacroParameters_PrintString]
Count=1
1=String

[MacroParamTypes_PrintString]
1=MX_STRING


[MacroParameters_ScrollDisplay]
Count=2
1=Direction
2=Num_Positions

[MacroParamTypes_ScrollDisplay]
1=MX_UINT8
2=MX_UINT8


[MacroParameters_ClearLine]
Count=1
1=Line

[MacroParamTypes_ClearLine]
1=MX_UINT8

[MacroParameters_RAM_Write]
Count=9
1=nIdx
2=d0
3=d1
4=d2
5=d3
6=d4
7=d5
8=d6
9=d7

[MacroParamTypes_RAM_Write]
1=MX_UINT8
2=MX_UINT8
3=MX_UINT8
4=MX_UINT8
5=MX_UINT8
6=MX_UINT8
7=MX_UINT8
8=MX_UINT8
9=MX_UINT8

/********************************************************************
 * ADDITIONAL CODE
 ********************************************************************/

/*DefinesCode_Start*/

/**** Macro Substitutions ****
a = Unique Component Reference Number
b = D1 Port Letter
c = D2 Port Letter
d = D3 Port Letter
e = D4 Port Letter
f = RS Port Letter
g = E Port Letter
h = Data 1_Pin
i = Data 2 Pin
j = Data 3 Pin
k = Data 4 Pin
l = RS Pin
m = Enable Pin
n = Row Count
o = Column Count
******************************/

   //component connections
   //Port pin lat workaround for 8-bit PIC devices running >= 32MHz
   //seems we need this mod for 19660800 16F1937 so changed to >= 16MHz

  #ifdef FC_CAL_PIC
   #if MX_CLK_SPEED >= 32000000
   #define %a_PORT0    lat%b
   #define %a_PORT1    lat%c
   #define %a_PORT2    lat%d
   #define %a_PORT3    lat%e
   #define %a_PORT4    lat%f
   #define %a_PORT5    lat%g
   #else
    #ifdef MX_LCD_PORT_LAT
    #define %a_PORT0    lat%b
    #define %a_PORT1    lat%c
    #define %a_PORT2    lat%d
    #define %a_PORT3    lat%e
    #define %a_PORT4    lat%f
    #define %a_PORT5    lat%g
    #else
    #define %a_PORT0    port%b
    #define %a_PORT1    port%c
    #define %a_PORT2    port%d
    #define %a_PORT3    port%e
    #define %a_PORT4    port%f
    #define %a_PORT5    port%g
   #endif
   #endif
  #else
   #define %a_PORT0    port%b
   #define %a_PORT1    port%c
   #define %a_PORT2    port%d
   #define %a_PORT3    port%e
   #define %a_PORT4    port%f
   #define %a_PORT5    port%g
  #endif

   #define %a_TRIS0    tris%b
   #define %a_TRIS1    tris%c
   #define %a_TRIS2    tris%d
   #define %a_TRIS3    tris%e
   #define %a_TRIS4    tris%f
   #define %a_TRIS5    tris%g
   #define %a_BIT0       %h
   #define %a_BIT1       %i
   #define %a_BIT2       %j
   #define %a_BIT3       %k
   #define %a_RS         %l
   #define %a_E          %m
   #define %a_ROWCNT   %n
   #define %a_COLCNT   %o

   #ifdef _BOOSTC
     #define %a_DELAY   delay_10us(10)
   #endif
   #ifdef _C2C_
     #define %a_DELAY   delay_us(100)
   #endif
   #ifdef HI_TECH_C
     #define %a_DELAY   __delay_us(120)
   #endif
   #ifndef %a_DELAY
     #define %a_DELAY   delay_us(100)
   #endif

/*DefinesCode_End*/

/*InitialisationCode_Start*/
/*InitialisationCode_End*/

/*InterruptCode_Start*/
/*InterruptCode_End*/

/********************************************************************
 * FUNCTIONS
 ********************************************************************/

void RawSend(MX_UINT8 in, MX_UINT8 mask)
{
/*Macro_RawSend_Start*/

   MX_UINT8 pt;

   FC_CAL_Bit_Low(%a_PORT0, %a_BIT0);
   FC_CAL_Bit_Low(%a_PORT1, %a_BIT1);
   FC_CAL_Bit_Low(%a_PORT2, %a_BIT2);
   FC_CAL_Bit_Low(%a_PORT3, %a_BIT3);
   FC_CAL_Bit_Low(%a_PORT4, %a_RS);
   FC_CAL_Bit_Low(%a_PORT5, %a_E);
   pt = ((in >> 4) & 0x0f);
   if (pt & 0x01)
       FC_CAL_Bit_High(%a_PORT0, %a_BIT0);
   if (pt & 0x02)
       FC_CAL_Bit_High(%a_PORT1, %a_BIT1);
   if (pt & 0x04)
       FC_CAL_Bit_High(%a_PORT2, %a_BIT2);
   if (pt & 0x08)
       FC_CAL_Bit_High(%a_PORT3, %a_BIT3);
   if (mask)
       FC_CAL_Bit_High(%a_PORT4, %a_RS);
   %a_DELAY;
   FC_CAL_Bit_High (%a_PORT5, %a_E);
   %a_DELAY;
   FC_CAL_Bit_Low (%a_PORT5, %a_E);
   pt = (in & 0x0f);
   %a_DELAY;
   FC_CAL_Bit_Low(%a_PORT0, %a_BIT0);
   FC_CAL_Bit_Low(%a_PORT1, %a_BIT1);
   FC_CAL_Bit_Low(%a_PORT2, %a_BIT2);
   FC_CAL_Bit_Low(%a_PORT3, %a_BIT3);
   FC_CAL_Bit_Low(%a_PORT4, %a_RS);
   FC_CAL_Bit_Low(%a_PORT5, %a_E);
   if (pt & 0x01)
       FC_CAL_Bit_High(%a_PORT0, %a_BIT0);
   if (pt & 0x02)
       FC_CAL_Bit_High(%a_PORT1, %a_BIT1);
   if (pt & 0x04)
       FC_CAL_Bit_High(%a_PORT2, %a_BIT2);
   if (pt & 0x08)
       FC_CAL_Bit_High(%a_PORT3, %a_BIT3);
   if (mask)
       FC_CAL_Bit_High(%a_PORT4, %a_RS);
   %a_DELAY;
   FC_CAL_Bit_High (%a_PORT5, %a_E);
   %a_DELAY;
   FC_CAL_Bit_Low (%a_PORT5, %a_E);
   %a_DELAY;

/*Macro_RawSend_End*/
}


void Start()
{
/*Macro_Start_Start*/

   FC_CAL_Bit_Low_DDR(%a_PORT0, %a_TRIS0, %a_BIT0);
   FC_CAL_Bit_Low_DDR(%a_PORT1, %a_TRIS1, %a_BIT1);
   FC_CAL_Bit_Low_DDR(%a_PORT2, %a_TRIS2, %a_BIT2);
   FC_CAL_Bit_Low_DDR(%a_PORT3, %a_TRIS3, %a_BIT3);
   FC_CAL_Bit_Low_DDR(%a_PORT4, %a_TRIS4, %a_RS);
   FC_CAL_Bit_Low_DDR(%a_PORT5, %a_TRIS5, %a_E);

   Wdt_Delay_Ms(12);

   RawSend(0x32, 0);
   Wdt_Delay_Ms(50);
   RawSend(0x28, 0);
   Wdt_Delay_Ms(50);
   RawSend(0x0c, 0);
   Wdt_Delay_Ms(50);
   RawSend(0x01, 0);
   Wdt_Delay_Ms(50);
   RawSend(0x06, 0);
   Wdt_Delay_Ms(50);
   

   //clear the display
   RawSend(0x01, 0);
   Wdt_Delay_Ms(50);
   RawSend(0x02, 0);
   Wdt_Delay_Ms(50);

/*Macro_Start_End*/
}



void Clear()
{
/*Macro_Clear_Start*/

   RawSend(0x01, 0);
   Wdt_Delay_Ms(2);
   RawSend(0x02, 0);
   Wdt_Delay_Ms(2);

/*Macro_Clear_End*/
}



void PrintASCII(MX_UINT8 Character)
{
/*Macro_PrintASCII_Start*/

   RawSend(Character, 0x10);

/*Macro_PrintASCII_End*/
}



void Command(MX_UINT8 in)
{
/*Macro_Command_Start*/

   RawSend(in, 0);
   Wdt_Delay_Ms(2);

/*Macro_Command_End*/
}



void Cursor(MX_UINT8 x, MX_UINT8 y)
{
/*Macro_Cursor_Start*/

  #if (%a_ROWCNT == 1)
    y=0x80;
  #endif

  #if (%a_ROWCNT == 2)
   if (y==0)
      y=0x80;
   else
      y=0xc0;
  #endif

  #if (%a_ROWCNT == 4)
   if (y==0)
      y=0x80;
   else if (y==1)
      y=0xc0;

   #if (%a_COLCNT == 16)
      else if (y==2)
         y=0x90;
      else
         y=0xd0;
   #endif

   #if (%a_COLCNT == 20)
      else if (y==2)
         y=0x94;
      else
         y=0xd4;
   #endif
  #endif

   RawSend(y+x, 0);
   Wdt_Delay_Ms(2);

/*Macro_Cursor_End*/
}


void PrintNumber(MX_SINT16 Number)
{
/*Macro_PrintNumber_Start*/

   MX_SINT16 tmp_int;
   MX_UINT8 tmp_byte;

   if (Number < 0)
   {
      RawSend('-', 0x10);
      Number = 0 - Number;
   }

   tmp_int = Number;
   if (Number >= 10000)
   {
      tmp_byte = tmp_int / 10000;
      RawSend('0' + tmp_byte, 0x10);

      while (tmp_byte > 0)
      {
         tmp_int = tmp_int - 10000;
         tmp_byte--;
      }
   }
   if (Number >= 1000)
   {
      tmp_byte = tmp_int / 1000;
      RawSend('0' + tmp_byte, 0x10);

      while (tmp_byte > 0)
      {
         tmp_int = tmp_int - 1000;
         tmp_byte--;
      }
   }
   if (Number >= 100)
   {
      tmp_byte = tmp_int / 100;
      RawSend('0' + tmp_byte, 0x10);

      while (tmp_byte > 0)
      {
         tmp_int = tmp_int - 100;
         tmp_byte--;
      }
   }
   if (Number >= 10)
   {
      tmp_byte = tmp_int / 10;
      RawSend('0' + tmp_byte, 0x10);

      while (tmp_byte > 0)
      {
         tmp_int = tmp_int - 10;
         tmp_byte--;
      }
   }
   RawSend('0' + tmp_int, 0x10);

/*Macro_PrintNumber_End*/
}


void PrintString(MX_UINT8 * String, MX_UINT8 MSZ_String)
{
/*Macro_PrintString_Start*/

   MX_UINT8 idx = 0;

   for (idx=0; idx<MSZ_String; idx++)
   {
      if (String[idx] == 0)
      {
         break;
      }
      RawSend(String[idx], 0x10);
   }

/*Macro_PrintString_End*/
}


void ScrollDisplay(MX_UINT8 Direction, MX_UINT8 Num_Positions)
{
/*Macro_ScrollDisplay_Start*/

   MX_UINT8 cmd = 0;
   MX_UINT8 count;

   //Choose the direction
   switch (Direction)
   {
      case 0:
      case 'l':
      case 'L':

         cmd = 0x18;
         break;

      case 1:
      case 'r':
      case 'R':

         cmd = 0x1C;
         break;

      default:
         break;
   }

   //If direction accepted then scroll the specified amount
   if (cmd)
   {
      for (count = 0; count < Num_Positions; count++)
         Command(cmd);
   }

/*Macro_ScrollDisplay_End*/
}


void ClearLine(MX_UINT8 Line)
{
/*Macro_ClearLine_Start*/

   MX_UINT8 count;
   MX_UINT8 rowcount;

   //Define number of columns per line
   #if (%a_ROWCNT == 1)
      rowcount=80;
   #endif

   #if (%a_ROWCNT == 2)
      rowcount=40;
   #endif

   #if (%a_ROWCNT == 4)
      #if (%a_COLCNT == 16)
         rowcount=16;
      #endif
      #if (%a_COLCNT == 20)
         rowcount=20;
      #endif
   #endif

   //Start at beginning of the line
   Cursor (0, Line);

   //Send out spaces to clear line
   for (count = 0; count < rowcount; count++)
      RawSend(' ', 0x10);

   //Move back to the beginning of the line.
   Cursor (0, Line);

/*Macro_ClearLine_End*/
}


void RAM_Write(MX_UINT8 nIdx, MX_UINT8 d0,  MX_UINT8 d1,  MX_UINT8 d2,  MX_UINT8 d3,  MX_UINT8 d4,  MX_UINT8 d5,  MX_UINT8 d6,  MX_UINT8 d7)
{
/*Macro_RAM_Write_Start*/
   //set CGRAM address

   RawSend(64 + (nIdx << 3), 0);
   delay_ms(2);

   //write CGRAM data
   RawSend(d0, 0x10);
   RawSend(d1, 0x10);
   RawSend(d2, 0x10);
   RawSend(d3, 0x10);
   RawSend(d4, 0x10);
   RawSend(d5, 0x10);
   RawSend(d6, 0x10);
   RawSend(d7, 0x10);

   //Clear the display
   RawSend(0x01, 0);
   delay_ms(2);
   RawSend(0x02, 0);
   delay_ms(2);

/*Macro_RAM_Write_End*/
}


