#include <16F877a.h>
#fuses HS,NOWDT,NOPROTECT, BROWNOUT, PUT
#use delay(clock=20000000)

#use rs232(baud=9600, UART1)

// Useful options (see PIC-C help for a complete list)
//
// SCL = pin, SDA = pin    - Choose the pins manually when using 
//                           software-based I2C.
// STREAM = id             - Give this I2C bus a name. Usful when
//                           using two I2C buses.
// FAST=nnnnnn             - Set I2C baudrate to nnnnnn Hz
//                           Defalt I2C rate is 100 KHz
//                           Fast I2C devices can run at 400 KHz or higher
//                           * only use 100KHz for software-based I2C.
// FORCE_HW                - Tell the compiler to use the PIC's i2c hardware
//                           for the i2c bus. Must use PIN_C3, PIN_C4 on the 16F887.
// FORCE_SW                - Tell the compiler to user software-based I2C.

#use i2c(MASTER, I2C1, FORCE_HW)

// Registers used to check and clear I2C errors
#bit SSPEN = 0x14.5
#bit SSPOV = 0x14.6
#bit WCOL  = 0x14.7

#define TARGET_ADDRESS  0xB0    // slave address

#define SUCCESS      1
#define NOT_SUCCESS  0

#define CMD_SEND_BYTE_TO_SLAVE         1
#define CMD_READ_TWO_BYTES_FROM_SLAVE  2


void resetI2C() {

   // clear the error flag registers and re-enable the i2c bus
   SSPEN = 0;   // disable i2c
   SSPOV = 0;   // clear the receive overflow flag
   WCOL = 0;    // clear the write collision flag

   SSPEN = 1;   // re-enable i2c   

}



/////////////////////////////////////////////////////////////////////
//  Send one byte to the slave
/////////////////////////////////////////////////////////////////////

int sendByteToSlave(int outByte) {

   disable_interrupts(GLOBAL);

   i2c_start();

   // if i2c_write does not return 0 -> it means we did not get an ACK back
   // from the slave. It means the slave is not present or is not working.

   if ( i2c_write(TARGET_ADDRESS | 0) != 0) {
      resetI2C();  // reset the bus (this is optional)
      enable_interrupts(GLOBAL);

      return(NOT_SUCCESS);
   }

   // send a command to the slave telling it that 
   // we want to send one byte to it.
   i2c_write(CMD_SEND_BYTE_TO_SLAVE);
   i2c_write(outByte);

   i2c_stop();
   enable_interrupts(GLOBAL);

   return(SUCCESS);
   
}



/////////////////////////////////////////////////////////////////////
//  Read two bytes from the slave
//
//  Note: It is important that we use a second i2c_start() when
//        we want to switch the data flow from master->slave to 
//        master<-slave. This i2c_start() will actually generate
//        a "re-start" condition on the bus. DO NOT use i2c_stop()
//        before this command. The program will not work.
/////////////////////////////////////////////////////////////////////

int readTwoBytesFromSlave(int *inByte1, int *inByte2) {

   disable_interrupts(GLOBAL);

   i2c_start();

   // if i2c_write does not return 0 -> it means we did not get an ACK back
   // from the slave. It means the slave is not present or is not working.

   if (i2c_write(TARGET_ADDRESS | 0) !=0) {
      resetI2C();  // reset the bus (this is optional)
      enable_interrupts(GLOBAL);
      return(NOT_SUCCESS);
      
   }

  
   // send a command to the slave telling it that 
   // we want to read two bytes from it.
   i2c_write(CMD_READ_TWO_BYTES_FROM_SLAVE);
   
   // We re-start the I2C to switch it to read from the slave
   // *Note: there must not be an i2c_stop() before this command
   //        even if it is inside an "if". The compiler will think
   //        we have stopped the i2c and i2c_start will not 
   //        generat a re-start condition on the bus. This will
   //        cause i2c communication errors 
   i2c_start();
   
   // tell the slave we want to read from it by setting bit 0 of the slave
   // address to 1.
   i2c_write(TARGET_ADDRESS | 1);
   
   *inByte1 = i2c_read();
   *inByte2 = i2c_read(0); // the last i2c_read() must always NACK using 
                           // parameter 0

   i2c_stop();

   enable_interrupts(GLOBAL);

   return(SUCCESS);
   
}


void main() {

   int byte1, byte2;

   while(1) {

      delay_ms(1000);

      // Send one byte to slave
      if (sendByteToSlave(123) == SUCCESS) {
         printf("Send success\r\n");
      } else {
         printf("Send failed\r\n");
      }
      
      // Read two bytes from slave and store them on two local variables
      // which we pass their addresses as parameters to the function call
       if (readTwoBytesFromSlave(&byte1, &byte2) == SUCCESS) {
         printf ("Read byte 1 = %u, byte2 = %u\r\n", byte1, byte2);
       } else {
         printf ("Read failed\r\n");
       }

   }


}
