summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-11-29 13:37:45 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-11-29 13:37:45 -0600
commitde235213f2fa1b302d94379e7e5376840ac40a6f (patch)
tree3ddbf7ff8d37523685095c866f9d61d9c9064041
parent2bb467f0fa91f650d2637418e5bf73f0833d4e6a (diff)
downloadnuttx-de235213f2fa1b302d94379e7e5376840ac40a6f.tar.gz
nuttx-de235213f2fa1b302d94379e7e5376840ac40a6f.tar.bz2
nuttx-de235213f2fa1b302d94379e7e5376840ac40a6f.zip
STM32 F4 I2C: Port Tridge's I2C noise resiliance logic from the PX4 repository.
-rw-r--r--nuttx/arch/arm/src/stm32/chip/stm32f103c_pinmap.h1
-rw-r--r--nuttx/arch/arm/src/stm32/stm32_i2c.c109
2 files changed, 87 insertions, 23 deletions
diff --git a/nuttx/arch/arm/src/stm32/chip/stm32f103c_pinmap.h b/nuttx/arch/arm/src/stm32/chip/stm32f103c_pinmap.h
index b4bbe7b8d..35838f70b 100644
--- a/nuttx/arch/arm/src/stm32/chip/stm32f103c_pinmap.h
+++ b/nuttx/arch/arm/src/stm32/chip/stm32f103c_pinmap.h
@@ -257,7 +257,6 @@
# define GPIO_USART3_RTS (GPIO_ALT|GPIO_CNF_AFPP|GPIO_MODE_50MHz|GPIO_PORTB|GPIO_PIN14)
#endif
-
#define GPIO_WKUP (GPIO_ALT|GPIO_CNF_AFPP|GPIO_MODE_50MHz|GPIO_PORTA|GPIO_PIN0)
/* These GPIOs are shared with JTAG / SWD. */
diff --git a/nuttx/arch/arm/src/stm32/stm32_i2c.c b/nuttx/arch/arm/src/stm32/stm32_i2c.c
index b8bbd5820..7f12c45bf 100644
--- a/nuttx/arch/arm/src/stm32/stm32_i2c.c
+++ b/nuttx/arch/arm/src/stm32/stm32_i2c.c
@@ -628,6 +628,7 @@ static inline int stm32_i2c_sem_waitdone(FAR struct stm32_i2c_priv_s *priv)
abstime.tv_nsec -= 1000 * 1000 * 1000;
}
#endif
+
/* Wait until either the transfer is complete or the timeout expires */
ret = sem_timedwait(&priv->sem_isr, &abstime);
@@ -1149,7 +1150,7 @@ static inline uint32_t stm32_i2c_disablefsmc(FAR struct stm32_i2c_priv_s *priv)
* Name: stm32_i2c_enablefsmc
*
* Description:
- * Re-enabled the FSMC
+ * Re-enable the FSMC
*
************************************************************************************/
@@ -1193,36 +1194,56 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv)
{
stm32_i2c_traceevent(priv, I2CEVENT_SENDADDR, priv->msgc);
- /* Get run-time data */
+ /* We check for msgc > 0 here as an unexpected interrupt with
+ * I2C_SR1_SB set due to noise on the I2C cable can otherwise cause
+ * msgc to wrap causing memory overwrite
+ */
- priv->ptr = priv->msgv->buffer;
- priv->dcnt = priv->msgv->length;
- priv->flags = priv->msgv->flags;
+ if (priv->msgc > 0 && priv->msgv != NULL)
+ {
+ /* Get run-time data */
- /* Send address byte and define addressing mode */
+ priv->ptr = priv->msgv->buffer;
+ priv->dcnt = priv->msgv->length;
+ priv->flags = priv->msgv->flags;
- stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET,
- (priv->flags & I2C_M_TEN) ?
- 0 : ((priv->msgv->addr << 1) | (priv->flags & I2C_M_READ)));
+ /* Send address byte and define addressing mode */
- /* Set ACK for receive mode */
+ stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET,
+ (priv->flags & I2C_M_TEN) ?
+ 0 : ((priv->msgv->addr << 1) | (priv->flags & I2C_M_READ)));
- if (priv->dcnt > 1 && (priv->flags & I2C_M_READ) != 0)
- {
- stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_ACK);
- }
+ /* Set ACK for receive mode */
- /* Increment to next pointer and decrement message count */
+ if (priv->dcnt > 1 && (priv->flags & I2C_M_READ) != 0)
+ {
+ stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET, 0, I2C_CR1_ACK);
+ }
- priv->msgv++;
- priv->msgc--;
+ /* Increment to next pointer and decrement message count */
+
+ priv->msgv++;
+ priv->msgc--;
+ }
+ else
+ {
+ /* Clear ISR by writing to DR register */
+
+ stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET, 0);
+ }
}
/* In 10-bit addressing mode, was first byte sent */
else if ((status & I2C_SR1_ADD10) != 0)
{
- /* TODO: Finish 10-bit mode addressing */
+ /* TODO: Finish 10-bit mode addressing.
+ *
+ * For now just clear ISR by writing to DR register. As we don't do
+ * 10 bit addressing this must be a spurious ISR
+ */
+
+ stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET, 0);
}
/* Was address sent, continue with either sending or reading data */
@@ -1283,6 +1304,37 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv)
irqrestore(state);
#endif
}
+ else
+ {
+ /* Throw away the unexpected byte */
+
+ stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
+ }
+ }
+ else if (status & I2C_SR1_TXE)
+ {
+ /* This should never happen, but it does happen occasionally with lots
+ * of noise on the bus. It means the peripheral is expecting more data
+ * bytes, but we don't have any to give.
+ */
+
+ stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET, 0);
+ }
+ else if (status & I2C_SR1_BTF)
+ {
+ /* We should have handled all cases where this could happen above, but
+ * just to ensure it gets ACKed, lets clear it here
+ */
+
+ stm32_i2c_getreg(priv, STM32_I2C_DR_OFFSET);
+ }
+ else if (status & I2C_SR1_STOPF)
+ {
+ /* We should never get this, as we are a master not a slave. Write CR1
+ * with its current value to clear the error
+ */
+
+ stm32_i2c_modifyreg(priv, STM32_I2C_CR1_OFFSET, 0, 0);
}
/* Do we have more bytes to send, enable/disable buffer interrupts
@@ -1601,13 +1653,13 @@ static int stm32_i2c_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits)
static int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs,
int count)
{
- struct stm32_i2c_inst_s *inst = (struct stm32_i2c_inst_s *)dev;
+ struct stm32_i2c_inst_s *inst = (struct stm32_i2c_inst_s *)dev;
FAR struct stm32_i2c_priv_s *priv = inst->priv;
- uint32_t status = 0;
+ uint32_t status = 0;
#ifdef I2C1_FSMC_CONFLICT
- uint32_t ahbenr;
+ uint32_t ahbenr;
#endif
- int errval = 0;
+ int errval = 0;
ASSERT(count);
@@ -1640,6 +1692,13 @@ static int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *ms
/* Old transfers are done */
+ /* Reset ptr and dcnt to ensure an unexpected data interrupt doesn't
+ * overwrite stale data.
+ */
+
+ priv->dcnt = 0;
+ priv->ptr = NULL;
+
priv->msgv = msgs;
priv->msgc = count;
@@ -1773,6 +1832,12 @@ static int stm32_i2c_process(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *ms
stm32_i2c_enablefsmc(ahbenr);
#endif
+
+ /* Ensure that any ISR happening after we finish can't overwrite any user data */
+
+ priv->dcnt = 0;
+ priv->ptr = NULL;
+
stm32_i2c_sem_post(dev);
return -errval;