// I2C - LCD LIBRARY 
// MCC Melody MSSP (I2C-SPI) peripheral module
// Adapted from examples in the following tutorial: 
// https://deepbluembedded.com/interfacing-i2c-lcd-16x2-tutorial-with-pic-microcontrollers-mplab-xc8/

// The adaptation consist on replacing the MCP23S17 16-bit SPI expander 
// using in the Explorer 8 schematic to connect the LM1602 LCD, by the PCF8574
// 8-bit I2C expander. The software is the MCC Melody I2C library generated
// selecting the MSSPx peripherals. 

/**
  Section: Included Files
 */
#include "I2C_LCD.h"
#include "PCF8574.h"

uint8_t RS, i2c_add;
uint8_t BackLight_State = LCD_BACKLIGHT_ON;  // Lighting by default

const i2c_host_interface_t *I2C = &i2c2_host_host_interface;


//---------------[ LCD Routines ]----------------
//-----------------------------------------------
 
void LCD_Init(uint8_t I2C_Add)
{
  i2c_add = I2C_Add;
  IO_Expander_Write(0x00);
  __delay_ms(30);
  LCD_CMD(0x03);
  __delay_ms(5);
  LCD_CMD(0x03);
  __delay_ms(5);
  LCD_CMD(0x03);
  __delay_ms(5);
  LCD_CMD(LCD_RETURN_HOME);
  __delay_ms(5);
  LCD_CMD(0x20 | (LCD_TYPE << 2));
  __delay_ms(50);
  LCD_CMD(LCD_TURN_ON);
  __delay_ms(50);
  LCD_CMD(LCD_CLEAR);
  __delay_ms(50);
  LCD_CMD(LCD_ENTRY_MODE_SET | LCD_RETURN_HOME);
  __delay_ms(50);
}
 
void LCD_Write_4Bit(uint8_t Nibble)
{
  // Get The RS Value To LSB OF Data
  Nibble |= RS;
  Nibble = Nibble | BackLight_State;
  IO_Expander_Write(Nibble | 0x04);
  IO_Expander_Write(Nibble & 0xFB);
  __delay_us(50);
}
 
void LCD_CMD(uint8_t CMD)
{
  RS = 0; // Command Register Select
  LCD_Write_4Bit(CMD & 0xF0);
  LCD_Write_4Bit((CMD << 4) & 0xF0);
}
 
void LCD_Write_Char(char Data)
{
  RS = 1; // Data Register Select
  LCD_Write_4Bit(Data & 0xF0);
  LCD_Write_4Bit((Data << 4) & 0xF0);
}
 
void LCD_Write_String(char * Str)
{
  for(int i=0; Str[i]!='\0'; i++)
    LCD_Write_Char(Str[i]);
}
 
void LCD_Set_Cursor(uint8_t ROW, uint8_t COL)
{
  switch(ROW) 
  {
    case 2:
      LCD_CMD(0xC0 + COL-1);
      break;
    case 3:
      LCD_CMD(0x94 + COL-1);
      break;
    case 4:
      LCD_CMD(0xD4 + COL-1);
      break;
    // Case 1
    default:
      LCD_CMD(0x80 + COL-1);
  }
}
 
void LCD_Backlight_ON(void)
{
  //BackLight_State = LCD_BACKLIGHT;
  IO_Expander_Write(LCD_BACKLIGHT_ON);
}
 
void LCD_Backlight_OFF(void)
{
  //BackLight_State = LCD_NOBACKLIGHT;
  IO_Expander_Write(LCD_BACKLIGHT_OFF);
}
 
void LCD_SL_Display(void)
{
  LCD_CMD(LCD_SHIFT_LEFT);
  __delay_us(40);
}
 
void LCD_SR_Display(void)
{
  LCD_CMD(LCD_SHIFT_RIGHT);
  __delay_us(40);
}
 
void LCD_SR_Cursor(void)
{
  LCD_CMD(LCD_MOVE_CURSOR_RIGHT);
  __delay_us(40);
}

void LCD_SL_Cursor(void)
{
  LCD_CMD(LCD_MOVE_CURSOR_LEFT);
  __delay_us(40);
}

void LCD_Blink_Cursor(void)
{
  LCD_CMD(LCD_BLINK_CURSOR_ON);
  __delay_us(40);
}
void LCD_Blink_Cursor_OFF(void)
{
  LCD_CMD(LCD_BLINK_CURSOR_OFF);
  __delay_us(40);
}
 void LCD_Cursor_ON(void)
{
  LCD_CMD(LCD_CURSOR_ON);
  __delay_us(40);
}
 
  void LCD_Cursor_OFF(void)
{
  LCD_CMD(LCD_CURSOR_OFF);
  __delay_us(40);
}

void LCD_Clear(void)
{
  LCD_CMD(LCD_CLEAR);
  __delay_us(40);
}