//***************************************************************************** // i2c_if.c // // I2C interface APIs. Operates in a polled mode. // // Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ // // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the // distribution. // // Neither the name of Texas Instruments Incorporated nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //***************************************************************************** // Standard includes #include #include #include // Driverlib includes #include "hw_types.h" #include "hw_memmap.h" #include "hw_ints.h" #include "hw_i2c.h" #include "i2c.h" #include "pin.h" #include "rom.h" #include "rom_map.h" #include "prcm.h" // Common interface include #include "i2c_if.h" //***************************************************************************** // MACRO DEFINITIONS //***************************************************************************** #define I2C_BASE I2CA0_BASE #define SYS_CLK 80000000 #define FAILURE -1 #define SUCCESS 0 #define RETERR_IF_TRUE(condition) {if(condition) return FAILURE;} #define RET_IF_ERR(Func) {int iRetVal = (Func); \ if (SUCCESS != iRetVal) \ return iRetVal;} //**************************************************************************** // LOCAL FUNCTION DEFINITIONS //**************************************************************************** static int I2CTransact(unsigned long ulCmd); //**************************************************************************** // //! Invokes the transaction over I2C //! //! \param ulCmd is the command to be executed over I2C //! //! This function works in a polling mode, //! 1. Initiates the transfer of the command. //! 2. Waits for the I2C transaction completion //! 3. Check for any error in transaction //! 4. Clears the master interrupt //! //! \return 0: Success, < 0: Failure. // //**************************************************************************** static int I2CTransact(unsigned long ulCmd) { // // Clear all interrupts // MAP_I2CMasterIntClearEx(I2C_BASE,MAP_I2CMasterIntStatusEx(I2C_BASE,false)); // // Set the time-out. Not to be used with breakpoints. // MAP_I2CMasterTimeoutSet(I2C_BASE, I2C_TIMEOUT_VAL); // // Initiate the transfer. // MAP_I2CMasterControl(I2C_BASE, ulCmd); // // Wait until the current byte has been transferred. // Poll on the raw interrupt status. // while((MAP_I2CMasterIntStatusEx(I2C_BASE, false) & (I2C_MASTER_INT_DATA | !I2C_MASTER_INT_TIMEOUT)) == 0) { if (MAP_I2CMasterIntStatusEx(I2C_BASE, false) & I2C_MASTER_INT_TIMEOUT) return -1; } // // Check for any errors in transfer // if(MAP_I2CMasterErr(I2C_BASE) != I2C_MASTER_ERR_NONE) { switch(ulCmd) { case I2C_MASTER_CMD_BURST_SEND_START: case I2C_MASTER_CMD_BURST_SEND_CONT: case I2C_MASTER_CMD_BURST_SEND_STOP: MAP_I2CMasterControl(I2C_BASE, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); break; case I2C_MASTER_CMD_BURST_RECEIVE_START: case I2C_MASTER_CMD_BURST_RECEIVE_CONT: case I2C_MASTER_CMD_BURST_RECEIVE_FINISH: MAP_I2CMasterControl(I2C_BASE, I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP); break; default: break; } return FAILURE; } return SUCCESS; } //**************************************************************************** // //! Invokes the I2C driver APIs to write to the specified address //! //! \param ucDevAddr is the 7-bit I2C slave address //! \param pucData is the pointer to the data to be written //! \param ucLen is the length of data to be written //! \param ucStop determines if the transaction is followed by stop bit //! //! This function works in a polling mode, //! 1. Writes the device register address to be written to. //! 2. In a loop, writes all the bytes over I2C //! //! \return 0: Success, < 0: Failure. // //**************************************************************************** int I2C_IF_Write(unsigned char ucDevAddr, unsigned char *pucData, unsigned char ucLen, unsigned char ucStop) { RETERR_IF_TRUE(pucData == NULL); RETERR_IF_TRUE(ucLen == 0); // // Set I2C codec slave address // MAP_I2CMasterSlaveAddrSet(I2C_BASE, ucDevAddr, false); // // Write the first byte to the controller. // MAP_I2CMasterDataPut(I2C_BASE, *pucData); // // Initiate the transfer. // RET_IF_ERR(I2CTransact(I2C_MASTER_CMD_BURST_SEND_START)); // // Decrement the count and increment the data pointer // to facilitate the next transfer // ucLen--; pucData++; // // Loop until the completion of transfer or error // while(ucLen) { // // Write the next byte of data // MAP_I2CMasterDataPut(I2C_BASE, *pucData); // // Transact over I2C to send byte // RET_IF_ERR(I2CTransact(I2C_MASTER_CMD_BURST_SEND_CONT)); // // Decrement the count and increment the data pointer // to facilitate the next transfer // ucLen--; pucData++; } // // If stop bit is to be sent, send it. // if(ucStop == true) { RET_IF_ERR(I2CTransact(I2C_MASTER_CMD_BURST_SEND_STOP)); } return SUCCESS; } //**************************************************************************** // //! Invokes the I2C driver APIs to read from the device. This assumes the //! device local address to read from is set using the I2CWrite API. //! //! \param ucDevAddr is the 7-bit I2C slave address //! \param pucData is the pointer to the read data to be placed //! \param ucLen is the length of data to be read //! //! This function works in a polling mode, //! 1. Writes the device register address to be written to. //! 2. In a loop, reads all the bytes over I2C //! //! \return 0: Success, < 0: Failure. // //**************************************************************************** int I2C_IF_Read(unsigned char ucDevAddr, unsigned char *pucData, unsigned char ucLen) { unsigned long ulCmdID; RETERR_IF_TRUE(pucData == NULL); RETERR_IF_TRUE(ucLen == 0); // // Set I2C codec slave address // MAP_I2CMasterSlaveAddrSet(I2C_BASE, ucDevAddr, true); // // Check if its a single receive or burst receive // if(ucLen == 1) { // // Configure for a single receive // ulCmdID = I2C_MASTER_CMD_SINGLE_RECEIVE; } else { // // Initiate a burst receive sequence // ulCmdID = I2C_MASTER_CMD_BURST_RECEIVE_START; } // // Initiate the transfer. // RET_IF_ERR(I2CTransact(ulCmdID)); // // Decrement the count and increment the data pointer // to facilitate the next transfer // ucLen--; // // Loop until the completion of reception or error // while(ucLen) { // // Receive the byte over I2C // *pucData = MAP_I2CMasterDataGet(I2C_BASE); // // Decrement the count and increment the data pointer // to facilitate the next transfer // ucLen--; pucData++; if(ucLen) { // // Continue the reception // RET_IF_ERR(I2CTransact(I2C_MASTER_CMD_BURST_RECEIVE_CONT)); } else { // // Complete the last reception // RET_IF_ERR(I2CTransact(I2C_MASTER_CMD_BURST_RECEIVE_FINISH)); } } // // Receive the byte over I2C // *pucData = MAP_I2CMasterDataGet(I2C_BASE); return SUCCESS; } //**************************************************************************** // //! Invokes the I2C driver APIs to read from a specified address the device. //! This assumes the device local address to be of 8-bit. For other //! combinations use I2CWrite followed by I2CRead. //! //! \param ucDevAddr is the 7-bit I2C slave address //! \param pucWrDataBuf is the pointer to the data to be written (reg addr) //! \param ucWrLen is the length of data to be written //! \param pucRdDataBuf is the pointer to the read data to be placed //! \param ucRdLen is the length of data to be read //! //! This function works in a polling mode, //! 1. Writes the data over I2C (device register address to be read from). //! 2. In a loop, reads all the bytes over I2C //! //! \return 0: Success, < 0: Failure. // //**************************************************************************** int I2C_IF_ReadFrom(unsigned char ucDevAddr, unsigned char *pucWrDataBuf, unsigned char ucWrLen, unsigned char *pucRdDataBuf, unsigned char ucRdLen) { // // Write the register address to be read from. // Stop bit implicitly assumed to be 0. // RET_IF_ERR(I2C_IF_Write(ucDevAddr,pucWrDataBuf,ucWrLen,0)); // // Read the specified length of data // RET_IF_ERR(I2C_IF_Read(ucDevAddr, pucRdDataBuf, ucRdLen)); return SUCCESS; } //**************************************************************************** // //! Enables and configures the I2C peripheral //! //! \param ulMode is the mode configuration of I2C //! The parameter \e ulMode is one of the following //! - \b I2C_MASTER_MODE_STD for 100 Kbps standard mode. //! - \b I2C_MASTER_MODE_FST for 400 Kbps fast mode. //! //! This function works in a polling mode, //! 1. Powers ON the I2C peripheral. //! 2. Configures the I2C peripheral //! //! \return 0: Success, < 0: Failure. // //**************************************************************************** int I2C_IF_Open(unsigned long ulMode) { // // Enable I2C Peripheral // //MAP_HwSemaphoreLock(HWSEM_I2C, HWSEM_WAIT_FOR_EVER); MAP_PRCMPeripheralClkEnable(PRCM_I2CA0, PRCM_RUN_MODE_CLK); MAP_PRCMPeripheralReset(PRCM_I2CA0); MAP_I2CMasterEnable(I2CA0_BASE); // Clear all interrupts. MAP_I2CMasterIntClearEx(I2CA0_BASE, MAP_I2CMasterIntStatusEx(I2CA0_BASE, false)); // Enable interrupts. MAP_I2CMasterIntEnableEx(I2CA0_BASE, I2C_MASTER_INT_TIMEOUT | // timeout I2C_MASTER_INT_DATA // data transaction complete ); // // Configure I2C module in the specified mode // switch(ulMode) { case I2C_MASTER_MODE_STD: /* 100000 */ MAP_I2CMasterInitExpClk(I2C_BASE,SYS_CLK,false); break; case I2C_MASTER_MODE_FST: /* 400000 */ MAP_I2CMasterInitExpClk(I2C_BASE,SYS_CLK,true); break; default: MAP_I2CMasterInitExpClk(I2C_BASE,SYS_CLK,true); break; } // // Disables the multi-master mode // //MAP_I2CMasterDisable(I2C_BASE); return SUCCESS; } //**************************************************************************** // //! Disables the I2C peripheral //! //! \param None //! //! This function works in a polling mode, //! 1. Powers OFF the I2C peripheral. //! //! \return 0: Success, < 0: Failure. // //**************************************************************************** int I2C_IF_Close() { // // Power OFF the I2C peripheral // MAP_PRCMPeripheralClkDisable(PRCM_I2CA0, PRCM_RUN_MODE_CLK); return SUCCESS; }