//==================================================================
void    I2CReStart(_I2C_CB* pCB)
    {
    I2CIdle(pCB);               // Wait for bus to be Idle
    //-----------------------------------------------------
    I2CpCON(pCB)->RSEN = 1;     // Initiate Repeated Start on SDA
                                // and SCL pins
    }
//==================================================================
// Internal (local) components of Synchronous I2C interface
//==================================================================
void I2CIdle(_I2C_CB* pCB)
    {
    I2C_CONBITS*    pCON    = I2CpCON(pCB);
    I2C_STATBITS*    pSTAT    = I2CpSTAT(pCB);
    /* Wait until I2C Bus is Inactive */
    //----------------------------------
    while
        (
        pCON->SEN || pCON->PEN || pCON->RCEN || pCON->RSEN || pCON->ACKEN  || pSTAT->TRSTAT
        );
    return;    
    }
//==================================================================
uint    I2CMasterReadByte(_I2C_CB* pCB, byte* data, uint Flag)
    {
    I2CIdle(pCB);                // Wait for bus to be Idle
    //-----------------------------------------------------
    I2C_CONBITS*    pCON    = I2CpCON(pCB);
    I2C_STATBITS*    pSTAT    = I2CpSTAT(pCB);
    //-----------------------------------------------------
    *(pCB->pI2C_STAT)    = 0;    // Reset Status byte
    //-----------------------------------------------------
    pCON->RCEN = 1;             // Put module into READ mode
    while(pCON->RCEN);          // Wait until byte is received
    if (pSTAT->I2COV)           // Overflow?
        {
        pSTAT->I2COV = 0;       // Reset overflow condition
        return I2CRC_OVFL;      // return error...
        }
    if (pSTAT->BCL)             // Bus collision?
        {
        pSTAT->BCL = 0;         // Reset bus collision condition
        return I2CRC_BCOL;      // return error...
        }
    while(!pSTAT->RBF);         // Wait for byte to be ready in receive
                                // register - Excessive check, but...
    (*data) = *(pCB->pI2C_RCV);    // return byte to caller
    //--------------------------------
    // Set Acknowledge (ACK or NACK) code
    //--------------------------------
    if(Flag == 0)              
        // If last char, generate NACK sequence
        pCON->ACKDT = 1;
    else                         
        // For other chars,generate ACK sequence
        pCON->ACKDT = 0;
    //--------------------------------
    pCON->ACKEN = 1;            // Initiate Acknowledge Sequence
    //--------------------------------
    I2CIdle(pCB);               // Ensure module is idle
                                // ACK/NACK transmitted
    //------------------------------------------------------------------
    return    I2CRC_OK;         // Byte successfully transferred
    }
//============================================================
// Asynchronous I2C API (visible externally) component
//============================================================
// <editor-fold defaultstate="collapsed" desc="uint	I2CAsyncStart(uint SubscrID)">
uint	I2CAsyncStart(uint I2Cx, I2CAsyncRqst* Rqst)
	{
	if (!_I2C_Init)		return I2CRC_NRDY;
	//=========================================================
	// Obtain references to proper Control Blocks and registers
	//---------------------------------------------------------
	_I2C_CB*		pCB;
	if ( NULL == (pCB = I2CpCB(I2Cx)) )		return I2CRC_NOTA;
	//---------------------------------------------------------
	I2C_CONBITS*	pCON	= I2CpCON(pCB);
	I2C_STATBITS*	pSTAT	= I2CpSTAT(pCB);
	//=========================================================
	// Validate run-time conditions
	//---------------------------------------------------------
	int		i;
	//---------------------------------------------------------
	uint	RC		= I2CRC_OK;
	BYTE	CPU_IPL;
	//---------------------------------------------------------
	// Enter I2C (and related modules) CRITICAL SECTION
	//---------------------------------------------------------
	SET_AND_SAVE_CPU_IPL(CPU_IPL, _I2C_IL);
		{
		//-----------------------------------------------------------
		// New request...
		//-----------------------------------------------------------
		if (pCB->_I2C_CallBack)
			// There is an active request being processed -
			// several checks need to be implemented...
			{
			//------------------------------------------------------------
			// First, check whether this request already active
			//---------------------------------------------------------
			if (	pCB->_I2C_CallBack		== Rqst->CallBack
				&&	pCB->_I2C_CallBackArg	== Rqst->CallBackArg )
				{
				// Duplicate request...
				RC = I2CRC_RQSTA;
				goto Finally;
				}
			//-------------------------------------------------------
			// Second, check whether this request already queued
			//-------------------------------------------------------
			for (i = 0; i < I2CMaxAsyncRqst; i++)
				{
				if (	pCB->_I2CRqstQueue[i].CallBack		== Rqst->CallBack
					 &&
						pCB->_I2CRqstQueue[i].CallBackArg	== Rqst->CallBackArg )
					{
					// Request is already in the queue...
					RC = I2CRC_OK;
					goto Finally;
					}
				}
			//-------------------------------------------------------
			// Third, try to add this request to the queue
			//-------------------------------------------------------
			for (i = 0; i < I2CMaxAsyncRqst; i++)
				{
				if (	NULL == pCB->_I2CRqstQueue[i].CallBack )
					{
					// Free slot found! Add request to the queue
					pCB->_I2CRqstQueue[i].CallBack		= Rqst->CallBack;
					pCB->_I2CRqstQueue[i].CallBackArg	= Rqst->CallBackArg;
					//-------------------------------------------
					RC = I2CRC_OK;
					goto Finally;
					}
				}
			// No free slots available in the queue...
			RC = I2CRC_QFULL;
			goto Finally;
			}
		else
			// No Active Asynchronous requests now - let's try
			// to innitiate a new Asynchronous request
			{
			// Frist, let's check bus condition
			if (0 == pSTAT->P)
				{
				// Bus is not idle as the last status is not "Stop"
				RC = I2CRC_BUSY;	// // Bus is busy...
				goto Finally;
				}
			//--------------------------------------------
			// Bus condition validated, proceed with activating
			// Async operation
			//---------------------------------------------------------
			// Store in the I2C Library control block address of the
			// callback function of the current bus owner and
			// respective callback parameter.
			//---------------------------------------------------------
			pCB->_I2C_CallBack		= Rqst->CallBack;
			// NOTE: non-NULL value of pCB->_I2C_CallBack field
			//		 also serves as a FLAG indicating that bus is
			//		 busy and tested for in I2CGetStatus(...) routine
			pCB->_I2C_CallBackArg	= Rqst->CallBackArg;
			//--------------------------------------------------------
			I2CSetIF(I2Cx, 0); 	// Clear  I2C Master interrupt flag
			I2CSetIE(I2Cx, 1);	// Enable I2C Master interrupt
			//--------------------------------------------------------
			// Initiate Start on I2C bus
			pCON->SEN = 1;
			// NOTE: because I2C bus is being allocated to the client,
			//		 from now until the asynchronous operation completes
			//		 I2C interrupts will be routed to client's callback
			//		 routine.
			RC = I2CRC_OK;
			goto Finally;
			}
		}
Finally:
	//---------------------------------------------------------
	// Leave I2C CRITICAL SECTION
	//---------------------------------------------------------
  	RESTORE_CPU_IPL(CPU_IPL);
	//=========================================================
	return RC;		
	}
//************************************************************
// Synchronous READ - I2C API (visible externally) component
//************************************************************
uint	I2CSyncRead(	uint	I2Cx,
						byte 	DevAddr, 
						byte	Register,
						byte* 	Buffer,
						uint  	BufLen )
	{
	if (!_I2C_Init)		return I2CRC_NRDY;
	//=========================================================
	// Obtain references to proper Control Blocks and registers
	//---------------------------------------------------------
	_I2C_CB*		pCB;
	if ( NULL == (pCB = I2CpCB(I2Cx)) )		return I2CRC_NOTA;
	//---------------------------------------------------------
	I2C_CONBITS*	pCON	= I2CpCON(pCB);
	I2C_STATBITS*	pSTAT	= I2CpSTAT(pCB);
	//=========================================================
	// Validate run-time conditions
	//---------------------------------------------------------
	uint	RC;
	BYTE	CPU_IPL;
	uint	RetryCount	= 0;

	//---------------------------------------------------------
	// Enter I2C (and related modules) CRITICAL SECTION
	//---------------------------------------------------------
Retry:
	RC	= I2CRC_OK;
	//------------------------------------
	SET_AND_SAVE_CPU_IPL(CPU_IPL, _I2C_IL);
		{
		if (I2CRC_OK == (RC=I2CGetStatus(pCB, pCON, pSTAT)))
			//---------------------------------------------------------
			// Set Flag indicating Synchronous operation is in progress
			//---------------------------------------------------------
			pCB->_I2C_SBSY	= 1;	// Should be cleared at exit
		}  
	//---------------------------------------------------------
	// Leave I2C CRITICAL SECTION
	//---------------------------------------------------------
  	RESTORE_CPU_IPL(CPU_IPL);
	//=========================================================
	switch (RC)	
		{
		case	I2CRC_OK:
			break;		// Run-time conditions are OK
		//-------------------------------------------
		case	I2CRC_ABSY:
		case	I2CRC_SBSY:
		case	I2CRC_BUSY:
			// Situation could be helped by delay/retry
			if (RetryCount < I2C_BUSYRetry)
				{
				TMRDelayTicks(1);	// Small delay ~125 usec
				RetryCount++;
				goto Retry;					// Attempt retry
				}
			else;		// Fall through to "default"
		//-------------------------------------------
		default:
			return RC;	// Run-time conditions are not met
		}
	//=========================================================

	//---------------------------------------------------------
	// Event sequence to accomplish simple READ from the target
	// slave device in the MASTER mode:
	//	1. Assert a Start condition on SDAx and SCLx.
	//	2. Send the I2C device address byte to the slave with a
	//	   read indication ("1" in the LSB of address)
	//	3. Wait for and verify an Acknowledge from the slave.
	//	4. Assert receiving state (RCEN = 1).
	//	5. Receive byte and send ACK to the slave.
	//	6. Repeat steps 4 and 5 for every byte in a message, 
	//	   except the last - after receiving last byte send NACK.
	//	7. Generate a Stop condition on SDAx and SCLx.
	//---------------------------------------------------------
	// Prepare READ and WRITE forms of device address
	byte	i2cAddrW	= DevAddr & 0xFE;	// Set WRITE cond.
	byte	i2cAddrR	= DevAddr | 0x01;	// Set READ cond.
	//---------------------------------------------------------
	RC			= I2CRC_OK;
	//---------------------------------------------------------
	I2CStart(pCB);		// Signal START on SDA and SCL pins
	//---------------------------------------------------------
	// Send Device WRITE address
	//---------------------------------------------------------
	RC = I2CMasterWriteByte(pCB, i2cAddrW);
	if (RC != I2CRC_OK)	goto Finally;
	//---------------------------------------------------------
	// Send Register address (as data))
	//---------------------------------------------------------
	RC = I2CMasterWriteByte(pCB, Register);
	if (RC != I2CRC_OK)	goto Finally;
	//---------------------------------------------------------
	I2CReStart(pCB);	// Signal Repeated-START on SDA and SCL pins
	//---------------------------------------------------------
	// Send Device READ address
	//---------------------------------------------------------
	RC = I2CMasterWriteByte(pCB, i2cAddrR);
	if (RC != I2CRC_OK)	goto Finally;
	//---------------------------------------------------------
	// Receive data
	//---------------------------------------------------------
	pSTAT->I2COV = 0;	// Clear receive OVERFLOW bit, if any
	//---------------------------------------------------------
	uint	BufPos;
	uint	BufCnt	= BufLen;
	//---------------------------------------------------------
	for (BufPos = 0; BufPos < BufLen; BufPos++)
		{
		BufCnt--;	// Used as a FLAG (BufCnt=0) to indicate last byte
		//---------------------------------------------------------
		RC = I2CMasterReadByte(pCB, Buffer, BufCnt);
		if (RC != I2CRC_OK)
			break;			// Error...
		Buffer++;
		}
	//---------------------------------------------------------
Finally:
	I2CStop(pCB);			// Signal STOP on SDA and SCL pins
	//---------------------------------------------------------
	pCB->_I2C_SBSY	= 0;	// Clear SYNC flag
	return RC;				// Return last Error Code...
	}
//==================================================================
void    I2CStop(_I2C_CB* pCB)
    {
    I2CIdle(pCB);               // Wait for bus to be Idle
    //-----------------------------------------------------
    I2CpCON(pCB)->PEN = 1;      // Initiate Stop on SDA and SCL pins
    }