/** **************************************************************************************** * @brief Initializes the I2C controller * @param[in] buffer i2c buffer (point to a gobal memory) * @param[in] size i2c buffer len, = address size + data size * @description * The function is used to initialize I2C in slave mode, this function is also * used to enable I2c interrupt, and enable NVIC I2C IRQ. ***************************************************************************************** */ void i2c_init(uint8_t *buffer, uint16_t size) { uint32_t reg; i2c_env.i2cOpFsm = I2C_OP_IDLE; i2c_env.i2cBufferSize = size; i2c_env.i2cBuffer = buffer; i2c_reset(); #if CONFIG_I2C_ENABLE_INTERRUPT==TRUE /* Enable the I2C Interrupt */ NVIC_EnableIRQ(I2C_IRQn); #endif /* * Mask all slave interrupt in I2C component */ reg = I2C_SLAVE_ADDR(QN9020_I2C_ADDR) // slave address, slave mode active | I2C_MASK_SLAVE_EN // slave | I2C_MASK_STP_INT_EN // Enable abnormal stop interrupt | I2C_MASK_SAM_INT_EN // Enable slave address match interrupt, slave mode active | I2C_MASK_GC_INT_EN // Enable general call interrupt, slave mode active | I2C_MASK_RX_INT_EN // Enable RX interrupt | I2C_MASK_TX_INT_EN; // Enable TX interrupt i2c_i2c_SetCR(QN_I2C, reg); reg = I2C_MASK_RD_EN | I2C_MASK_SLV_ACK_SEND; //ACK i2c_i2c_SetTXD(QN_I2C, reg); }
/** **************************************************************************************** * @brief Start a data transmission. * @param[in] saddr slave device address(7bits, without R/W bit) * @return Error code * @description * This function is used to complete an I2C write transaction from start to stop. All the intermittent steps * are handled in the interrupt handler while the interrupt is enabled. * Before this function is called, the read length, write length, I2C master buffer, * and I2C state need to be filled. Please refer to I2C_BYTE_WRITE(). * As soon as the end of the data transfer is detected, the callback function is called. ***************************************************************************************** */ static enum I2C_ERR_CODE i2c_write(uint8_t saddr) { uint32_t reg; uint32_t timeout = 0; if (i2c_bus_check() == I2C_BUS_BUSY) { return I2C_CONFLICT; } else { i2c_env.i2cOpFsm = I2C_OP_WRDATA; } // start write slave address with write bit reg = I2C_MASK_WR_EN | I2C_MASK_START | ((saddr << 1) & 0xFE); i2c_i2c_SetTXD(QN_I2C, reg); do { timeout++; if (timeout > I2C_MAX_TIMEOUT) { return I2C_TIMEOUT; } #if CONFIG_I2C_ENABLE_INTERRUPT==FALSE i2c_polling(); #endif if (i2c_env.i2cOpFsm == I2C_OP_ABORT) { return I2C_NO_ACK; } } while (i2c_env.i2cOpFsm != I2C_OP_FINISH); #if I2C_CALLBACK_EN==TRUE // Call end of transmission callback if (i2c_env.callback != NULL) { i2c_env.callback(); } #endif return I2C_NO_ERROR; }
/** **************************************************************************************** * @brief I2C interrupt handler, deal with slave mode only. **************************************************************************************** */ void I2C_IRQHandler(void) { uint32_t status; uint32_t reg; status = i2c_i2c_GetIntStatus(QN_I2C); if (status & I2C_MASK_STP_INT) { reg = I2C_MASK_RD_EN | I2C_MASK_SLV_ACK_SEND; // ACK i2c_i2c_SetTXD(QN_I2C, reg); i2c_env.i2cOpFsm = I2C_OP_IDLE; i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_STP_INT); } if (status & I2C_MASK_GC_INT) { reg = I2C_MASK_RD_EN | I2C_MASK_SLV_ACK_SEND; // ACK i2c_i2c_SetTXD(QN_I2C, reg); i2c_env.i2cOpFsm = I2C_OP_IDLE; i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_GC_INT); } if (status & I2C_MASK_SAM_INT) { // slave adress is match reg = i2c_i2c_GetRXD(QN_I2C); if (reg & 0x01) { // master read, slave write reg = I2C_MASK_WR_EN | I2C_MASK_SLV_ACK_SEND // ACK | i2c_env.i2cBuffer[i2c_env.i2cIndex++]; i2c_env.i2cOpFsm = I2C_OP_WRDATA; } else { // mast write, slave read reg = I2C_MASK_RD_EN | I2C_MASK_SLV_ACK_SEND; // ACK i2c_env.i2cOpFsm = I2C_OP_SETADDR; } i2c_i2c_SetTXD(QN_I2C, reg); i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_SAM_INT|I2C_MASK_RX_INT); status = 0; } if (status & I2C_MASK_RX_INT) { if (i2c_env.i2cOpFsm == I2C_OP_SETADDR) { i2c_env.i2cIndex = i2c_i2c_GetRXD(QN_I2C); // The 1st byte is the index. i2c_env.i2cOpFsm = I2C_OP_RDDATA; } else if (i2c_env.i2cOpFsm == I2C_OP_RDDATA) { i2c_env.i2cBuffer[i2c_env.i2cIndex++] = i2c_i2c_GetRXD(QN_I2C); } if (i2c_env.i2cIndex >= i2c_env.i2cBufferSize) { reg = I2C_MASK_RD_EN | I2C_MASK_SLV_NACK_SEND; // NACK } else { reg = I2C_MASK_RD_EN | I2C_MASK_SLV_ACK_SEND; // ACK } i2c_i2c_SetTXD(QN_I2C, reg); i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_RX_INT); } if (status & I2C_MASK_TX_INT) { if (!(i2c_i2c_GetSR(QN_I2C) & I2C_MASK_ACK_RECEIVED)) { // ACK == 0, go on if (i2c_env.i2cIndex >= i2c_env.i2cBufferSize) { // user can modify here i2c_env.i2cIndex = 0; } reg = I2C_MASK_WR_EN | I2C_MASK_SLV_ACK_SEND // ACK | i2c_env.i2cBuffer[i2c_env.i2cIndex++]; } else { // NO ACK, SLAVE back to RD reg = I2C_MASK_RD_EN | I2C_MASK_SLV_ACK_SEND; // ACK } i2c_i2c_SetTXD(QN_I2C, reg); i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_TX_INT); } }
/** **************************************************************************************** * @brief I2C interrupt handler, deal with master mode only. **************************************************************************************** */ void I2C_IRQHandler(void) { uint32_t status; uint32_t reg = 0; status = i2c_i2c_GetIntStatus(QN_I2C); if (status & I2C_MASK_AL_INT) { i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_AL_INT); } if (status & I2C_MASK_RX_INT) { i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_RX_INT); // store read result i2c_env.i2cBuffer[i2c_env.i2cIndex++] = i2c_i2c_GetRXD(QN_I2C); i2c_env.i2cRxCount--; if (i2c_env.i2cRxCount > 1) { reg = I2C_MASK_RD_EN | I2C_MASK_ACK_SEND; // ACK } else if (i2c_env.i2cRxCount == 1) { reg = I2C_MASK_RD_EN | I2C_MASK_NACK_SEND; // NACK } else if (i2c_env.i2cRxCount == 0) { // data rx finish reg = I2C_MASK_STOP; // STOP i2c_env.i2cOpFsm = I2C_OP_FINISH; } i2c_i2c_SetTXD(QN_I2C, reg); } if (status & I2C_MASK_TX_INT) { i2c_i2c_ClrIntStatus(QN_I2C, I2C_MASK_TX_INT); // check ack type if (i2c_i2c_GetSR(QN_I2C) & I2C_MASK_ACK_RECEIVED) { // NO ACK i2c_i2c_SetTXD(QN_I2C, I2C_MASK_STOP); // STOP i2c_env.i2cOpFsm = I2C_OP_ABORT; } else { // ACK if (i2c_env.i2cOpFsm == I2C_OP_RDDATA) { // enable data read if (i2c_env.i2cRxCount > 1) { reg = I2C_MASK_RD_EN | I2C_MASK_ACK_SEND; // ACK } else if (i2c_env.i2cRxCount == 1) { reg = I2C_MASK_RD_EN | I2C_MASK_NACK_SEND; // NACK } i2c_i2c_SetTXD(QN_I2C, reg); } else { if (i2c_env.i2cIndex < i2c_env.i2cTxCount) { reg = I2C_MASK_WR_EN | i2c_env.i2cBuffer[i2c_env.i2cIndex++]; // write address buffer i2c_i2c_SetTXD(QN_I2C, reg); } else { // data tx finish, check need stop or not if (i2c_env.i2cOpFsm == I2C_OP_WRDATA) { i2c_i2c_SetTXD(QN_I2C, I2C_MASK_STOP); // STOP i2c_env.i2cOpFsm = I2C_OP_FINISH; } else if (i2c_env.i2cOpFsm == I2C_OP_SETADDR) { //i2c_i2c_SetTXD(QN_I2C, I2C_MASK_STOP); i2c_env.i2cOpFsm = I2C_OP_RDDATA; } } } } } }
/** **************************************************************************************** * @brief Start a data reception. * @param[in] saddr slave device address(7bits, without R/W bit) * @return Error code * @description * This function is used to complete an I2C read transaction from start to stop. All the intermittent steps * are handled in the interrupt handler while the interrupt is enabled. * Before this function is called, the read length, write length, I2C master buffer, * and I2C state need to be filled. Please refer to I2C_BYTE_READ(). * As soon as the end of the data transfer is detected, the callback function is called. ***************************************************************************************** */ static enum I2C_ERR_CODE i2c_read(uint8_t saddr) { uint32_t reg; uint32_t timeout = 0; if (i2c_bus_check() == I2C_BUS_BUSY) { return I2C_CONFLICT; } if (i2c_env.i2cTxCount) { i2c_env.i2cOpFsm = I2C_OP_SETADDR; // start write slave address with write bit reg = I2C_MASK_WR_EN | I2C_MASK_START | ((saddr << 1) & 0xFE); i2c_i2c_SetTXD(QN_I2C, reg); do { timeout++; if (timeout > I2C_MAX_TIMEOUT) { return I2C_TIMEOUT; } #if CONFIG_I2C_ENABLE_INTERRUPT==FALSE i2c_polling(); #endif if (i2c_env.i2cOpFsm == I2C_OP_ABORT) { return I2C_NO_ACK; } } while (i2c_env.i2cOpFsm != I2C_OP_RDDATA); } else { // does not need write address, directly read data from device i2c_env.i2cOpFsm = I2C_OP_RDDATA; } // start write slave address with read bit reg = I2C_MASK_WR_EN | I2C_MASK_START | ((saddr << 1) | 0x01); i2c_i2c_SetTXD(QN_I2C, reg); timeout = 0; do { timeout++; if (timeout > I2C_MAX_TIMEOUT) { return I2C_TIMEOUT; } #if CONFIG_I2C_ENABLE_INTERRUPT==FALSE i2c_polling(); #endif if (i2c_env.i2cOpFsm == I2C_OP_ABORT) { return I2C_NO_ACK; } } while (i2c_env.i2cOpFsm != I2C_OP_FINISH); #if I2C_CALLBACK_EN==TRUE // Call end of reception callback if (i2c_env.callback != NULL) { i2c_env.callback(); } #endif return I2C_NO_ERROR; }
/** **************************************************************************************** * @brief Start a data reception. * @param[in] saddr slave device address (7bits, without R/W bit) * @description * This function is used to complete an I2C read transaction from start to stop. All the intermittent steps * are handled in the interrupt handler while the interrupt is enabled. * Before this function is called, the read length, write length, I2C master buffer, * and I2C state need to be filled. Please refer to I2C_BYTE_READ(). * As soon as the end of the data transfer is detected, the callback function is called. ***************************************************************************************** */ void i2c_read(uint8_t saddr) { uint32_t reg; uint32_t timeout = 0; if (i2c_bus_check() == I2C_BUS_BUSY) { return; } if (i2c_env.i2cTxCount) { i2c_env.i2cOpFsm = I2C_OP_SETADDR; // start write slave address with write bit reg = I2C_MASK_WR_EN | I2C_MASK_START | ((saddr << 1) & 0xFE); i2c_i2c_SetTXD(QN_I2C, reg); #ifdef SLP_TEST_EN // Wait For Interrupt __WFI(); // Enter sleep mode // Wakeup when I2C interrupt is triggered #endif do { timeout++; if (timeout > I2C_MAX_TIMEOUT) { break; } #if CONFIG_I2C_ENABLE_INTERRUPT==FALSE i2c_polling(); #endif } while ((i2c_env.i2cOpFsm != I2C_OP_RDDATA)&&(i2c_env.i2cOpFsm != I2C_OP_ABORT)); } else { // does not need write address, directly read data from device i2c_env.i2cOpFsm = I2C_OP_RDDATA; } // start write slave address with read bit reg = I2C_MASK_WR_EN | I2C_MASK_START | ((saddr << 1) | 0x01); i2c_i2c_SetTXD(QN_I2C, reg); timeout = 0; do { timeout++; if (timeout > I2C_MAX_TIMEOUT){ break; } #if CONFIG_I2C_ENABLE_INTERRUPT==FALSE i2c_polling(); #endif } while (i2c_env.i2cOpFsm != I2C_OP_FINISH); #if I2C_CALLBACK_EN==TRUE // Call end of reception callback if ((i2c_env.i2cOpFsm == I2C_OP_FINISH) && (i2c_env.callback != NULL)) { i2c_env.callback(); } #endif }