static inline void i2c_irq(struct i2c_periph *periph) { /* There are 7 possible reasons to get here: If IT_EV_FEN ------------------------- We are always interested in all IT_EV_FEV: all are required. 1) SB // Start Condition Success in Master mode 2) ADDR // Address sent received Acknoledge [3 ADDR10] // -- 10bit address stuff [4 STOPF] // -- only for slaves: master has no stop interrupt 5) BTF // I2C has stopped working (it is waiting for new data, all buffers are tx_empty/rx_full) // Beware: using the buffered I2C has some interesting properties: -in master receive mode: BTF only occurs after the 2nd received byte: after the first byte is received it is in RD but the I2C can still receive a second byte. Only when the 2nd byte is received while the RxNE is 1 then a BTF occurs (I2C can not continue receiving bytes or they will get lost). During BTF I2C is halted (SCL held low) -in master transmitmode: when writing a byte to WD, you instantly get a new TxE interrupt while the first is not transmitted yet. The byte was pushed to the I2C shift register and the buffer is ready for more. You can already fill new data in the buffer while the first is still being transmitted for max performance transmission. // Beware: besides data buffering you can/must plan several consecutive actions. You can send 2 bytes to the buffer, ask for a stop and a new start in one go. -thanks to / because of this buffering and event sheduling there is not 1 interrupt per start / byte / stop This also means you must think more in advance and a transaction could be popped from the stack even before it is actually completely transmitted. But then you would not know the result yet so you have to keep it until the result is known. // Beware: the order in which Status is read determines how flags are cleared. You should not just read SR1 & SR2 every time If IT_EV_FEN AND IT_EV_BUF -------------------------- Buffer event are not always wanted and are tipically switched on during longer data transfers. Make sure to turn off in time. 6) RxNE 7) TxE -------------------------------------------------------------------------------------------------- // This driver uses only a subset of the pprz_i2c_states for several reasons: // -we have less interrupts than the I2CStatus states (for efficiency) // -STM32 has such a powerfull I2C engine with plenty of status register flags that only little extra status information needs to be stored. // Status is re-used (abused) to remember the last COMMAND THAT WAS SENT to the STM I2C hardware. // TODO: check which are used enum I2CStatus { I2CIdle, // No more last command I2CStartRequested, // Last command was start I2CRestartRequested, // Last command was restart I2CStopRequested, // Very important to not send double stop conditions I2CSendingByte, // Some address/data operation // Following are not used I2CReadingByte, I2CAddrWrSent, I2CAddrRdSent, I2CSendingLastByte, I2CReadingLastByte, I2CComplete, I2CFailed }; --------- The STM waits indefinately (holding SCL low) for user interaction: a) after a master-start (waiting for address) b) after an address (waiting for data) not during data sending when using buffered c) after the last byte is transmitted (waiting for either stop or restart) not during data receiving when using buffered not after the last byte is received -The STM I2C stalls indefinately when a stop condition was attempted that did not succeed. The BUSY flag remains on. -There is no STOP interrupt: use needs another way to finish. */ /////////////////////////////////////////////////////////////////////////////////// // Reading the status: // - Caution: this clears several flags and can start transmissions etc... // - Certain flags like STOP / (N)ACK need to be guaranteed to be set before // the transmission of the byte is finished. At higher clock rates that can be // quite fast: so we allow no other interrupt to be triggered in between // reading the status and setting all needed flags // Direct Access to the I2C Registers // Do not read SR2 as it might start the reading while an (n)ack bit might be needed first I2C_TypeDef *regs = (I2C_TypeDef *) periph->reg_addr; #ifdef I2C_DEBUG_LED LED1_ON(); LED1_OFF(); #endif /////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// // // TRANSACTION HANDLER enum STMI2CSubTransactionStatus ret = 0; /////////////////////// // Nothing Left To Do if (periph->trans_extract_idx == periph->trans_insert_idx) { #ifdef I2C_DEBUG_LED LED2_ON(); LED1_ON(); LED2_OFF(); LED1_OFF(); // no transaction and also an error? LED_SHOW_ACTIVE_BITS(regs); #endif // If we still get an interrupt but there are no more things to do // (which can happen if an event was sheduled just before a bus error occurs) // then its easy: just stop: clear all interrupt generating bits // Count The Errors i2c_error(periph); // Clear Running Events stmi2c_clear_pending_interrupts(regs); // Mark this as a special error periph->errors->unexpected_event_cnt++; periph->status = I2CIdle; // There are no transactions anymore: // furtheron we need a transaction pointer: so we are not allowed to continue return; } struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; /////////////////////////// // If there was an error: if (( regs->SR1 & I2C_SR1_BITS_ERR ) != 0x0000) { #ifdef I2C_DEBUG_LED LED1_ON(); LED2_ON(); LED1_OFF(); LED2_OFF(); LED_SHOW_ACTIVE_BITS(regs); #endif // Set result in transaction trans->status = I2CTransFailed; // Prepare for next ret = STMI2C_SubTra_Ready; // Make sure a TxRx does not Restart trans->type = I2CTransRx; /* // There are 2 types of errors: some need a STOP, some better do without: Following will not get an extra stop if ( // Lost Arbitration (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ERR_ARLO, regs->SR1 ) ) // Buss Error When Master Only || ((BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ERR_BUS, regs->SR1 ) ) && (!BIT_X_IS_SET_IN_REG( I2C_SR2_BIT_MSL, regs->SR2 ) )) || (BIT_X_IS_SET_IN_REG( I2C_SR1_BIT_ERR_OVR, regs->SR1 ) ) ) { ret = STMI2C_SubTra_Error; } */ // Count The Errors i2c_error(periph); // Clear Running Events stmi2c_clear_pending_interrupts(regs); } /////////////////////////// // Normal Event: else { if (trans->type == I2CTransRx) // TxRx are converted to Rx after the Tx Part { switch (trans->len_r) { case 1: ret = stmi2c_read1(regs,trans); break; case 2: ret = stmi2c_read2(regs,trans); break; default: ret = stmi2c_readmany(regs,periph, trans); break; } } else // TxRx or Tx { ret = stmi2c_send(regs,periph,trans); } } ///////////////////////////////// // Sub-transaction has finished if (ret != STMI2C_SubTra_Busy) { // If a restart is not needed if (trans->type != I2CTransTxRx) { // Ready, no stop condition set yet if (ret == STMI2C_SubTra_Ready) { // Program a stop PPRZ_I2C_SEND_STOP(regs); // Silent any BTF that would occur before STOP is executed regs->DR = 0x00; } // In case of unexpected condition: e.g. not slave, no event if (ret == STMI2C_SubTra_Error) { trans->status = I2CTransFailed; // Error #ifdef I2C_DEBUG_LED LED2_ON(); LED1_ON(); LED2_OFF(); LED1_OFF(); #endif LED_SHOW_ACTIVE_BITS(regs); // Clear Running Events stmi2c_clear_pending_interrupts(regs); } // Jump to the next transaction periph->trans_extract_idx++; if (periph->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) periph->trans_extract_idx = 0; // Tell everyone we are ready periph->status = I2CIdle; // if we have no more transaction to process, stop here if (periph->trans_extract_idx == periph->trans_insert_idx) { #ifdef I2C_DEBUG_LED LED2_ON(); LED1_ON(); LED1_OFF(); LED1_ON(); LED1_OFF(); LED2_OFF(); #endif } // if not, start next transaction else { // Restart transaction doing the Rx part now // --- moved to idle function PPRZ_I2C_SEND_START(periph); // ------ } } // RxTx -> Restart and do Rx part else { trans->type = I2CTransRx; periph->status = I2CStartRequested; regs->CR1 |= I2C_CR1_BIT_START; // Silent any BTF that would occur before SB regs->DR = 0x00; } } return; }
static inline void i2c_irq(struct i2c_periph *periph) { /* There are 7 possible event reasons to get here + all errors If IT_EV_FEN ------------------------- We are always interested in all IT_EV_FEV: all are required. 1) SB // Start Condition Success in Master mode 2) ADDR // Address sent received Acknoledge [ADDR10] // -- 10bit address stuff: not used [STOPF] // -- only for slaves: master has no stop interrupt: not used 3) BTF // I2C has stopped working (it is waiting for new data, all buffers are tx_empty/rx_full) // Beware: using the buffered I2C has some interesting properties: - in master receive mode: BTF only occurs after the 2nd received byte: after the first byte is received it is in RD but the I2C can still receive a second byte. Only when the 2nd byte is received while the RxNE is 1 then a BTF occurs (I2C can not continue receiving bytes or they will get lost). During BTF I2C is halted (SCL held low) - in master transmit mode: when writing a byte to WD, you instantly get a new TxE interrupt while the first is not transmitted yet. The byte was pushed to the I2C shift register and the buffer is ready for more. You can already fill new data in the buffer while the first is still being transmitted for max performance transmission. // Beware: besides data buffering you can/must plan several consecutive actions. You can send 2 bytes to the buffer, ask for a stop and a new start in one go. - thanks to / because of this buffering and event sheduling there is not 1 interrupt per start / byte / stop This also means you must think more in advance and a transaction could be popped from the transaction stack even before it's stop condition is actually generated. // Beware: the order in which Status (and other register) is read determines how flags are cleared. You should NOT simply read SR1 & SR2 every time If IT_EV_FEN AND IT_EV_BUF -------------------------- Buffer event are not always wanted and are typically switched on during longer data transfers. Make sure to turn off in time. 4) RxNE 5) TxE -------------------------------------------------------------------------------------------------- The STM waits indefinately (holding SCL low) for user interaction: a) after a master-start (waiting for address) b) after an address (waiting for data) not during data sending when using buffered c) after the last byte is transmitted (waiting for either stop or restart) not during data receiving when using buffered not after the last byte is received - The STM I2C stalls indefinately when a stop condition was attempted that did not succeed. The BUSY flag remains on. - There is no STOP interrupt. Caution Reading the status: - Caution: this clears several flags and can start transmissions etc... - Certain flags like STOP / (N)ACK need to be guaranteed to be set before the transmission of the byte is finished. At higher clock rates that can be quite fast: so we allow no other interrupt to be triggered in between reading the status and setting all needed flags */ // Here we go ... // Apparently we got an I2C interrupt: EVT BUF or ERR #ifdef I2C_DEBUG_LED // Notify ISR is triggered LED1_ON(); LED1_OFF(); #endif // Save Some Direct Access to the I2C Registers ... uint32_t i2c = (uint32_t) periph->reg_addr; ///////////////////////////// // Check if we were ready ... if (periph->trans_extract_idx == periph->trans_insert_idx) { // Nothing Left To Do #ifdef I2C_DEBUG_LED LED2_ON(); LED1_ON(); LED2_OFF(); LED1_OFF(); // no transaction and also an error? LED_SHOW_ACTIVE_BITS(regs); #endif // If we still get an interrupt but there are no more things to do // (which can happen if an event was sheduled just before a bus error occurs) // (or can happen if both error and event interrupts were called together [the 2nd will then get this error]) // since there is nothing more to do: its easy: just stop: clear all interrupt generating bits // Count The Errors i2c_error(periph); // Clear Running Events stmi2c_clear_pending_interrupts(i2c); // Mark this as a special error periph->errors->last_unexpected_event++; // Document the current Status periph->status = I2CIdle; // There are no transactions anymore: return // further-on in this routine we need a transaction pointer: so we are not allowed to continue return; } // get the I2C transaction we were working on ... enum STMI2CSubTransactionStatus ret = 0; struct i2c_transaction* trans = periph->trans[periph->trans_extract_idx]; /////////////////////////// // If there was an error: if (( I2C_SR1(i2c) & I2C_SR1_ERR_MASK ) != 0x0000) { #ifdef I2C_DEBUG_LED LED1_ON(); LED2_ON(); LED1_OFF(); LED2_OFF(); LED_SHOW_ACTIVE_BITS(regs); #endif // Notify everyone about the error ... // Set result in transaction trans->status = I2CTransFailed; // Document the current Status periph->status = I2CFailed; // Make sure a TxRx does not Restart trans->type = I2CTransRx; // Count The Errors i2c_error(periph); // Clear Running Events stmi2c_clear_pending_interrupts(i2c); // Now continue as if everything was normal from now on ret = STMI2C_SubTra_Ready; } /////////////////////////// // Normal Event: else { /////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// // // SUB-TRANSACTION HANDLER if (trans->type == I2CTransRx) // TxRx are converted to Rx after the Tx Part { switch (trans->len_r) { case 1: ret = stmi2c_read1(i2c,periph,trans); break; case 2: ret = stmi2c_read2(i2c,periph,trans); break; default: ret = stmi2c_readmany(i2c,periph,trans); break; } } else // TxRx or Tx { ret = stmi2c_send(i2c,periph,trans); } } ///////////////////////////////// // Sub-transaction has finished if (ret != STMI2C_SubTra_Busy) { // Ready or SubTraError // -ready: with or without stop already asked // In case of unexpected event condition during subtransaction handling: if (ret == STMI2C_SubTra_Error) { // Tell everyone about the subtransaction error: // this is the previously called SPURRIOUS INTERRUPT periph->status = I2CFailed; trans->type = I2CTransRx; // Avoid possible restart trans->status = I2CTransFailed; // Notify Ready periph->errors->unexpected_event_cnt++; // Error #ifdef I2C_DEBUG_LED LED2_ON(); LED1_ON(); LED2_OFF(); LED1_OFF(); LED_SHOW_ACTIVE_BITS(regs); #endif // Clear Running Events stmi2c_clear_pending_interrupts(i2c); } // RxTx -> Restart and do Rx part if (trans->type == I2CTransTxRx) { trans->type = I2CTransRx; periph->status = I2CStartRequested; i2c_send_start(i2c); // Silent any BTF that would occur before SB i2c_send_data(i2c, 0x00); } // If a restart is not needed: Rx part or Tx-only else { // Ready, no stop condition set yet if (ret == STMI2C_SubTra_Ready) { // Program a stop PPRZ_I2C_SEND_STOP(i2c); // Silent any BTF that would occur before STOP is executed i2c_send_data(i2c, 0x00); } // Jump to the next transaction periph->trans_extract_idx++; if (periph->trans_extract_idx >= I2C_TRANSACTION_QUEUE_LEN) periph->trans_extract_idx = 0; // Tell everyone we are ready periph->status = I2CIdle; // if we have no more transaction to process, stop here if (periph->trans_extract_idx == periph->trans_insert_idx) { periph->watchdog = -1; // stop watchdog #ifdef I2C_DEBUG_LED LED2_ON(); LED1_ON(); LED1_OFF(); LED1_ON(); LED1_OFF(); LED2_OFF(); #endif } // if not, start next transaction else { // Restart transaction doing the Rx part now // --- moved to idle function PPRZ_I2C_SEND_START(periph); // ------ } } } return; }