/**********************************************************
 i2cLCD.c - Gestione LCD con interfaccia I2C
 *********************************************************
 I2C con MSSP (RC1:0).
 Comando per backlight negativo (0=on - 1=off) con RC4 ad
 alta corrente, open drain.
 
 PIC16F18313
 *********************************************************/
#include <xc.h>
#include "i2cLCD.h"

/* I/O

 PORTC
 | RC5 | RC4 | RC3 | RC2 | RC1 | RC0 |
 |-----|-----|-----|-----|-----|-----|
 |  -  | BKL |  -  |  -  | SDA | SCL |
 */

#define _XTAL_FREQ 4000000  // Fosc 4MHz
#define I2C_clk 100000 	    // I2C clock 100kHz 

/***********************************************************
 Costanti LCD
************************************************************/
// LCD i2c address
char addr = 0x78;    // SA1=SA0=0 selez. hardware
//#define add 0X7A;  // SA1=0 SA0=1
//#define add 0X7C;  // SA1=1 SA0=0
//#define add 0X7E;  // SA1=SA0=1

//**********************************************************
// Inizializza I/O per I2C
//**********************************************************
void i2c_Init(){  
    // impostazioni singole dei pin per LCD i2c

	ANSA0 = 0;		// no analogica
	ANSA1 = 0;		//
	ANSA4 = 0;		//
	WPUA0 = 1;		// wpu
	WPUA1 = 1;		// wpu
	WPUA4 = 0;		// no wpu
	LATA4 = 1;		// preset backlight off
    ODCA4 = 1;     	// RA4 open drain
    TRISA0 = 1;    	// RC0 in SCL
    TRISA1 = 1;    	// RC1 in SDA
    TRISA4 = 0;     // RC4 out BKL

    // Inizializza PSS per I2C
	// SCL su A0, SDA su A1
    RA0PPS = 0x18;     // RA0->MSSP1:SCL1    
    RA1PPS = 0x19;     // RA1->MSSP1:SDA1  	
    SSP1CLKPPS = 0x00; // RA0->MSSP1:SCL1 
    SSP1DATPPS = 0x01; // RA1->MSSP1:SDA1
	
    // Impostazioni MSSP
    SSP1CON1 = 0b00101000;      // SSPEN on, I2C Master
    SSP1CON2 = 0;
    // clock = FOSC / (4 * (SSP1ADD+1))
    SSP1ADD = (_XTAL_FREQ/(4*I2C_clk))-1; // 9 = 100kHz
    SSP1STAT = 0; 
}

/***********************************************************
 Funzioni LCD
************************************************************/
//---------  Invia un byte su I2C
char i2c_Write(char data){
    PIR1bits.SSP1IF = 0; 	        // cancella il flag 
    SSP1BUF = data; 		        // invio byte
    while(!PIR1bits.SSP1IF);       // wait fine tx
    return (SSP1CON2bits.ACKSTAT); // Restituisce 0 se ACK
}                                  // se NACK restituisce 1

//--------- Trasmissione Byte I2C
void i2c_ByteWrite(char addr, char cont, char data){
    i2c_Idle(); 		 // Conferma bus inattivo
    i2c_Start();		 // Invia bit di start
    i2c_Write(addr); 		 // Invia indirizzo
    i2c_Write(cont); 		 // Invia controllo
    i2c_Write(data);		 // Invia dato
    i2c_Stop(); 		 // Invia bit di stop
}

//***********************************************************
// Funzioni per il display LCD
//***********************************************************
//--------- Invia un dato all'LCD
void LCD_dat(char data){
 i2c_ByteWrite(addr, 0x40, data);
 __delay_us(50); 		// 50usec
}

//--------- Invia un comando all'LCD
void LCD_cmd(char cmd){
 i2c_ByteWrite(addr, 0x00, cmd);
 if(cmd & 0xFC) 		// istruzione normale
 __delay_us(50); 		// attesa 50usec
 else 				// comandi Clear e Home
 __delay_ms(2); 		// attesa 2ms
}

//-------- Cancella il display
void LCD_clr(void){
 LCD_cmd(0x01); 		//comando Clear
}

//-------- Specifica la posizione del cursore
// ypos = riga, xpos = posizione sulla riga
void LCD_posyx(char ypos, char xpos){
 unsigned char pos;
 switch(ypos & 0x03){
 case 0: pos=0x80;break;
 case 1: pos=0xC0;break;
 }
 LCD_cmd(pos += xpos);
}

//-------- Inizializzazione LCD
void LCD_Init(void){
 __delay_ms(100);		// attesa iniziale
 LCD_cmd(0x38); 		// Modo 8 bit 2 righe
 __delay_us(100);
 LCD_cmd(0x0E); 		// display e cursor on, blink Off
 __delay_us(100);
 LCD_cmd(0x01); 		// clear
 __delay_ms(10);
 LCD_cmd(0x06); 		// modo inc., no shift
 __delay_us(100);
}

//-------- Uscita stringa
void LCD_str(char *str){
 while(*str) 		// continua a fine stringa (0)
 LCD_dat(*str++); 	// Out carattere, puntatore +1
}

//-------- Uscita stringa da ROM
void LCD_ROMstr(const char *str){
 while(*str)        //continua a fine stringa (0)
 LCD_dat(*str++);   //Out carattere, puntatore +1
}