aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--os/hal/platforms/STM32/I2Cv1/i2c_lld.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/os/hal/platforms/STM32/I2Cv1/i2c_lld.c b/os/hal/platforms/STM32/I2Cv1/i2c_lld.c
index 5957266d4..1c325b9b0 100644
--- a/os/hal/platforms/STM32/I2Cv1/i2c_lld.c
+++ b/os/hal/platforms/STM32/I2Cv1/i2c_lld.c
@@ -290,6 +290,9 @@ static void i2c_lld_set_opmode(I2CDriver *i2cp) {
* @details Important but implicit destination of this function is
* clearing interrupts flags.
* @note Internal use only.
+ * @note Function reads SR2 <b>before</b> SR1. This is workaround, not a bug.
+ * ADDR was not cleared and it was possible to clear ADDR after
+ * transfer is properly setup (enabled DMA stream).
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
@@ -297,8 +300,8 @@ static void i2c_lld_set_opmode(I2CDriver *i2cp) {
*/
static uint32_t i2c_get_event(I2CDriver *i2cp) {
I2C_TypeDef *dp = i2cp->i2c;
- uint16_t regSR1 = dp->SR1;
uint16_t regSR2 = dp->SR2;
+ uint16_t regSR1 = dp->SR1;
#if CH_DBG_ENABLE_ASSERTS
dbgSR1 = regSR1;
@@ -319,15 +322,21 @@ static uint32_t i2c_get_event(I2CDriver *i2cp) {
*/
static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) {
I2C_TypeDef *dp = i2cp->i2c;
+ uint32_t event = i2c_get_event(i2cp);
+ uint32_t rxlen = dmaStreamGetTransactionSize(i2cp->dmarx);
/* Interrupts are disabled just before dmaStreamEnable() because there
is no need of interrupts until next transaction begin. All the work is
done by the DMA.*/
- switch (i2c_get_event(i2cp)) {
+ switch (event) {
case I2C_EV5_MASTER_MODE_SELECT:
dp->DR = i2cp->addr;
break;
case I2C_EV6_MASTER_REC_MODE_SELECTED:
+ if (rxlen <= 2)
+ dp->CR1 &= ~I2C_CR1_ACK;
+ if (rxlen == 2)
+ dp->CR1 |= I2C_CR1_POS;
dp->CR2 &= ~I2C_CR2_ITEVTEN;
dmaStreamEnable(i2cp->dmarx);
dp->CR2 |= I2C_CR2_LAST; /* Needed in receiver mode. */
@@ -338,7 +347,7 @@ static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) {
break;
case I2C_EV8_2_MASTER_BYTE_TRANSMITTED:
/* Catches BTF event after the end of transmission.*/
- if (dmaStreamGetTransactionSize(i2cp->dmarx) > 0) {
+ if (rxlen > 0) {
/* Starts "read after write" operation, LSB = 1 -> receive.*/
i2cp->addr |= 0x01;
dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
@@ -351,6 +360,10 @@ static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) {
default:
break;
}
+ /* Clear ADDR flag. Last part of workaround
+ (see comments to i2c_get_event() function). */
+ if (event & (I2C_SR1_ADDR | I2C_SR1_ADD10))
+ if (dp->SR2){;}
}
/**