/** * @brief I2C error handler. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] isr content of the ISR register to be decoded * * @notapi */ static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) { #if STM32_I2C_USE_DMA == TRUE /* Clears DMA interrupt flags just to be safe.*/ dmaStreamDisable(i2cp->dmatx); dmaStreamDisable(i2cp->dmarx); #else /* Disabling RX and TX interrupts.*/ i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); #endif if (isr & I2C_ISR_BERR) i2cp->errors |= I2C_BUS_ERROR; if (isr & I2C_ISR_ARLO) i2cp->errors |= I2C_ARBITRATION_LOST; if (isr & I2C_ISR_OVR) i2cp->errors |= I2C_OVERRUN; if (isr & I2C_ISR_TIMEOUT) i2cp->errors |= I2C_TIMEOUT; /* If some error has been identified then sends wakes the waiting thread.*/ if (i2cp->errors != I2C_NO_ERROR) _i2c_wakeup_error_isr(i2cp); }
/** * @brief Common IRQ handler. * @note Tries hard to clear all the pending interrupt sources, we don't * want to go through the whole ISR and have another interrupt soon * after. * * @param[in] i2cp pointer to an I2CDriver */ static void i2c_serve_interrupt(I2CDriver *i2cp) { NRF_TWIM_Type *i2c = i2cp->i2c; if (i2c->EVENTS_ERROR) { uint32_t err = i2c->ERRORSRC; i2c->EVENTS_ERROR = 0; (void)i2c->EVENTS_ERROR; if (err & 0x01) // nRF52832 Product Specification v1.3 p.314 TWIM_ERRORSRC OVERRUN bit = 0x01 i2cp->errors |= I2C_OVERRUN; if (err & (TWIM_ERRORSRC_ANACK_Msk | TWIM_ERRORSRC_DNACK_Msk)) i2cp->errors |= I2C_ACK_FAILURE; i2c->TASKS_STOP = 1; _i2c_wakeup_error_isr(i2cp); } else if(i2c->EVENTS_STOPPED) { i2c->EVENTS_STOPPED = 0; (void)i2c->EVENTS_STOPPED; _i2c_wakeup_isr(i2cp); } }
/** * @brief I2C shared ISR code. * * @param[in] i2cp pointer to the @p I2CDriver object * @param[in] isr content of the ISR register to be decoded * * @notapi */ static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { I2C_TypeDef *dp = i2cp->i2c; /* Special case of a received NACK, the transfer is aborted.*/ if ((isr & I2C_ISR_NACKF) != 0U) { #if STM32_I2C_USE_DMA == TRUE /* Stops the associated DMA streams.*/ dmaStreamDisable(i2cp->dmatx); dmaStreamDisable(i2cp->dmarx); #endif /* Error flag.*/ i2cp->errors |= I2C_ACK_FAILURE; /* Transaction finished sending the STOP.*/ dp->CR2 |= I2C_CR2_STOP; /* Make sure no more interrupts.*/ dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE); /* Errors are signaled to the upper layer.*/ _i2c_wakeup_error_isr(i2cp); return; } #if STM32_I2C_USE_DMA == FALSE /* Handling of data transfer if the DMA mode is disabled.*/ { uint32_t cr1 = dp->CR1; if (i2cp->state == I2C_ACTIVE_TX) { /* Transmission phase.*/ if (((cr1 &I2C_CR1_TXIE) != 0U) && ((isr & I2C_ISR_TXIS) != 0U)) { dp->TXDR = (uint32_t)*i2cp->txptr; i2cp->txptr++; i2cp->txbytes--; if (i2cp->txbytes == 0U) { dp->CR1 &= ~I2C_CR1_TXIE; } } } else { /* Receive phase.*/ if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { *i2cp->rxptr = (uint8_t)dp->RXDR; i2cp->rxptr++; i2cp->rxbytes--; if (i2cp->rxbytes == 0U) { dp->CR1 &= ~I2C_CR1_RXIE; } } } } #endif /* Partial transfer handling, restarting the transfer and returning.*/ if ((isr & I2C_ISR_TCR) != 0U) { if (i2cp->state == I2C_ACTIVE_TX) { i2c_lld_setup_tx_transfer(i2cp); } else { i2c_lld_setup_rx_transfer(i2cp); } return; } /* The following condition is true if a transfer phase has been completed.*/ if ((isr & I2C_ISR_TC) != 0U) { if (i2cp->state == I2C_ACTIVE_TX) { /* End of the transmit phase.*/ #if STM32_I2C_USE_DMA == TRUE /* Disabling TX DMA channel.*/ dmaStreamDisable(i2cp->dmatx); #endif /* Starting receive phase if necessary.*/ if (i2c_lld_get_rxbytes(i2cp) > 0U) { /* Setting up the peripheral.*/ i2c_lld_setup_rx_transfer(i2cp); #if STM32_I2C_USE_DMA == TRUE /* Enabling RX DMA.*/ dmaStreamEnable(i2cp->dmarx); #else /* RX interrupt enabled.*/ dp->CR1 |= I2C_CR1_RXIE; #endif /* Starts the read operation.*/ dp->CR2 |= I2C_CR2_START; /* State change.*/ i2cp->state = I2C_ACTIVE_RX; /* Note, returning because the transaction is not over yet.*/ return; } } else { /* End of the receive phase.*/ #if STM32_I2C_USE_DMA == TRUE /* Disabling RX DMA channel.*/ dmaStreamDisable(i2cp->dmarx); #endif } /* Transaction finished sending the STOP.*/ dp->CR2 |= I2C_CR2_STOP; /* Make sure no more 'Transfer Complete' interrupts.*/ dp->CR1 &= ~I2C_CR1_TCIE; /* Normal transaction end.*/ _i2c_wakeup_isr(i2cp); } }
/** * @brief Common IRQ handler. * @note Tries hard to clear all the pending interrupt sources, we don't * want to go through the whole ISR and have another interrupt soon * after. * * @param[in] i2cp pointer to an I2CDriver */ static void serve_interrupt(I2CDriver *i2cp) { I2C_TypeDef *i2c = i2cp->i2c; intstate_t state = i2cp->intstate; if (i2c->S & I2Cx_S_ARBL) { i2cp->errors |= I2C_ARBITRATION_LOST; i2c->S |= I2Cx_S_ARBL; } else if (state == STATE_SEND) { if (i2c->S & I2Cx_S_RXAK) i2cp->errors |= I2C_ACK_FAILURE; else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes) i2c->D = i2cp->txbuf[i2cp->txidx++]; else i2cp->intstate = STATE_STOP; } else if (state == STATE_DUMMY) { if (i2c->S & I2Cx_S_RXAK) i2cp->errors |= I2C_ACK_FAILURE; else { i2c->C1 &= ~I2Cx_C1_TX; if (i2cp->rxbytes > 1) i2c->C1 &= ~I2Cx_C1_TXAK; else i2c->C1 |= I2Cx_C1_TXAK; (void) i2c->D; i2cp->intstate = STATE_RECV; } } else if (state == STATE_RECV) { if (i2cp->rxbytes > 1) { if (i2cp->rxidx == (i2cp->rxbytes - 2)) i2c->C1 |= I2Cx_C1_TXAK; else i2c->C1 &= ~I2Cx_C1_TXAK; } if (i2cp->rxidx == i2cp->rxbytes - 1) i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); i2cp->rxbuf[i2cp->rxidx++] = i2c->D; if (i2cp->rxidx == i2cp->rxbytes) i2cp->intstate = STATE_STOP; } /* Reset interrupt flag */ i2c->S |= I2Cx_S_IICIF; if (i2cp->errors != I2C_NO_ERROR) _i2c_wakeup_error_isr(i2cp); if (i2cp->intstate == STATE_STOP) _i2c_wakeup_isr(i2cp); }
/** * @brief Common IRQ handler. * @note Tries hard to clear all the pending interrupt sources, we don't * want to go through the whole ISR and have another interrupt soon * after. * * @param[in] i2cp pointer to an I2CDriver */ static void serve_interrupt(I2CDriver *i2cp) { I2C_TypeDef *i2c = i2cp->i2c; intstate_t state = i2cp->intstate; /* check if we're master or slave */ if (i2c->C1 & I2Cx_C1_MST) { /* master */ if (i2c->S & I2Cx_S_ARBL) { /* check if we lost arbitration */ i2cp->errors |= I2C_ARBITRATION_LOST; i2c->S |= I2Cx_S_ARBL; /* TODO: may need to do more here, reset bus? */ /* Perhaps clear MST? */ } #if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */ else if ((i2cp->rsta_workaround == RSTA_WORKAROUND_ON) && (i2cp->i2c->FLT & I2Cx_FLT_STARTF)) { i2cp->rsta_workaround = RSTA_WORKAROUND_OFF; /* clear+disable STARTF/STOPF interrupts and wake up the thread */ i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF; i2cp->i2c->FLT &= ~I2Cx_FLT_SSIE; i2c->S |= I2Cx_S_IICIF; _i2c_wakeup_isr(i2cp); } #endif /* KL27Z RST workaround */ else if (i2c->S & I2Cx_S_TCF) { /* just completed byte transfer */ if (i2c->C1 & I2Cx_C1_TX) { /* the byte was transmitted */ if (state == STATE_SEND) { /* currently sending stuff */ if (i2c->S & I2Cx_S_RXAK) { /* slave did not ACK */ i2cp->errors |= I2C_ACK_FAILURE; /* the thread will be woken up at the end of ISR and release the bus */ } else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes) { /* slave ACK'd and we want to send more */ i2c->D = i2cp->txbuf[i2cp->txidx++]; } else { /* slave ACK'd and we are done sending */ i2cp->intstate = STATE_STOP; /* this wakes up the waiting thread at the end of ISR */ } } else if (state == STATE_RECV) { /* should be receiving stuff, so we've just sent the address */ if (i2c->S & I2Cx_S_RXAK) { /* slave did not ACK */ i2cp->errors |= I2C_ACK_FAILURE; /* the thread will be woken up and release the bus */ } else { /* slave ACK'd, we should be receiving next */ i2c->C1 &= ~I2Cx_C1_TX; if (i2cp->rxbytes > 1) { /* multi-byte read, send ACK after next transfer */ i2c->C1 &= ~I2Cx_C1_TXAK; } else { /* only 1 byte remaining, send NAK */ i2c->C1 |= I2Cx_C1_TXAK; } (void) i2c->D; /* dummy read; triggers next receive */ } } /* possibly check other states here - should not happen! */ } else { /* the byte was received */ if (state == STATE_RECV) { /* currently receiving stuff */ /* the received byte is now in D */ if (i2cp->rxbytes > 1) { /* expecting at least one byte after this one */ if (i2cp->rxidx == (i2cp->rxbytes - 2)) { /* expecting exactly one byte after this one, NAK that one */ i2c->C1 |= I2Cx_C1_TXAK; } else { /* expecting more than one after this one, respond with ACK */ i2c->C1 &= ~I2Cx_C1_TXAK; } } if (i2cp->rxidx == i2cp->rxbytes - 1) { /* D is the last byte we're expecting */ /* release bus: switch to RX mode, send STOP */ /* need to do it now otherwise the I2C module will wait for another byte */ // delayMicroseconds(1); i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); i2cp->intstate = STATE_STOP; /* this wakes up the waiting thread at the end of ISR */ } /* get the data from D; this triggers the next receive */ i2cp->rxbuf[i2cp->rxidx++] = i2c->D; // if (i2cp->rxidx == i2cp->rxbytes) { /* done receiving */ // } } /* possibly check other states here - should not happen! */ } } /* possibly check other interrupt flags here */ } else { /* slave */ /* Not implemented yet */ } /* Reset other interrupt sources */ #if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */ i2cp->i2c->FLT |= I2Cx_FLT_STOPF; #endif #if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */ i2cp->i2c->FLT |= I2Cx_FLT_STARTF; #endif /* Reset interrupt flag */ i2c->S |= I2Cx_S_IICIF; if (i2cp->errors != I2C_NO_ERROR) _i2c_wakeup_error_isr(i2cp); if (i2cp->intstate == STATE_STOP) _i2c_wakeup_isr(i2cp); }
/** * @brief I2C shared ISR code. * * @param[in] i2cp pointer to the @p I2CDriver object * * @notapi */ static void i2c_lld_serve_interrupt(I2CDriver *i2cp) { I2C_TypeDef *dp = i2cp->i2c; uint32_t status; // clear MIS bit in MICR by writing 1 dp->MICR = 1; // read interrupt status status = dp->MCS; if (status & TIVA_MCS_ERROR) { i2cp->errors |= I2C_BUS_ERROR; } if (status & TIVA_MCS_ARBLST) { i2cp->errors |= I2C_ARBITRATION_LOST; } if (i2cp->errors == I2C_NO_ERROR) { // no error detected switch(i2cp->intstate) { case STATE_IDLE: { _i2c_wakeup_isr(i2cp); break; } case STATE_WRITE_NEXT: { if (i2cp->txbytes == 1) { i2cp->intstate = STATE_WRITE_FINAL; } dp->MDR = *(i2cp->txbuf); i2cp->txbuf++; i2cp->txbytes--; // start transmission dp->MCS = TIVA_I2C_BURST_SEND_CONTINUE; break; } case STATE_WRITE_FINAL: { if (i2cp->rxbytes == 0) { i2cp->intstate = STATE_IDLE; } else if (i2cp->rxbytes == 1) { i2cp->intstate = STATE_READ_ONE; } else { i2cp->intstate = STATE_READ_FIRST; } dp->MDR = *(i2cp->txbuf); i2cp->txbuf++; // txbytes - 1 i2cp->txbytes--; // start transmission dp->MCS = TIVA_I2C_BURST_SEND_FINISH; break; } case STATE_WAIT_ACK: { break; } case STATE_SEND_ACK: { break; } case STATE_READ_ONE: { i2cp->intstate = STATE_READ_WAIT; // Initializes driver fields, LSB = 1 -> read. i2cp->addr |= 1; // set slave address dp->MSA = i2cp->addr; i2cp->rxbytes--; //start receiving dp->MCS = TIVA_I2C_SINGLE_RECEIVE; break; } case STATE_READ_FIRST: { if (i2cp->rxbytes == 2) { i2cp->intstate = STATE_READ_FINAL; } else { i2cp->intstate = STATE_READ_NEXT; } // Initializes driver fields, LSB = 1 -> read. i2cp->addr |= 1; // set slave address dp->MSA = i2cp->addr; i2cp->rxbytes--; //start receiving dp->MCS = TIVA_I2C_BURST_RECEIVE_START; break; } case STATE_READ_NEXT: { if(i2cp->rxbytes == 2) { i2cp->intstate = STATE_READ_FINAL; } *(i2cp->rxbuf) = dp->MDR; i2cp->rxbuf++; i2cp->rxbytes--; //start receiving dp->MCS = TIVA_I2C_BURST_RECEIVE_CONTINUE; break; } case STATE_READ_FINAL: { i2cp->intstate = STATE_READ_WAIT; *(i2cp->rxbuf) = dp->MDR; i2cp->rxbuf++; i2cp->rxbytes--; //start receiving dp->MCS = TIVA_I2C_BURST_RECEIVE_FINISH; break; } case STATE_READ_WAIT: { i2cp->intstate = STATE_IDLE; *(i2cp->rxbuf) = dp->MDR; i2cp->rxbuf++; _i2c_wakeup_isr(i2cp); break; } } } else { // error detected _i2c_wakeup_error_isr(i2cp); } }