/*  Változótípusok és uniók definiálása ******************************/
typedef unsigned char       uint8;   // 8 bites, előjel nélküli szám
typedef unsigned int        uint16;  //16 bites, előjel nélküli szám
typedef unsigned long       uint32;  //32 bites, előjel nélküli szám
typedef signed char         int8;    // 8 bites, előjeles szám
typedef signed int          int16;   //16 bites, előjeles szám
typedef signed long         int32;   //32 bites, előjeles szám

///Unió típus szó és bájt eléréssel 16 bites adatokhoz
typedef union _union16 { 
  uint16 word;
  struct {
    uint8 lo_byte;
    uint8 hi_byte;
  };
} union16;

///Unió típus duplaszó, szó és bájt eléréssel 32 bites adatokhoz
typedef union _union32 {
  uint32 dword;
  struct {
    uint16 lo_word;
    uint16 hi_word;
  };
  uint8 byte[4];
} union32;

#define I2C_SCL		TRISBbits.TRISB1
#define I2C_SDA 	TRISBbits.TRISB0
#define I2C_ACK		0	//pozitív nyugtázás
#define I2C_NAK		1	//negatív nyugtázás

/* Makrók az SSPCON1 regiszter beállításához  */
#define   SSPENB    			0b00100000  	/* Enable serial port and configures SCK, SDO, SDI*/
#define   SLAVE_7   			0b00000110     	/* I2C Slave mode, 7-bit address*/
#define   SLAVE_10  			0b00000111    	/* I2C Slave mode, 10-bit address*/
#define   MASTER    			0b00001000     	/* I2C Master mode */
#define   MASTER_FIRMW			0b00001011		//I2C Firmware Controlled Master mode (slave Idle)
#define   SLAVE_7_STSP_INT 		0b00001110		//I2C Slave mode, 7-bit address with Start and Stop bit interrupts enabled
#define   SLAVE_10_STSP_INT 	0b00001111		//I2C Slave mode, 10-bit address with Start and Stop bit interrupts enabled

/* Makrók az SSPSTAT regiszter beállításához */
#define   SLEW_ON   			0b00000000  	/* Slew rate vezérlés engedélyezve (400kHz mód) */
#define   SLEW_OFF  			0b10000000  	/* Slew rate vezérlés letiltva  (100kHz mód) */
#define   SMBUS_ENABLE 			0b01000000  	/* SMBUS specifikus bemenet engedélyezése */
#define   SMBUS_DISABLE			0b00000000  	/* SMBUS specifikus bemenet tiltása  */

#define i2c_idle()  while ((SSPCON2 & 0x1F) | (SSPSTATbits.R_W))
#define i2c_start() SSPCON2bits.SEN=1; while(SSPCON2bits.SEN)
#define i2c_rstart() SSPCON2bits.RSEN=1; while(SSPCON2bits.RSEN)
#define i2c_stop()  SSPCON2bits.PEN=1; while(SSPCON2bits.PEN)
#define i2c_ack(ackbit)   SSPCON2bits.ACKDT=ackbit; SSPCON2bits.ACKEN=1; while(SSPCON2bits.ACKEN)
#define Fosc 48000000L
void i2c_init(uint16 bitrate_kHz);
uint8 i2c_getc(uint8 ack2send);
uint8 i2c_putc(uint8 data);
void i2c_write1(uint8 addr,uint8 data);
void i2c_write2(uint8 addr,uint8 d1, uint8 d2);
void i2c_writeN(uint8 addr, uint8* data, uint8 cnt);
void i2c_read1(uint8 addr,uint8* p1);
void i2c_read2(uint8 addr,uint8* p1, uint8* p2);
void i2c_readN(uint8 addr,uint8* p1, uint8 cnt);


/** Az I2C modul konfigurálása és engedélyezése a bitrate_kHz 
 * paraméter által megadott adatsebességgel.
 * \param bitrate_kHz a kHz egységekben megadott adatátviteli sebesség
 */
void i2c_init(unsigned int bitrate_kHz) {
  #define clk_tmp Fosc/4000L
  SSPADD = clk_tmp/bitrate_kHz -1;
  SSPSTAT = SLEW_OFF | SMBUS_DISABLE;
  SSPCON1 = MASTER;
  SSPCON2 = 0x00;
  I2C_SCL = 1;
  I2C_SDA = 1;
  SSPCON1 |= SSPENB; 			// az MSSP egység engedélyezése
}

/** Művelet: Beolvas egy bájtot az I2C buszról, maj kiküldi az \em ack2Send 
 * paraméterrel megadott nyugtázó bitet a slave eszköznek.
 * \param ack2Send a nyugtázó bit (0: ACK, 1: NAK), amelyet a beolvasás végén kiküldünk a slave-nek
 * \return a beolvasott bájt
 */

uint8 i2c_getc(uint8 ack2send) {
  i2c_idle();
  SSPCON2bits.RCEN = 1;                 // a master engedélyezése egy bájt vételére
  while ( !SSPSTATbits.BF );            // megvárjuk, amíg a bájt beérkezik 
  i2c_ack(ack2send);					// kiküldjük a nyugtázó bitet
  return ( SSPBUF );                    // visszatérünk a bájttal
}

/** Művelet: Kiír egy bájtot az I2C buszra, maj megvárja nyugtázást
 * \param data ki1randó adatbájt
 * \return a nyugtázás eredménye ((0: ACK, 1: NAK)
 */
uint8 i2c_putc(uint8 data) {
  SSPBUF = data;                        // kirakunk egy bájtot SSPBUF-ba
  while( SSPSTATbits.BF );              // megvárjuk,amíg az íráslezajlik  
  i2c_idle();                           // megvárjuk a busz aktivitás végét
  return ( SSPCON2bits.ACKSTAT );       // a nyugtázó bittel térünk vissza
}

/** Tranzakció: Egy bájt írása 
 * \param addr  a slave I2C címe
 * \param data  a kiküldeni kívánt bájt
 */
void i2c_write1(uint8 addr,uint8 data) {
  i2c_start();
  i2c_putc(addr & 0xFE);
  i2c_putc(data);
  i2c_stop();
}

/** Tranzakció: Két bájt írása
 * \param addr a slave I2C címe
 * \param d1  az első kiküldeni kívánt bájt
 * \param d2  a második kiküldeni kívánt bájt
 */
void i2c_write2(uint8 addr,uint8 d1, uint8 d2) {
  i2c_start();
  i2c_putc(addr & 0xFE);
  i2c_putc(d1);
  i2c_putc(d2);
  i2c_stop();
}

/** Tranzakció: Adott számú adatbájt írása 
 * \param addr a slave I2C címe
 * \param *data a kírandó adatok mutatója
 * \param cnt a kiírandó adatbájtok száma
 */
void i2c_writeN(uint8 addr, uint8* data, uint8 cnt) {
  uint8 i;
  i2c_start();
  i2c_putc(addr & 0xFE);
  for (i=0; i < cnt; i++) {
    i2c_putc(*data);
    data++;
  }
  i2c_stop();
}

/** Tranzakció: Egy bájtot olvas az \em addr című eszközről. 
 *  A beolvasott adat a \em p1 mutatóval megcímzett helyre kerül.
 * \param addr a slave I2C címe
 * \param *p1 a beolvasott adat eltárolási helyének mutatója
 */
void i2c_read1(uint8 addr,uint8* p1) {
  i2c_start();
  i2c_putc(addr | 0x01);
  *p1 = i2c_getc(I2C_NAK);
  i2c_stop();
}

/** Tranzakció: Két bájtot olvas az \em addr című eszközről. 
 *  A beolvasott adatok a \em p1 és \em p2 mutatóval megcímzett 
 *  bájtokba kerülnek elhelyezésre.
 * \param addr a slave I2C címe
 * \param *p1 az első beolvasott adat eltárolási helyének mutatója
 * \param *p2 a második beolvasott adat eltárolási helyének mutatója
 */
void i2c_read2(uint8 addr,uint8* p1, uint8* p2) {
  i2c_start();
  i2c_putc(addr | 0x01);
  *p1 = i2c_getc(I2C_ACK);
  *p2 = i2c_getc(I2C_NAK);
  i2c_stop();
}

/** Tranzakció: A \em cnt paraméterrel megadott darabszámú bájtot olvas 
 *  az \em addr című eszközről. A beolvasott adat a \em p1 mutatóval megcímzett 
 *  helytől kezdődően kerülnek elhelyezésre, egymás után.
 * \param addr a slave I2C címe
 * \param *p1 a beolvasott adatok eltárolási helyének mutatója
 * \param cnt a beolvasandó adatbájtok száma
 */ 
void i2c_readN(uint8 addr,uint8* p1, uint8 cnt) {
  uint8 i;
  i2c_start();
  i2c_putc(addr | 0x01);
  for (i=0; i < cnt; i++) {
    if(i != cnt-1) *p1 = i2c_getc(I2C_ACK);
    else  *p1 = i2c_getc(I2C_NAK);
    p1++;
  }
  i2c_stop();
}

