

/***********************************************************************
MODULE:    I2C
VERSION:   1.03
CONTAINS:  Routines for controlling the I2C Peripheral on the Philips
           P89LPC936
COPYRIGHT: Embedded Systems Academy, Inc. - www.esacademy.com
LICENSE:   May be freely used in commercial and non-commercial code
           without royalties provided this copyright notice remains
           in this file and unaltered
WARNING:   IF THIS FILE IS REGENERATED BY CODE ARCHITECT ANY CHANGES
           MADE WILL BE LOST. WHERE POSSIBLE USE ONLY CODE ARCHITECT
           TO CHANGE THE CONTENTS OF THIS FILE
GENERATED: On "Jun 09 2008" at "11:54:01" by Code Architect 2.12
***********************************************************************/

// SFR description needs to be included
#include "reg936.h"
#include "i2c.h"
#include "i2c_callback.h"

// MODULE INTERNAL VARIABLES

// internal copy of slave address
static unsigned char mslaveaddress;
// I2C transfer status
static unsigned char mi2cstatus;
// current number of byte transferred
static unsigned int mbytenum;

// MODULE INTERNAL FUNCTIONS

/***********************************************************************
DESC:    I2C interrupt service routine. handles the transfer and
         calls the relevent callback functions. Changes mi2cstatus
RETURNS: Nothing
************************************************************************/
void i2c_isr
  (
  void
  ) interrupt 6 using 1
{
  unsigned char status;

  status = I2STAT & 0xF8;

  switch(status)
  {
    case 0x08:
    case 0x10:
      I2DAT = mslaveaddress;
      STA = 0;
      STO = 0;
      mbytenum = 0;
      break;

    // MASTER TRANSMITTER
    // ACK for slave address + W
    case 0x18:
      I2DAT = i2c_master_getbyte(mbytenum);
      STA = 0;
      STO = 0;
      break;
    // no ACK for slave address + W
    case 0x20:
      // stop condition
      STA = 0;
      STO = 1;
      mi2cstatus = I2C_ERROR;
      i2c_transfer_finished();
      break;
    // ACK for data byte
    case 0x28:
      if (i2c_master_islasttxbyte(mbytenum))
      {
        // stop condition
        STA = 0;
        STO = 1;
        mi2cstatus = I2C_OK;
        i2c_transfer_finished();
      }
      else
      {
        mbytenum++;
        I2DAT = i2c_master_getbyte(mbytenum);
        STA = 0;
        STO = 0;
      } // if
      break;
    // no ACK for data byte
    case 0x30:
      // stop condition
      STA = 0;
      STO = 1;
      mi2cstatus = I2C_ERROR;
      i2c_transfer_finished();
      break;
    // arbitration lost
    case 0x38:
      // start condition - try again
      STA = 1;
      STO = 0;
      break;

    // MASTER RECEIVER
    // ACK for slave address + R
    case 0x40:
      STA = 0;
      STO = 0;
      if (i2c_master_islastrxbyte(mbytenum))
      {
        // return NACK for data byte
        AA = 0;
      }
      else
      {
        // return ACK for data byte
        AA = 1;
      } // if
      break;
    // no ACK for slave address + R
    case 0x48:
      STA = 0;
      // stop condition
      STO = 1;
      mi2cstatus = I2C_ERROR;
      i2c_transfer_finished();
      break;
    // ACK for data byte
    case 0x50:
      i2c_master_receivedbyte(mbytenum, I2DAT);
      mbytenum++;
      STA = 0;
      STO = 0;
      if (i2c_master_islastrxbyte(mbytenum))
      {
        // return NACK for next data byte
        AA = 0;
      }
      else
      {
        // return ACK for next data byte
        AA = 1;
      } // if
      break;
    // no ACK for data byte
    case 0x58:
      i2c_master_receivedbyte(mbytenum, I2DAT);
      STA = 0;
      // stop condition
      STO = 1;
      mi2cstatus = I2C_OK;
      i2c_transfer_finished();
      break;

    // SLAVE RECEIVER
    // slave address + W received
    // or general call address received
    // or lost arbitration, slave address + W received
    // or lost arbitration, general call address received
    case 0x60:
    case 0x68:
    case 0x70:
    case 0x78:
      STA = 0;
      STO = 0;
      mbytenum = 0;
      if (i2c_slave_islastrxbyte(mbytenum))
      {
        // we don't want to receive more bytes
        AA = 0;
      }
      else
      {
        // want to receive more bytes
        AA = 1;
      } // if
      break;
    // data byte received
    case 0x80:
    case 0x90:
      i2c_slave_receivedbyte(mbytenum, I2DAT);
      mbytenum++;
      STA = 0;
      STO = 0;
      if (i2c_slave_islastrxbyte(mbytenum))
      {
        // we don't want to receive more bytes
        AA = 0;
      }
      else
      {
        // want to receive more bytes
        AA = 1;
      } // if
      break;
    // data received, NACK returned
    case 0x88:
    case 0x98:
      i2c_slave_receivedbyte(mbytenum, I2DAT);
      mbytenum++;
      mi2cstatus = I2C_OK;
      STA = 0;
      STO = 0;
      // go back to slave mode waiting to be addressed
      AA = 1;
      i2c_transfer_finished();
      break;
    // stop condition received
    case 0xA0:
      mi2cstatus = I2C_OK;
      STA = 0;
      STO = 0;
      // go back to slave mode waiting to be addressed
      AA = 1;
      i2c_transfer_finished();
      break;

    // SLAVE TRANSMITTER
    // slave address + R received, ACK returned
    // arbitration lost, slave address + R received, ACK returned
    // data byte transmitted, ACK received
    case 0xA8:
    case 0xB0:
      mbytenum = 0;
    case 0xB8:
      I2DAT = i2c_slave_getbyte(mbytenum);
      STA = 0;
      STO = 0;
      if (i2c_slave_islasttxbyte(mbytenum))
      {
        // no more data bytes to transmit
        AA = 0;
      }
      else
      {
        // more data bytes to transmit
        AA = 1;
      } // if
      mbytenum++;
      break;
    // data byte transmitted, NACK received
    // last data byte transmitted, NACK received
    case 0xC0:
    case 0xC8:
      mi2cstatus = I2C_OK;
      STA = 0;
      STO = 0;
      // go back to slave move waiting to be addressed
      AA = 1;
      i2c_transfer_finished();
      break;

    // unknown state
    default:
      mi2cstatus = I2C_ERROR;
      STA = 0;
      STO = 0;
      // go back to slave mode waiting to be addressed
      AA = 1;
      i2c_transfer_finished();
      break;
  } // switch - status

  // clear interrupt flag
  SI = 0;
} // i2c_isr

// EXTERNAL INTERFACE FUNCTIONS

/***********************************************************************
DESC:    initializes the I2C peripheral and interrupt
         sets the device's I2C address and whether it will
         respond to the general call address
         Uses a data rate of 100kHz
RETURNS: Nothing
************************************************************************/
void i2c_init
  (
  unsigned char address,  // The 7-bit I2C address to use
  bit generalcall         // 1 = respond to general call, 0 = ignore
                          // general call
  )
{
  // set pins to open-drain
  P1M1 |= 0x0C;
  P1M2 |= 0x0C;
  EA = 1;

  // configure I2C address
  I2ADR = 0x00;
  I2ADR = address << 1;
  if (generalcall)
  {
    I2ADR |= 0x01;
  } // if

  // configure internal SCL generator
  I2SCLH = 0x1E;
  I2SCLL = 0x1E;

  // configure I2C interface
  // use internal SCL generator
  I2CON = 0x44;

  // set interrupt priority to 0
  IP1 &= ~0x01;
  IP1H &= ~0x01;

  // initial status
  mi2cstatus = I2C_IDLE;

  // enable interrupt
  EI2C = 1;
} // i2c_init

/***********************************************************************
DESC:    attempts to start an I2C transmission as a master to a
         device with a specific address. If successful then
         master callback functions will be called to handle the
         data for the transfer
RETURNS: I2C_BUSYTX if I2C is already busy transmitting data
         I2C_BUSYRX if I2C is already busy receiving data
         I2C_OK if transmission started
NOTES:   Can check if I2C busy by ANDing returned value with
         I2C_BUSY

unsigned char i2c_transmit  (unsigned char address)

************************************************************************/
unsigned char i2c_transmit
  (
  unsigned char address    // address of device to transmit to
  )
{
  // if already busy then return current status
  if (mi2cstatus & I2C_BUSY) return mi2cstatus;

  // now we are busy performing a transfer
  mi2cstatus = I2C_BUSYTX;

  // store slave address + W for use in ISR
  mslaveaddress = address << 1;

  // transmit start condition
  STA = 1;

  // transmission started
  return I2C_OK;
} // i2c_transmit

/***********************************************************************
DESC:    attempts to start an I2C reception as a master from a
         device with a specific address. If successful then
         master callback functions will be called to handle the
         data for the transfer
RETURNS: I2C_BUSYTX if I2C is already busy transmitting data
         I2C_BUSYRX if I2C is already busy receiving data
         I2C_OK if reception started
NOTES:   Can check if I2C busy by ANDing returned value with
         I2C_BUSY
************************************************************************/
unsigned char i2c_receive
  (
  unsigned char address    // address of device to receive from
  )
{
  // if already busy then return current status
  if (mi2cstatus & I2C_BUSY) return mi2cstatus;

  // now we are busy performing a transfer
  mi2cstatus = I2C_BUSYRX;

  // store slave address + R for use in ISR
  mslaveaddress = (address << 1) | 0x01;

  // transmit start condition
  STA = 1;

  // reception started
  return I2C_OK;
} // i2c_receive

/***********************************************************************
DESC:    returns the current status of the I2C peripheral.
         allows polling to be used to determine if a transfer
         has been completed
RETURNS: I2C_BUSYTX if I2C is already busy transmitting data
         I2C_BUSYRX if I2C is already busy receiving data
         I2C_OK if no transfer in progress and last transfer
         was successful
         I2C_ERROR if no transfer in progress and last transfer
         encountered an error
         I2C_IDLE if no transfer in progress
NOTES:   Can check if I2C busy by ANDing returned value with
         I2C_BUSY. Example:
         i2c_transmit(0x00);
         while (i2c_getstatus() & I2C_BUSY);
         if (i2c_getstatus() == I2C_OK)
         ...
************************************************************************/
unsigned char i2c_getstatus
  (
  void
  )
{
  // wait until any interrupt completed
  while(SI);

  // return status
  return mi2cstatus;
} // i2c_getstatus

