/*=================================< i2c.c >==================================*/
//                                                                      
//    Title:      I2C DRIVER UNIT
// 	Last Saved: 21 Apr 2003 11:41:18
//    Written by: JONATHAN GUY
//    Target:     LH79520
//    Compiler:   gcc
//		Copyright:	Subject to the Revely Open Source License (see readme.txt)
//
//    Description:
//		These functions bit bang [bit bash] a simple I2C port on the LH79520 Micro
//		Since the 79520 deoesn't have quasi-bidirectional I/O's, two I/O's have 
//		been combined to the SDA signal.
//
//		Port H.2 = SDA input
//		Port H.3 = SCL output
//		Port H.4 = SDA output
//
//    This is only a simple I2C implementation and the following limitations 
//    apply:
//       - Micro must be master.
//       - Only one master is allowed.
//       - Slave can not hold SCL low to insert a wait.
//       - Function can transmit bytes then receive bytes but not the
//         other way around.
//    I2C control is performed by:
//       - I2cInitialise to call I2C driver to set up I/O pins.
//       - I2cTalk a versatile function which can write and receive up to 
//         255 bytes from any 8 bit I2C address.
//
//    History:    
//    1.0   JAG   Initial release.  
//    1.1   JAG   Changed target to 68332G. TPU Functions changed to QOM 
//                (output only).
//    1.2   JAG   Now uses port E instead of TPU.
//		1.3	JAG	Now ported to Sharp LH79520
//
//
/*============================================================================*/
#include <regs.h>
#include <stddefs.h>
#include <i2c.h>

/*==========================< CONSTANT DEFINITIONS >==========================*/

/*========================< LOCAL FUNCTION PROTOTYPES >=======================*/
void I2cDelay(void);
void I2cSDALow(void);
void I2cSDAHigh(void);
void I2cSCLLow(void);
void I2cSCLHigh(void);
void I2cSetSDAToInput(void);
void I2cSetSDAToOutput(void);
char I2cGetSDAState(void);

/*=============================< UNIT VARIABLES >=============================*/


/*============================< GLOBAL FUNCTIONS >============================*/

/*------------------------------< I2cInitialise >-----------------------------*/
BYTE I2cInitialise(BYTE bSpeed)
{
   BYTE bDat=0xff; 
   
 	MEMMUX &= 0xfffff8ff;	// Port H not chip select functions
 	PORTH_DDR = 0x00000008;		// SCL is output
 	PORTH_DATA = 0x00000018;	// Both are HI
 	I2cSCLHigh();
 	I2cSetSDAToInput();
 	
 	
   /* Talk to ???? to confirm bus OK */
   //return I2cTalk(STATUS_LEDS, 1, 0, &bDat);
   
   return 1;
}
 
/*--------------------------------< I2cTalk >---------------------------------*/
BYTE I2cTalk(  BYTE bAddr, 
               BYTE bTxCount, 
               BYTE bRxCount,  
               PBYTE pBuffer)
{
   BYTE bStatus; 
   PBYTE pTxbuff;
   PBYTE pRxbuff;
   BYTE bTxbyte, bRxbyte, bBitnum, bTemp;
                      
   //if (bStatus != I2C_OFF)    // Prevent re-entrant calling
   //   return I2C_OFF;
   bStatus = I2C_IDLE;
   pTxbuff = pBuffer;
   pRxbuff = pBuffer;
  
   while ((bTxCount > 0) || (bRxCount > 0))
   {
      if (bStatus == I2C_IDLE)
      {
         /* Generate [re]start condition */
         I2cSCLHigh();
         I2cDelay();
         I2cSDALow();
      }
      if (bStatus == I2C_START)
      {
         if (bTxCount == 0)
            bAddr |= 0x01;    /* Set R/W bit to Read */   
         bTxbyte = bAddr;     /* Follow start condition with address Tx */
      }
      /* If in Tx mode then prepare data byte to send */
      if (bStatus == I2C_TXBYTE) 
      {
         bTxbyte = *pTxbuff;  /* Load Tx byte */ 
         pTxbuff++;           /* and point to next */
         bTxCount--;
      }
      if ((bStatus == I2C_START) || (bStatus == I2C_TXBYTE))
      {
         /* Send a byte (could be data or address) */
         for (bBitnum=1; bBitnum < 9; bBitnum++)
         {
            I2cSCLLow();
            I2cDelay();
            if ((bTxbyte & 0x80) !=0)    /* Is bit to Tx a 'one'? */
               I2cSDAHigh();
            else
               I2cSDALow();
            I2cDelay();
            I2cSCLHigh();
            I2cDelay();
            bTxbyte <<= 1; /* Shift next bit to Tx into MSB position */
            I2cSCLLow();
         }
         /* Request acknowledge */
         I2cSetSDAToInput();
         I2cSDAHigh();		// Activate pull-up
         I2cDelay();
         I2cSCLHigh();
         I2cDelay();
         //if (I2cGetSDAState() == 1)    /* is SDA-Rx bit a 'one' (no ack) ? */
         //   bStatus = I2C_NOACK;             /* if so signal error */  
         I2cSCLLow();
         I2cSDAHigh();
      }
      if (bStatus == I2C_RXBYTE)
      {
         I2cSetSDAToInput();      
         I2cSDAHigh();		// Activate pull-up
         /* Receive a byte */
         for (bBitnum=1; bBitnum < 9; bBitnum++)
         {
            I2cSCLLow();          /* take SCL low and wait for slave to output data on SDA */       
            I2cDelay();
            I2cSCLHigh();
            I2cDelay();
            bRxbyte <<= 1;    /* shift bits up ready for next Rx bit */
            if (I2cGetSDAState() == 1)    /* is SDA-Rx bit a 'one' ? */
               bRxbyte |= 0x01;                 /* if so set LSB */
            I2cSCLLow();
         }
         I2cSDAHigh();
         bRxCount--;
       	/* send acknowledge */  
         I2cDelay();
         I2cSCLHigh(); 
         I2cSCLLow();
   
         *pRxbuff = bRxbyte;   /* Store Rx'd byte in buffer */
         pRxbuff++;            /* and point to next location */                        
      }
      /* Update Status */
      switch (bStatus)
      { 
         case I2C_IDLE:    bStatus = I2C_START;
                           break;
         case I2C_START:   bStatus = I2C_ADDR;
                           break;
         case I2C_ADDR:    if (bTxCount==0)           /* If no data to send */
                              bStatus = I2C_RXBYTE;   /* then rx data */
                           else
                              bStatus = I2C_TXBYTE;   /* else tx more data */
                           break;                              
         case I2C_TXBYTE:  if (bTxCount==0)           /* switch from tx to rx */
                              bStatus = I2C_IDLE;     /* and send repeated start */
                           else
                              bStatus = I2C_ADDR;     /* ready for next tx */
                           break;
         case I2C_RXBYTE:  bStatus = I2C_ADDR;        /* ready for next rx */
                           break;
         case I2C_NOACK:   bTxCount=0;       /* Error so force exit */
                           bRxCount=0;
                           
      }
   } 
   /* Generate Stop condition */
   I2cSDALow();
   I2cDelay();
   I2cSCLHigh();
   I2cDelay();
   I2cSDAHigh();
   return bStatus;
}



/*=============================< LOCAL FUNCTIONS >============================*/


/*--------------------------------< I2cDelay >--------------------------------*/
void I2cDelay(void)
{
   asm ( 
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     "nop;"
     );
}

/*--------------------------------< I2cSDALow >-------------------------------*/
void I2cSDALow(void)
{
   PORTH_DDR |= 0x00000010;		// Port H.4 is output
   PORTH_DATA &= 0xffffffef;	// Port H.4 is low
}

/*-------------------------------< I2cSDAHigh >-------------------------------*/
void I2cSDAHigh(void)
{
	PORTH_DDR &= 0xffffffef;		// Port H.4 is input
}

/*--------------------------------< I2cSCLLow >-------------------------------*/
void I2cSCLLow(void)
{
   PORTH_DATA &= 0xfffffff7;	// Port H.3 is low
}

/*--------------------------------< I2cSCLHigh >-------------------------------*/
void I2cSCLHigh(void)
{
   PORTH_DATA |= 0x00000008;	// Port H.3 is high
   
}

/*------------------------------< I2cSetSDAToInput >---------------------------*/
void I2cSetSDAToInput(void)
{
   PORTH_DDR &= 0xffffffef;		// Port H.4 is input
} 

/*-----------------------------< I2cSetSDAToOutput >---------------------------*/
void I2cSetSDAToOutput(void)
{
   PORTH_DDR |= 0x00000010;		// Port H.4 is output
}   

/*-------------------------------< I2cGetSDAState >----------------------------*/
char I2cGetSDAState(void)
{
   if (PORTH_DATA & 0x00000004)  // Read Port H.2
      return TRUE;
   else
      return FALSE;
}

/* end of i2c.c */







