summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/stm32/stm32_i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/arch/arm/src/stm32/stm32_i2c.c')
-rw-r--r--nuttx/arch/arm/src/stm32/stm32_i2c.c80
1 files changed, 61 insertions, 19 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_i2c.c b/nuttx/arch/arm/src/stm32/stm32_i2c.c
index 15f22e602..dd551bf3b 100644
--- a/nuttx/arch/arm/src/stm32/stm32_i2c.c
+++ b/nuttx/arch/arm/src/stm32/stm32_i2c.c
@@ -429,12 +429,25 @@ static inline void stm32_i2c_sendstart(FAR struct stm32_i2c_priv_s *priv)
static inline void stm32_i2c_clrstart(FAR struct stm32_i2c_priv_s *priv)
{
- /* "This [START] bit is set and cleared by software and cleared by hardware
+ /* "Note: When the STOP, START or PEC bit is set, the software must
+ * not perform any write access to I2C_CR1 before this bit is
+ * cleared by hardware. Otherwise there is a risk of setting a
+ * second STOP, START or PEC request."
+ *
+ * "The [STOP] bit is set and cleared by software, cleared by hardware
+ * when a Stop condition is detected, set by hardware when a timeout
+ * error is detected.
+ *
+ * "This [START] bit is set and cleared by software and cleared by hardware
* when start is sent or PE=0." The bit must be cleared by software if the
* START is never sent.
+ *
+ * "This [PEC] bit is set and cleared by software, and cleared by hardware
+ * when PEC is transferred or by a START or Stop condition or when PE=0."
*/
- stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_START, 0);
+ stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET,
+ I2C_CR1_START|I2C_CR1_STOP|I2C_CR1_PEC, 0);
}
static inline void stm32_i2c_sendstop(FAR struct stm32_i2c_priv_s *priv)
@@ -871,6 +884,7 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
struct stm32_i2c_inst_s *inst = (struct stm32_i2c_inst_s *)dev;
uint32_t status = 0;
uint32_t ahbenr;
+ uint16_t regval;
int status_errno = 0;
ASSERT(count);
@@ -879,15 +893,43 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
ahbenr = stm32_i2c_disablefsmc(inst->priv);
- /* Wait as stop might still be in progress
- *
- * \todo GET RID OF THIS PERFORMANCE LOSS and for() loop
+ /* Wait as stop might still be in progress; but stop might also
+ * be set because of a timeout error: "The [STOP] bit is set and
+ * cleared by software, cleared by hardware when a Stop condition is
+ * detected, set by hardware when a timeout error is detected."
*/
-
- for (; stm32_i2c_getreg(inst->priv, STM32_I2C_CR1_OFFSET) & I2C_CR1_STOP; )
+
+ for (;;)
{
- up_waste();
+ /* Check for STOP condition */
+
+ regval = stm32_i2c_getreg(inst->priv, STM32_I2C_CR1_OFFSET);
+ if ((regval & I2C_CR1_STOP) == 0)
+ {
+ break;
+ }
+
+ /* Check for timeout error */
+
+ regval = stm32_i2c_getreg(inst->priv, STM32_I2C_SR1_OFFSET);
+ if ((regval & I2C_SR1_TIMEOUT) != 0)
+ {
+ break;
+ }
}
+
+ /* Clear any pending error interrupts */
+
+ stm32_i2c_putreg(inst->priv, STM32_I2C_SR1_OFFSET, 0);
+
+ /* "Note: When the STOP, START or PEC bit is set, the software must
+ * not perform any write access to I2C_CR1 before this bit is
+ * cleared by hardware. Otherwise there is a risk of setting a
+ * second STOP, START or PEC request." However, if the bits are
+ * not cleared by hardware, then we will have to do that from hardware.
+ */
+
+ stm32_i2c_clrstart(inst->priv);
/* Old transfers are done */
@@ -898,10 +940,6 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
stm32_i2c_setclock(inst->priv, inst->frequency);
- /* Clear any pending error interrupts */
-
- stm32_i2c_putreg(inst->priv, STM32_I2C_SR1_OFFSET, 0);
-
/* Trigger start condition, then the process moves into the ISR. I2C
* interrupts will be enabled within stm32_i2c_waitisr().
*/
@@ -917,10 +955,10 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
status = stm32_i2c_getstatus(inst->priv);
status_errno = ETIMEDOUT;
- /* " Note: When the STOP, START or PEC bit is set, the software must
- * not perform any write access to I2C_CR1 before this bit is
- * cleared by hardware. Otherwise there is a risk of setting a
- * second STOP, START or PEC request."
+ /* "Note: When the STOP, START or PEC bit is set, the software must
+ * not perform any write access to I2C_CR1 before this bit is
+ * cleared by hardware. Otherwise there is a risk of setting a
+ * second STOP, START or PEC request."
*/
stm32_i2c_clrstart(inst->priv);
@@ -936,6 +974,8 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
if ((status & I2C_SR1_ERRORMASK) != 0)
{
+ /* I2C_SR1_ERRORMASK is the 'OR' of the following individual bits: */
+
if (status & I2C_SR1_BERR)
{
/* Bus Error */
@@ -975,7 +1015,7 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
/* This is not an error and should never happen since SMBus is not enabled */
- else if (status & I2C_SR1_SMBALERT)
+ else /* if (status & I2C_SR1_SMBALERT) */
{
/* SMBus alert is an optional signal with an interrupt line for devices
* that want to trade their ability to master for a pin.
@@ -987,9 +1027,11 @@ int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int
/* This is not an error, but should not happen. The BUSY signal can hang,
* however, if there are unhealthy devices on the bus that need to be reset.
+ * NOTE: We will only see this buy indication if stm32_i2c_sem_waitisr()
+ * fails above; Otherwise it is cleared.
*/
- else if (status & (I2C_SR2_BUSY << 16))
+ else if ((status & (I2C_SR2_BUSY << 16)) != 0)
{
/* I2C Bus is for some reason busy */
@@ -1016,7 +1058,7 @@ int stm32_i2c_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int buflen
.buffer = (uint8_t *)buffer,
.length = buflen
};
-
+
return stm32_i2c_process(dev, &msgv, 1);
}