/* ----- file I2C_BUS.C66 -----                                             */



/* Use it for interfacing the devices connected to the I2C-bus              */



#include "I2C_BUS.H"



/****************************************************************************/
/* drive bus in inactive state, reenable eventually locked SDA-line         */

void I2CInit(void) {



/* re-enable SDA-line                                                       */

   I2CDelay();
   I2CPutSCL(0);                       /* preset SCL=0 to avoid accidential */
                                       /* transmission of START condition   */
                                       /* by following SDA-transition       */
   I2CDelay();
   I2CPutSDA(0);                       /* drive SDA to inactive state       */
   I2CDelay();
   I2CPutSCL(1);                       /* set SCL for the first time        */
   I2CDelay();
   I2CPutSCL(0);                       /* release SCL                       */
   I2CDelay();
   I2CPutSCL(1);                       /* set SCL for the second time       */
   I2CDelay();
   I2CPutSCL(0);                       /* release SCL                       */
   I2CDelay();
   I2CPutSCL(1);                       /* set SCL for the third time        */

/* I2C-bus is now going in the inactive state by the following commands     */

   I2CDelay();
   I2CPutSDA(1);                       /* this is also the state after RESET*/
   I2CDelay();
   I2CPutSDA(1);                       /* setup SCL for START condition     */
}

/****************************************************************************/
/* writes a sequence of bytes to a device on I2C-bus                        */
/*                                                                          */
/*    *SourcePtr: Sourcepointer from RAM                                    */
/*    DestAddr:   destination-address in I2C-device                         */
/*    Size:       length of sequence                                        */

BYTE I2CWrite( BYTE *SourcePtr, BYTE DeviceID, BYTE DestAddr, WORD Size) {
   BYTE Result;



   I2CStart();                         /* send START condition              */
   I2CWriteByte(DeviceID & 0xFE);      /* send ID with cleared RD/WR bit    */

   if((Result = I2CReceiveAck()) != I2C_SUCCESSFUL) {
                                       /* is device responding ?            */
      I2CStop();
      return(Result);
   }

   I2CWriteByte(DestAddr);             /* write destination address         */

   if((Result = I2CReceiveAck()) != I2C_SUCCESSFUL) {
      I2CStop();
      return(Result);
   }

   while(Size--) {                     /* loop until all bytes send         */
      I2CWriteByte(*(SourcePtr++));

      if((Result = I2CReceiveAck()) != I2C_SUCCESSFUL) {
         I2CStop();
         return(Result);
      }
   }

   I2CStop();                          /* send STOP condition               */
   return(I2C_SUCCESSFUL);             /* no error occured                  */
}

/****************************************************************************/
/* reads a sequence of bytes from a device on I2C-bus                       */
/*                                                                          */
/*    *dest:   destination-pointer from RAM                                 */
/*    source:  source-address in I2C-device                                 */
/*    length:  length of sequence                                           */

BYTE I2CRead(BYTE *DestPtr, BYTE DeviceID, BYTE SourceAddr, WORD Size) {
   BYTE Result;



   I2CStart();                         /* send START condition              */
   I2CWriteByte(DeviceID & 0xFE);      /* send ID with cleared RD/WR bit    */

   if((Result = I2CReceiveAck()) != I2C_SUCCESSFUL) {
                                       /* is device responding ?            */
      I2CStop();
      return(Result);
   }

   I2CWriteByte(SourceAddr);           /* write source address              */

   if((Result = I2CReceiveAck()) != I2C_SUCCESSFUL) {
      I2CStop();
      return(Result);
   }

   I2CStart();                         /* send START condition              */
   I2CWriteByte(DeviceID | 0x01);      /* send ID with set RD/WR bit        */

   if((Result = I2CReceiveAck()) != I2C_SUCCESSFUL) {
      I2CStop();
      return(Result);
   }

   while(Size--) {                     /* loop until all data received      */
      *(DestPtr++) = I2CReadByte();    /* read data                         */

      if(Size)                         /* send ACK to slave except last time*/
         I2CSendAck();
   }

   I2CStop();                          /* send STOP condition               */
   return(I2C_SUCCESSFUL);             /* no error occured                  */
}

/****************************************************************************/
/* sends START condition on I2C-bus                                         */

void I2CStart(void) {



   I2CDelay();
   I2CPutSDA(1);                       /* this is also the idle bus state   */
   I2CDelay();
   I2CPutSCL(1);                       /* this is also the idle bus state   */
   I2CDelay();
   I2CPutSDA(0);                       /* generate START condition          */
   I2CDelay();
   I2CPutSCL(0);                       /* drive SCL to inactive             */
}

/****************************************************************************/
/* sends STOP condition on I2C-bus                                          */

void I2CStop(void) {



   I2CDelay();
   I2CPutSCL(0);                       /* setup SCL (redundant)             */
   I2CDelay();
   I2CPutSDA(0);                       /* setup SDA (redundant)             */
   I2CDelay();
   I2CPutSCL(1);                       /* setup SCL for STOP condition      */
   I2CDelay();
   I2CPutSDA(1);                       /* generate STOP condition           */
}

/****************************************************************************/
/* sends ACKNOWLEDGE condition, i.e. sends an bit-value=0                   */

void I2CSendAck(void) {



   I2CSendBit(0);
}

/****************************************************************************/
/* receives ACKNOWLEDGE condition, i.e. receives an bit-value=0             */

BYTE I2CReceiveAck(void) {



   if (I2CReceiveBit() == 0)
      return(I2C_SUCCESSFUL);          /* must be 0 if device responds      */
   else
      return(I2C_NO_ACKNOWLEDGE);
}

/****************************************************************************/
/* sends one bit to I2C-bus, also used for sending ACKNOWLEDGE condition    */

void I2CSendBit(BYTE State) {



   I2CDelay();
   I2CPutSCL(0);                       /* setup SCL (redundant)             */
   I2CDelay();
   I2CPutSDA(State);                   /* setup SDA                         */
   I2CDelay();
   I2CPutSCL(1);                       /* enable SDA writing                */

   I2CDelay();                         /* let SCL get stabilized High       */

   while(!I2CGetSCL());                /* wait until SCL-line is released   */

   I2CDelay();
   I2CPutSCL(0);                       /* disable SDA writing (SDA is now   */
                                       /* clocked)                          */
   I2CDelay();
   I2CPutSDA(1);                       /* release SDA */
}

/****************************************************************************/
/* receives one bit from I2C-bus, also used for receiving an ACKNOWLEDGE    */
/* condition                                                                */

BYTE I2CReceiveBit(void) {
   BYTE State;



   I2CDelay();
   I2CPutSCL(0);                       /* setup SCL (redundant)             */
   I2CDelay();
   I2CPutSDA(1);                       /* setup SDA for input (TRISTATE)    */
   I2CDelay();
   I2CPutSCL(1);                       /* enable SDA reading                */

   I2CDelay();                         /* let SCL get stabilized High       */

   while(!I2CGetSCL());                /* wait until SCL-line is released   */

   I2CDelay();
   State = I2CGetSDA();                /* read SDA state                    */
   I2CPutSCL(0);                       /* disable SDA reading (SDA is now   */
                                       /* clocked)                          */
   I2CDelay();
   return(State);                      /* return state */
}

/****************************************************************************/
/* sends a byte to I2C-device                                               */

void I2CWriteByte(BYTE TransferData) {
   BYTE Loop;



   for(Loop = 0; Loop <= 7; Loop++) {
      I2CSendBit((TransferData & 0x80) == 0x80);
      TransferData = (TransferData << 1);
   }
}

/****************************************************************************/
/* reads a byte from I2C-device                                             */

BYTE I2CReadByte(void) {
   BYTE Value, Loop;



   Value = 0;

   for(Loop = 0; Loop <= 7; Loop++) {
      Value = (Value << 1);            /* 1. shift doesn't affect (value=0) */
      Value = Value | I2CReceiveBit();
   }

   return(Value);                      /* return byte read                  */
}

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

/* ----- end I2C_BUS.C66 ----- */