//================================================================== // 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 }
//================================================================== uint I2CMasterWriteByte(_I2C_CB* pCB, byte data) { I2CIdle(pCB); // Wait for bus to be Idle //----------------------------------------------------- I2C_STATBITS* pSTAT = I2CpSTAT(pCB); //----------------------------------------------------- while(pSTAT->TBF); // Wait till transmit register // is empty (available) //----------------------------------------------------- *(pCB->pI2C_STAT) = 0; // Reset Status byte //----------------------------------------------------- *(pCB->pI2C_TRN) = data; // Put data byte into transmit register if(pSTAT->IWCOL) // Check for write collision return I2CRC_WCOL; // Return error... while(pSTAT->TRSTAT); // Wait until write cycle is complete //----------------------------------------------------- I2CIdle(pCB); // Ensure module is idle //----------------------------------------------------- if ( pSTAT->ACKSTAT ) // Test for ACK condition received return I2CRC_NACK; // NACK... //------------------------------------------------------------------ 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... }