/***************************************************************************//** * @brief * Initialize I2C. * * @param[in] i2c * Pointer to I2C peripheral register block. * * @param[in] init * Pointer to I2C initialization structure. ******************************************************************************/ void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init) { EFM_ASSERT(I2C_REF_VALID(i2c)); i2c->IEN = 0; i2c->IFC = _I2C_IFC_MASK; I2C_BusFreqSet(i2c, init->refFreq, init->freq, init->clhr); BITBAND_Peripheral(&(i2c->CTRL), _I2C_CTRL_SLAVE_SHIFT, ~((unsigned int)(init->master))); BITBAND_Peripheral(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, (unsigned int)(init->enable)); }
/***************************************************************************//** * @brief * Continue an initiated I2C transfer (single master mode only). * * @details * This function is used repeatedly after a I2C_TransferInit() in order to * complete a transfer. It may be used in polled mode as the below example * shows: * @verbatim * I2C_TransferReturn_TypeDef ret; * * // Do a polled transfer * ret = I2C_TransferInit(I2C0, seq); * while (ret == i2cTransferInProgress) * { * ret = I2C_Transfer(I2C0); * } * @endverbatim * It may also be used in interrupt driven mode, where this function is invoked * from the interrupt handler. Notice that if used in interrupt mode, NVIC * interrupts must be configured and enabled for the I2C bus used. I2C * peripheral specific interrupts are managed by this SW. * * @note * Only single master mode is supported. * * @param[in] i2c * Pointer to I2C peripheral register block. * * @return * Returns status for ongoing transfer. * @li #i2cTransferInProgress - indicates that transfer not finished. * @li #i2cTransferDone - transfer completed successfully. * @li otherwise some sort of error has occurred. * ******************************************************************************/ I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c) { uint32_t tmp; uint32_t pending; I2C_Transfer_TypeDef *transfer; I2C_TransferSeq_TypeDef *seq; EFM_ASSERT(I2C_REF_VALID(i2c)); /* Support up to 2 I2C buses */ if (i2c == I2C0) { transfer = i2cTransfer; } #if (I2C_COUNT > 1) else if (i2c == I2C1) { transfer = i2cTransfer + 1; } #endif else { return(i2cTransferUsageFault); } seq = transfer->seq; for (;; ) { pending = i2c->IF; /* If some sort of fault, abort transfer. */ if (pending & I2C_IF_ERRORS) { if (pending & I2C_IF_ARBLOST) { /* If arbitration fault, it indicates either a slave device */ /* not responding as expected, or other master which is not */ /* supported by this SW. */ transfer->result = i2cTransferArbLost; } else if (pending & I2C_IF_BUSERR) { /* A bus error indicates a misplaced start or stop, which should */ /* not occur in master mode controlled by this SW. */ transfer->result = i2cTransferBusErr; } /* If error situation occurred, it is difficult to know */ /* exact cause and how to resolve. It will be up to a wrapper */ /* to determine how to handle a fault/recovery if possible. */ transfer->state = i2cStateDone; goto done; } switch (transfer->state) { /***************************************************/ /* Send first start+address (first byte if 10 bit) */ /***************************************************/ case i2cStateStartAddrSend: if (seq->flags & I2C_FLAG_10BIT_ADDR) { tmp = (((uint32_t)(seq->addr) >> 8) & 0x06) | 0xf0; /* In 10 bit address mode, the address following the first */ /* start always indicate write. */ } else { tmp = (uint32_t)(seq->addr) & 0xfe; if (seq->flags & I2C_FLAG_READ) { /* Indicate read request */ tmp |= 1; } } transfer->state = i2cStateAddrWFAckNack; i2c->TXDATA = tmp; /* Data not transmitted until START sent */ i2c->CMD = I2C_CMD_START; goto done; /*******************************************************/ /* Wait for ACK/NACK on address (first byte if 10 bit) */ /*******************************************************/ case i2cStateAddrWFAckNack: if (pending & I2C_IF_NACK) { i2c->IFC = I2C_IFC_NACK; transfer->result = i2cTransferNack; transfer->state = i2cStateWFStopSent; i2c->CMD = I2C_CMD_STOP; } else if (pending & I2C_IF_ACK) { i2c->IFC = I2C_IFC_ACK; /* If 10 bit address, send 2nd byte of address. */ if (seq->flags & I2C_FLAG_10BIT_ADDR) { transfer->state = i2cStateAddrWF2ndAckNack; i2c->TXDATA = (uint32_t)(seq->addr) & 0xff; } else { /* Determine whether receiving or sending data */ if (seq->flags & I2C_FLAG_READ) { transfer->state = i2cStateWFData; if(seq->buf[transfer->bufIndx].len==1) { i2c->CMD = I2C_CMD_NACK; } } else { transfer->state = i2cStateDataSend; continue; } } } goto done; /******************************************************/ /* Wait for ACK/NACK on second byte of 10 bit address */ /******************************************************/ case i2cStateAddrWF2ndAckNack: if (pending & I2C_IF_NACK) { i2c->IFC = I2C_IFC_NACK; transfer->result = i2cTransferNack; transfer->state = i2cStateWFStopSent; i2c->CMD = I2C_CMD_STOP; } else if (pending & I2C_IF_ACK) { i2c->IFC = I2C_IFC_ACK; /* If using plain read sequence with 10 bit address, switch to send */ /* repeated start. */ if (seq->flags & I2C_FLAG_READ) { transfer->state = i2cStateRStartAddrSend; } /* Otherwise expected to write 0 or more bytes */ else { transfer->state = i2cStateDataSend; } continue; } goto done; /*******************************/ /* Send repeated start+address */ /*******************************/ case i2cStateRStartAddrSend: if (seq->flags & I2C_FLAG_10BIT_ADDR) { tmp = ((seq->addr >> 8) & 0x06) | 0xf0; }
/***************************************************************************//** * @brief * Enable/disable I2C. * * @note * After enabling the I2C (from being disabled), the I2C is in BUSY state. * * @param[in] i2c * Pointer to I2C peripheral register block. * * @param[in] enable * true to enable counting, false to disable. ******************************************************************************/ void I2C_Enable(I2C_TypeDef *i2c, bool enable) { EFM_ASSERT(I2C_REF_VALID(i2c)); BITBAND_Peripheral(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, (unsigned int)enable); }
/***************************************************************************//** * @brief * Enable/disable I2C. * * @note * After enabling the I2C (from being disabled), the I2C is in BUSY state. * * @param[in] i2c * Pointer to I2C peripheral register block. * * @param[in] enable * true to enable counting, false to disable. ******************************************************************************/ void I2C_Enable(I2C_TypeDef *i2c, bool enable) { EFM_ASSERT(I2C_REF_VALID(i2c)); BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, enable); }