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

      i2cl.c     ( Light version )
      v1.0 (17/04/2003)

      Software implementation of the I2C protocol
      Light Low level I2C I/O functions for HI-TECH PIC C - master mode only.

      No ACK control after write
      No Check of different levels during bus acces

      Input and output may be on the same resister. You need to edit i2c.h to 
      define SCL (clock) and SDA (data) pin.

      Written by Philippe Corbes <philippe.corbes@laposte.net>
      This code is free for personal use. 
      You need my agreement for a commercial use. 


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

  #include    <pic.h>
  #include    "delay.h"
  #include    "i2cl.h"




  void
  i2c_start(void)
  {
      /*----------------------------------------------------------------
       *  Send (re)start condition
       *  No control of lines
       *             ____________
       *  scl       /            \
       *      _____/              \
       *        __________
       *  sda \/          \ 
       *      /            \_______
       *
       *----------------------------------------------------------------
       */

      DelayUs(I2C_TBUF);          /* bus free time before a restart */

      SDA_HIGH();                 /* data is high */
      DelayUs(I2C_TSU_DAT);

      SCL_HIGH();                 /* clock is high */
      DelayUs(I2C_TSU_STA);

      SDA_LOW();                  /* data high->low transition */
      DelayUs(I2C_THD_STA);

      SCL_LOW();                  /* clock low */
      return;
  }


  void
  i2c_stop(void)
  {
      /*----------------------------------------------------------------
       *  Send stop condition
       *  It's possible to begin a new transaction just after.
       *                   ______________
       *  scl \           /
       *      /\_________/
       *      ___               _________
       *  sda    \             /
       *      ___/\___________/
       *
       *----------------------------------------------------------------
       */

      SCL_LOW();              /* cock is low */
      DelayUs(I2C_TSU_DAT);

      SDA_LOW();              /* data is low first */
      DelayUs(I2C_TLOW);      /* clock low time */

      SCL_HIGH();             /* cock is high */
      DelayUs(I2C_TSU_STO);

      SDA_HIGH();             /* float data high */

      return;
  }


  void
  i2c_write(unsigned char abyte)
  {
      /*----------------------------------------------------------------
       *  Send a byte to the slave and acknowledges the transfer
       *  Return: - I2C_ERROR on bus error
       *          - I2C_ACK_TIMEOUT on ack timeout
       *          - I2C_OK if ack received from slave
       *
       *----------------------------------------------------------------
       *  SEND a byte to the slave
       *  Exit with I2C_ERROR on bus error
       *            ________            _____ .. __
       *  scl      /        \          /           \
       *      ____/          \________/             \___
       *       __________________  __________ .. _______
       *  sda /                  \/
       *      \__________________/\__________ .. _______
       *      Write Bit 7         Write Bit 6 .. bit0
       * 
       *  THEN If Byte sent, Check for an acknowledge
       *              ________   
       *  scl        /        \ 
       *      ______/          \
       *        ________________
       *  sda  /                  
       *      /
       *                     X Read Ack
       *----------------------------------------------------------------
       */

      int i;

      /* SEND a byte to the slave */
      for(i=7; i>=0; i--) {

          /* data hold time = 0, send data now */
          if ((abyte>>i)&0x01) {      /* bit to send */
              SDA_HIGH();
          } else {
              SDA_LOW();
          }
          DelayUs(I2C_TSU_DAT);

          SCL_HIGH();                 /* clock high */

          DelayUs(I2C_THIGH);         /* clock high time */

          SCL_LOW();                  /* clock low */
          DelayUs(I2C_TLOW - I2C_TSU_DAT);

      }

      /* THEN If Byte sent, Check for an acknowledge */

      SDA_HIGH();                     /* disable data line - listen for ack */
      DelayUs(I2C_TSU_DAT);           /* SCL low to data out valid */

      SCL_HIGH();                     /* float clock high */

      DelayUs(I2C_THIGH);

      SCL_LOW();                      /* clock low */

      return ;
  }


  unsigned char
  i2c_read(unsigned char more)
  {
      /*----------------------------------------------------------------
       *  Read a byte from the slave and acknowledges the transfer
       *  Return: - the byte
       *          - I2C_ERROR on bus error
       *----------------------------------------------------------------
       *  READ a byte from the slave
       *    - returns the byte, or I2C_ERROR if a bus error
       *             ________            ____ .. __
       *  scl       /        \          /          \
       *      _____/          \________/            \___
       *        _____________________________ .. _______
       *  sda  /                                      
       *      /             X Read bit 7          X Read bit 0            
       *
       *  THEN Send an (~)acknowledge to the slave
       *    - status of I2C_LAST implies this is the last byte to be sent
       *            ________              ________
       *  scl      /        \            /        \
       *      ____/          \      ____/          \
       *                        OR    ______________
       *  sda \                      /                 
       *       \______________      /
       *      Write Ack         OR  Write Nack
       *----------------------------------------------------------------
       */

      unsigned char i;
      unsigned char abyte;


      /* READ a byte from the slave */
      SDA_HIGH();                     /* release data line */

      for(i=0; i<8; i++) {
          DelayUs(I2C_TSU_DAT);       /* min clock low  period */

          SCL_HIGH();                 /* float clock high */

          DelayUs(I2C_THIGH);
          abyte = abyte << 1;         /* read the next bit */
          abyte |= SDA;

          SCL_LOW();
          DelayUs(I2C_TLOW - I2C_TSU_DAT);
      }

      /* THEN Send an (~)acknowledge to the slave  */
      if ( more & 0x01) {
          SDA_LOW();                  /* drive line low -> more to come */
      } else {
          SDA_HIGH();
      }
      DelayUs(I2C_TSU_DAT);

      SCL_HIGH();                     /* float clock high */
      DelayUs(I2C_THIGH);

      SCL_LOW();

      return abyte;
  }