in arch/arm/src/stm32/stm32_i2c_alt.c [1138:1880]
static int stm32_i2c_isr_process(struct stm32_i2c_priv_s *priv)
{
#ifndef CONFIG_I2C_POLLED
uint32_t regval;
#endif
uint32_t status;
i2cinfo("I2C ISR called\n");
/* Get state of the I2C controller (register SR1 only)
*
* Get control register SR1 only as reading both SR1 and SR2 clears the
* ADDR flag(possibly others) causing the hardware to advance to the
* next state without the proper action being taken.
*/
status = stm32_i2c_getreg(priv, STM32_I2C_SR1_OFFSET);
/* Update private version of the state */
priv->status = status;
/* Check if this is a new transmission so to set up the
* trace table accordingly.
*/
stm32_i2c_tracenew(priv, status);
stm32_i2c_traceevent(priv, I2CEVENT_ISR_CALL, 0);
/* Messages handling (1/2)
*
* Message handling should only operate when a message has been completely
* sent and after the ISR had the chance to run to set bits after the last
* written/read byte, i.e. priv->dcnt == -1. This is also the case in when
* the ISR is called for the first time. This can seen in
* stm32_i2c_transfer() before entering the stm32_i2c_sem_waitdone()
* waiting process.
*
* Message handling should only operate when:
* - A message has been completely sent and there are still messages
* to send(i.e. msgc > 0).
* - After the ISR had the chance to run to set start bit or
* termination flags after the last written/read byte(after last byte
* dcnt=0, msg handling dcnt = -1).
*
* When the ISR is called for the first time the same conditions hold.
* This can seen in stm32_i2c_transfer() before entering the
* stm32_i2c_sem_waitdone() waiting process.
*/
if (priv->dcnt == -1 && priv->msgc > 0)
{
i2cinfo("Switch to new message\n");
/* Get current message to process data and copy to private structure */
priv->ptr = priv->msgv->buffer; /* Copy buffer to private struct */
priv->dcnt = priv->msgv->length; /* Set counter of current msg length */
priv->total_msg_len = priv->msgv->length; /* Set total msg length */
priv->flags = priv->msgv->flags; /* Copy flags to private struct */
i2cinfo("Current flags %i\n", priv->flags);
/* Decrease counter to indicate the number of messages left to
* process
*/
priv->msgc--;
/* Decrease message pointer. If last message set next message vector
* to null
*/
if (priv->msgc == 0)
{
/* No more messages, don't need to increment msgv. This pointer
* will be set to zero when reaching the termination of the ISR
* calls, i.e. Messages handling(2/2).
*/
}
else
{
/* If not last message increment to next message to process */
priv->msgv++;
}
/* Trace event */
stm32_i2c_traceevent(priv, I2CEVENT_MSG_HANDLING, priv->msgc);
}
/* Note the event where we are on the last message and after the last
* byte is handled at the bottom of this function, as it terminates
* the repeated calls to the ISR.
*/
/* I2C protocol logic
*
* I2C protocol logic follows. It's organized in an if else chain such that
* only one mode of operation is executed every time the ISR is called.
*/
/* Address Handling
*
* Check if a start bit was set and transmit address with proper format.
*
* Note:
* On first call the start bit has been set by stm32_i2c_waitdone()
* Otherwise it will be set from this ISR.
*
* Remember that after a start bit an address has always to be sent.
*/
if ((status & I2C_SR1_SB) != 0)
{
/* Start bit is set */
i2cinfo("Entering address handling, status = %" PRIi32 "\n", status);
/* Check for empty message (for robustness) */
if (priv->dcnt > 0)
{
/* When reading messages of length 1 or 2 actions have to be taken
* during this event. The following block handles that.
*/
if (priv->total_msg_len == 1 && (priv->flags & I2C_M_READ))
{
i2cinfo("short read N=1: setting NACK\n");
/* Set POS bit to zero
* (can be up from a previous 2 byte receive)
*/
stm32_i2c_modifyreg(priv,
STM32_I2C_CR1_OFFSET, I2C_CR1_POS, 0);
/* Immediately set NACK */
stm32_i2c_modifyreg(priv,
STM32_I2C_CR1_OFFSET, I2C_CR1_ACK, 0);
stm32_i2c_traceevent(priv, I2CEVENT_ADDR_HDL_READ_1, 0);
}
else if (priv->total_msg_len == 2 && (priv->flags & I2C_M_READ))
{
i2cinfo("short read N=2: setting POS and ACK bits\n");
stm32_i2c_modifyreg(priv,
STM32_I2C_CR1_OFFSET, 0, I2C_CR1_POS);
stm32_i2c_modifyreg(priv,
STM32_I2C_CR1_OFFSET, 0, I2C_CR1_ACK);
stm32_i2c_traceevent(priv,
I2CEVENT_ADDR_HDL_READ_2, 0);
}
else
{
/* Enable ACK after address byte */
i2cinfo("setting ACK\n");
/* Set POS bit to zero
* (can be up from a previous 2 byte receive)
*/
stm32_i2c_modifyreg(priv,
STM32_I2C_CR1_OFFSET, I2C_CR1_POS, 0);
/* ACK is the expected answer for N>=3 reads and writes */
stm32_i2c_modifyreg(priv,
STM32_I2C_CR1_OFFSET, 0, I2C_CR1_ACK);
}
/* Send address byte with correct 8th bit set(for writing or
* reading) Transmission happens after having written to the
* data register STM32_I2C_DR
*/
stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET,
(priv->flags & I2C_M_TEN) ?
0 : ((priv->msgv->addr << 1) |
(priv->flags & I2C_M_READ)));
i2cinfo("Address sent. Addr=%#02x Write/Read bit=%i\n",
priv->msgv->addr, (priv->flags & I2C_M_READ));
/* Flag that address has just been sent */
priv->check_addr_ack = true;
stm32_i2c_traceevent(priv, I2CEVENT_SENDADDR, priv->msgv->addr);
}
else
{
/* TODO: untested!! */
i2cwarn("WARNING: An empty message has been detected, "
"ignoring and passing to next message.\n");
/* Trace event */
stm32_i2c_traceevent(priv, I2CEVENT_EMPTY_MSG, 0);
/* Set condition to activate msg handling */
priv->dcnt = -1;
/* Restart ISR by setting an interrupt buffer bit */
stm32_i2c_modifyreg(priv,
STM32_I2C_CR2_OFFSET, 0, I2C_CR2_ITBUFEN);
}
}
/* Address cleared event
*
* Check if the address cleared, i.e. the driver found a valid
* address.
* If a NACK was received the address is invalid, if an ACK was
* received the address is valid and transmission can continue.
*/
/* Check for NACK after an address */
#ifndef CONFIG_I2C_POLLED
/* When polling the i2c ISR it's not possible to determine when
* an address has been ACKed(i.e. the address is valid).
*
* The mechanism to deal a NACKed address is to wait for the I2C
* call to timeout (value defined in defconfig by one of the
* following: CONFIG_STM32_I2C_DYNTIMEO, CONFIG_STM32_I2CTIMEOSEC,
* CONFIG_STM32_I2CTIMEOMS, CONFIG_STM32_I2CTIMEOTICKS).
*
* To be safe in the case of a timeout/NACKed address a stop bit
* is set on the bus to clear it. In POLLED operation it's done
* stm32_i2c_transfer() after the call to stm32_i2c_sem_waitdone().
*
* In ISR driven operation the stop bit in case of a NACKed address
* is set in the ISR itself.
*
* Note: this commentary is found in both places.
*/
else if ((status & I2C_SR1_ADDR) == 0 && priv->check_addr_ack)
{
i2cinfo("Invalid Address.");
i2cinfo(" Setting stop bit and clearing message\n");
i2cinfo("status %" PRIi32 "\n", status);
/* Set condition to terminate msg chain transmission as address
* is invalid.
*/
priv->dcnt = -1;
priv->msgc = 0;
i2cinfo("dcnt %i , msgc %i\n", priv->dcnt, priv->msgc);
/* Reset flag to check for valid address */
priv->check_addr_ack = false;
/* Send stop bit to clear bus */
stm32_i2c_sendstop(priv);
/* Trace event */
stm32_i2c_traceevent(priv,
I2CEVENT_ADDRESS_NACKED, priv->msgv->addr);
}
#endif
/* ACK in read mode, ACK in write mode is handled separately */
else if ((priv->flags & I2C_M_READ) != 0 && (status & I2C_SR1_ADDR) != 0 &&
priv->check_addr_ack)
{
/* Reset check addr flag as we are handling this event */
priv->check_addr_ack = false;
/* Clear ADDR flag by reading SR2 and adding it to status */
status |= (stm32_i2c_getreg(priv, STM32_I2C_SR2_OFFSET) << 16);
/* Note:
*
* When reading a single byte the stop condition has to be set
* immediately after clearing the state flags, which happens
* when reading SR2(as SR1 has already been read).
*
* Similarly when reading 2 bytes the NACK bit has to be set as just
* after the clearing of the address.
*/
if (priv->dcnt == 1 && priv->total_msg_len == 1)
{
/* this should only happen when receiving a message of length 1 */
stm32_i2c_modifyreg(priv,
STM32_I2C_CR2_OFFSET, 0, I2C_CR2_ITBUFEN);
stm32_i2c_sendstop(priv);
i2cinfo("Address ACKed beginning data reception\n");
i2cinfo("short read N=1: programming stop bit\n");
priv->dcnt--;
/* Trace */
stm32_i2c_traceevent(priv, I2CEVENT_ADDRESS_ACKED_READ_1, 0);
}
else if (priv->dcnt == 2 && priv->total_msg_len == 2)
{
/* This should only happen when receiving a message of length 2
* Set NACK
*/
stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_ACK, 0);
i2cinfo("Address ACKed beginning data reception\n");
i2cinfo("short read N=2: programming NACK\n");
/* Trace */
stm32_i2c_traceevent(priv, I2CEVENT_ADDRESS_ACKED_READ_2, 0);
}
else
{
i2cinfo("Address ACKed beginning data reception\n");
/* Trace */
stm32_i2c_traceevent(priv, I2CEVENT_ADDRESS_ACKED, 0);
}
}
/* Write mode
*
* Handles all write related I2C protocol logic. Also handles the
* ACK event after clearing the ADDR flag as the write has to
* begin immediately after.
*/
else if ((priv->flags & (I2C_M_READ)) == 0 &&
(status & (I2C_SR1_ADDR | I2C_SR1_TXE)) != 0)
{
/* The has cleared(ADDR is set, ACK was received after the address)
* or the transmit buffer is empty flag has been set(TxE) then we can
* transmit the next byte.
*/
i2cinfo("Entering write mode dcnt = %i msgc = %i\n",
priv->dcnt, priv->msgc);
/* Clear ADDR flag by reading SR2 and adding it to status */
status |= (stm32_i2c_getreg(priv, STM32_I2C_SR2_OFFSET) << 16);
/* Address has cleared so don't check on next call */
priv->check_addr_ack = false;
/* Check if we have transmitted the whole message or we are after
* the last byte where the stop condition or else(according to the
* msg flags) has to be set.
*/
if (priv->dcnt >= 1)
{
/* Transmitting message.
* Send byte == write data into write register
*/
stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET, *priv->ptr++);
/* Decrease current message length */
stm32_i2c_traceevent(priv, I2CEVENT_WRITE_TO_DR, priv->dcnt);
priv->dcnt--;
}
else if (priv->dcnt == 0)
{
/* After last byte, check what to do based on next message flags */
if (priv->msgc == 0)
{
/* If last message send stop bit */
stm32_i2c_sendstop(priv);
i2cinfo("Stop sent dcnt = %i msgc = %i\n",
priv->dcnt, priv->msgc);
/* Decrease counter to get to next message */
priv->dcnt--;
i2cinfo("dcnt %i\n", priv->dcnt);
stm32_i2c_traceevent(priv, I2CEVENT_WRITE_STOP, priv->dcnt);
}
/* If there is a next message with no flags or the read flag
* a restart sequence has to be sent.
* Note msgv already points to the next message.
*/
else if (priv->msgc > 0 &&
(priv->msgv->flags == 0 ||
(priv->msgv[0].flags & I2C_M_READ) != 0))
{
stm32_i2c_sendstart(priv);
i2cinfo("Restart detected!\n");
i2cinfo("Nextflag %i\n", priv->msgv[0].flags);
/* Decrease counter to get to next message */
priv->dcnt--;
i2cinfo("dcnt %i\n", priv->dcnt);
stm32_i2c_traceevent(priv, I2CEVENT_WRITE_RESTART, priv->dcnt);
}
/* If there is a next message with the NO_RESTART flag
* do nothing.
*/
else if (priv->msgc > 0 &&
((priv->msgv->flags & I2C_M_NOSTART) != 0))
{
/* Set condition to get to next message */
priv->dcnt = -1;
stm32_i2c_traceevent(priv,
I2CEVENT_WRITE_NO_RESTART, priv->dcnt);
}
else
{
i2cerr(
"ERROR: Write mode: next message has an unrecognized flag.\n");
stm32_i2c_traceevent(priv, I2CEVENT_WRITE_FLAG_ERROR,
priv->msgv->flags);
}
}
else
{
i2cerr("ERROR: Write mode error.\n");
stm32_i2c_traceevent(priv, I2CEVENT_WRITE_ERROR, 0);
}
}
/* Read mode
*
* Handles all read related I2C protocol logic.
*
* * * * * * * WARNING STM32F1xx HARDWARE ERRATA * * * * * * *
* source: https://github.com/hikob/openlab/blob/master/drivers/stm32/i2c.c
*
* RXNE-only events should not be handled since it sometimes
* fails. Only BTF & RXNE events should be handled (with the
* consequence of slowing down the transfer).
*
* It seems that when a RXNE interrupt is handled 'around'
* the end of the next byte reception, the DR register read
* is ignored by the i2c controller: it does not flush the
* DR with next byte
*
* Thus we read twice the same byte and we read effectively
* read one byte less than expected from the i2c slave point
* of view.
*
* Example:
* + we want to receive 6 bytes (B1 to B6)
* + the problem appear when reading B3
* -> we read B1 B2 B3 B3 B4 B5(B3 twice)
* -> the i2c transfer was B1 B2 B3 B4 B5(B6 is not sent)
*/
else if ((priv->flags & (I2C_M_READ)) != 0 && (status & I2C_SR1_RXNE) != 0)
{
/* When read flag is set and the receive buffer is not empty
* (RXNE is set) then the driver can read from the data register.
*/
i2cinfo("Entering read mode dcnt = %i msgc = %i, status %" PRIi32 "\n",
priv->dcnt, priv->msgc, status);
/* Implementation of method 2 for receiving data following
* the stm32f1xx reference manual.
*/
/* Case total message length = 1 */
if (priv->dcnt == 0 && priv->total_msg_len == 1)
{
i2cinfo("short read N=1: Read data from data register(DR)\n");
*priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
priv->dcnt--;
stm32_i2c_traceevent(priv, I2CEVENT_READ, 0);
}
/* Case total message length = 2 */
else if (priv->dcnt == 2 && priv->total_msg_len == 2 &&
!(status & I2C_SR1_BTF))
{
i2cinfo("short read N=2: "
"DR full, SR empty. Waiting for more bytes.\n");
stm32_i2c_traceevent(priv, I2CEVENT_READ_SR_EMPTY, 0);
}
else if (priv->dcnt == 2 && priv->total_msg_len == 2 &&
(status & I2C_SR1_BTF))
{
i2cinfo("short read N=2: "
"DR and SR full setting stop bit and reading twice\n");
stm32_i2c_sendstop(priv);
*priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
priv->dcnt--;
*priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
priv->dcnt--;
/* Stop request already programmed so set dcnt for next message */
priv->dcnt--;
/* Set trace */
stm32_i2c_traceevent(priv, I2CEVENT_READ_2, 0);
}
/* Case total message length >= 3 */
else if (priv->total_msg_len >= 3 && !(status & I2C_SR1_BTF))
{
/* If the shift register is still empty (i.e. BTF is low)
* then do nothing and wait for it to fill in the next ISR.
* (should not happen in ISR mode, but if using polled mode
* this should be able to handle it).
*/
i2cinfo("DR full, SR empty. Waiting for more bytes.\n");
stm32_i2c_traceevent(priv, I2CEVENT_READ_SR_EMPTY, 0);
}
else if (priv->dcnt >= 4 &&
priv->total_msg_len >= 3 && (status & I2C_SR1_BTF))
{
/* Read data from data register(DR). Note this clears the
* RXNE(receive buffer not empty) flag.
*/
i2cinfo("Read data from data register(DR)\n");
*priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
/* Decrease current message length */
priv->dcnt--;
stm32_i2c_traceevent(priv, I2CEVENT_READ, 0);
}
else if (priv->dcnt == 3 &&
(status & I2C_SR1_BTF) && priv->total_msg_len >= 3)
{
/* This means that we are reading dcnt 3 and there is
* already dcnt 2 in the shift register.
* This coincides with EV7_2 in the reference manual.
*/
i2cinfo("Program NACK\n");
i2cinfo("Read data from data register(DR) dcnt=3\n");
stm32_i2c_traceevent(priv, I2CEVENT_READ_3, priv->dcnt);
/* Program NACK */
stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_ACK, 0);
/* Read dcnt = 3, to ensure a BTF event after having received
* in the shift register.
*/
*priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
/* Decrease current message length */
priv->dcnt--;
}
else if (priv->dcnt == 2 &&
(status & I2C_SR1_BTF) && priv->total_msg_len >= 3)
{
i2cinfo("Program stop\n");
i2cinfo("Read data from data register(DR) dcnt=2\n");
i2cinfo("Read data from data register(SR) dcnt=1\n");
i2cinfo("Setting condition to stop ISR dcnt = -1\n");
stm32_i2c_traceevent(priv, I2CEVENT_READ_3, priv->dcnt);
/* Program stop */
stm32_i2c_sendstop(priv);
/* read dcnt = 2 */
*priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
/* read last byte dcnt=1 */
*priv->ptr++ = stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
/* Stop already sent will not get another interrupt set
* condition to stop ISR
*/
priv->dcnt = -1;
}
/* Error handling for read mode */
else
{
i2cerr("ERROR: I2C read mode no correct state detected\n");
i2cerr(" state %" PRIi32 ", dcnt=%i\n", status, priv->dcnt);
/* set condition to terminate ISR and wake waiting thread */
priv->dcnt = -1;
priv->msgc = 0;
stm32_i2c_traceevent(priv, I2CEVENT_READ_ERROR, 0);
}
/* Read rest of the state */
status |= (stm32_i2c_getreg(priv, STM32_I2C_SR2_OFFSET) << 16);
}
/* Empty call handler
*
* Case to handle an empty call to the ISR where it only has to
* Shutdown
*/
else if (priv->dcnt == -1 && priv->msgc == 0)
{
/* Read rest of the state */
status |= (stm32_i2c_getreg(priv, STM32_I2C_SR2_OFFSET) << 16);
i2cwarn("WARNING: Empty call to ISR: Stopping ISR\n");
stm32_i2c_traceevent(priv, I2CEVENT_ISR_EMPTY_CALL, 0);
}
/* Error handler
*
* Gets triggered if the driver does not recognize a situation(state)
* it can deal with.
* This should not happen in interrupt based operation(i.e. when
* CONFIG_I2C_POLLED is not set in the defconfig file).
* During polled operation(i.e. CONFIG_I2C_POLLED=y in defconfig)
* this case should do nothing but tracing the event that the
* device wasn't ready yet.
*/
else
{
#ifdef CONFIG_I2C_POLLED
stm32_i2c_traceevent(priv, I2CEVENT_POLL_DEV_NOT_RDY, 0);
#else
/* Read rest of the state */
status |= (stm32_i2c_getreg(priv, STM32_I2C_SR2_OFFSET) << 16);
i2cerr("ERROR: "
"No correct state detected(start bit, read or write)\n");
i2cerr(" state %" PRIi32 "\n", status);
/* Set condition to terminate ISR and wake waiting thread */
priv->dcnt = -1;
priv->msgc = 0;
stm32_i2c_traceevent(priv, I2CEVENT_STATE_ERROR, 0);
#endif
}
/* Messages handling(2/2)
*
* Transmission of the whole message chain has been completed. We have to
* terminate the ISR and wake up stm32_i2c_transfer() that is waiting for
* the ISR cycle to handle the sending/receiving of the messages.
*/
/* First check for errors */
if ((status & I2C_SR1_ERRORMASK) != 0)
{
stm32_i2c_traceevent(priv,
I2CEVENT_ISR_SR1ERROR,
status & I2C_SR1_ERRORMASK);
/* Clear interrupt flags */
stm32_i2c_putreg(priv, STM32_I2C_SR1_OFFSET, 0);
priv->dcnt = -1;
priv->msgc = 0;
}
if (priv->dcnt == -1 && priv->msgc == 0)
{
i2cinfo("Shutting down I2C ISR\n");
stm32_i2c_traceevent(priv, I2CEVENT_ISR_SHUTDOWN, 0);
/* Clear internal pointer to the message content.
* Good practice + done by last implementation when messages are
* finished (compatibility concerns)
*/
priv->msgv = NULL;
#ifdef CONFIG_I2C_POLLED
priv->intstate = INTSTATE_DONE;
#else
/* Clear all interrupts */
regval = stm32_i2c_getreg(priv, STM32_I2C_CR2_OFFSET);
regval &= ~I2C_CR2_ALLINTS;
stm32_i2c_putreg(priv, STM32_I2C_CR2_OFFSET, regval);
/* Is there a thread waiting for this event(there should be) */
if (priv->intstate == INTSTATE_WAITING)
{
/* Yes.. inform the thread that the transfer is complete
* and wake it up.
*/
nxsem_post(&priv->sem_isr);
priv->intstate = INTSTATE_DONE;
}
#endif
}
return OK;
}