From e542ba6e4a4baaaeabc3b7c9a5e539779b61fd7f Mon Sep 17 00:00:00 2001
From: EmilioCorigliano <emilio.corigliano@mail.polimi.it>
Date: Thu, 20 Apr 2023 01:10:04 +0200
Subject: [PATCH] [I2C] Now repeated start works properly

For the repeated start, we have to keep the AUTOEND flag reset for both the transactions before and after the reStart. Not doing so would lead to a premature STOP condition and the failure of the reStart
---
 src/shared/drivers/i2c/I2CDriver-f7.cpp | 90 ++++++++++++++++---------
 1 file changed, 60 insertions(+), 30 deletions(-)

diff --git a/src/shared/drivers/i2c/I2CDriver-f7.cpp b/src/shared/drivers/i2c/I2CDriver-f7.cpp
index 606e3835a..a589d3334 100644
--- a/src/shared/drivers/i2c/I2CDriver-f7.cpp
+++ b/src/shared/drivers/i2c/I2CDriver-f7.cpp
@@ -412,11 +412,16 @@ bool I2CDriver::doOperation(const I2CSlaveConfig &slaveConfig)
         setupPeripheral(slaveConfig);
     }
 
-    reStarting = false;
-
     // Setting up transaction
     setupTransaction();
 
+    // Sending STOP condition and wake up thread
+    reStarting = false;
+
+    // clearing pending flags
+    i2c->ICR |= (I2C_ICR_ADDRCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF |
+                 I2C_ICR_OVRCF | I2C_ICR_STOPCF);
+
     // Starting the transaction with the START bit
     // From the wait till the end of transaction it will all be executed in the
     // event Interrupt Service Routine
@@ -506,8 +511,13 @@ void I2CDriver::setupTransaction()
         i2c->CR2 &= ~I2C_CR2_RD_WRN;
     }
 
-    // setting automatic stop generation if we won't generate a reStart
-    if (transaction.generateStop)
+    // setting automatic stop generation if we won't generate a reStart.
+    // For a bug in the peripheral (or a behaviour not explained in the
+    // datasheet), when we have to issue a reStart condition, we can't set the
+    // AUTOEND flag because it will generate the STOP condition prematurely. So,
+    // in this case, we will generate the STOP condition manually at the end of
+    // the transaction.
+    if (transaction.generateStop && !reStarting)
     {
         i2c->CR2 |= I2C_CR2_AUTOEND;
     }
@@ -522,17 +532,17 @@ void I2CDriver::setupTransaction()
 
 void I2CDriver::setupReload()
 {
-    if (transaction.nBytes - transaction.nBytesDone <= 0xffu)
+    if ((transaction.nBytes - transaction.nBytesDone) <= 0xffu)
     {
         i2c->CR2 &= ~I2C_CR2_RELOAD;
-        i2c->CR2 |= (transaction.nBytes - transaction.nBytesDone)
-                    << I2C_CR2_NBYTES_Pos;
+        i2c->CR2 |= ((transaction.nBytes - transaction.nBytesDone)
+                     << I2C_CR2_NBYTES_Pos);
     }
     else
     {
         i2c->CR2 |=
-            I2C_CR2_RELOAD |             // There must be a reload
-            0xff << I2C_CR2_NBYTES_Pos;  // maximum bytes that can be sent
+            (I2C_CR2_RELOAD |                // There must be a reload
+             (0xff << I2C_CR2_NBYTES_Pos));  // maximum bytes that can be sent
     }
 }
 
@@ -632,7 +642,9 @@ inline void I2CDriver::IRQwakeUpWaitingThread()
     i2c->CR1 &= ~(I2C_CR1_ERRIE |   // interrupt for errors
                   I2C_CR1_NACKIE |  // interrupt for NACKs
                   I2C_CR1_TCIE |    // interrupt for TC and TCR
-                  I2C_CR1_STOPIE);  // interrupt for STOP detected
+                  I2C_CR1_STOPIE |  // interrupt for STOP detected
+                  I2C_CR1_TXIE |    // interrupt for tx buffer
+                  I2C_CR1_RXIE);    // interrupt for rx buffer
 
     if (waiting)
     {
@@ -650,12 +662,6 @@ inline void I2CDriver::IRQwakeUpWaitingThread()
 
 void I2CDriver::IRQhandleInterrupt()
 {
-    // Transfer complete reload: setting registers for the remaining bytes
-    if (i2c->ISR & I2C_ISR_TCR)
-    {
-        setupReload();
-    }
-
     // If NACK reception, return error
     if (i2c->ISR & I2C_ISR_NACKF)
     {
@@ -677,39 +683,63 @@ void I2CDriver::IRQhandleInterrupt()
             transaction.buffRead[transaction.nBytesDone] = i2c->RXDR;
             transaction.nBytesDone++;
         }
-        else if (i2c->ISR & I2C_ISR_TXIS)
+        else if (i2c->ISR & (I2C_ISR_TXIS | I2C_ISR_TXE))
         {
             // WRITE
             i2c->TXDR = transaction.buffWrite[transaction.nBytesDone];
             transaction.nBytesDone++;
         }
     }
-    else
-    {
-        // Sending STOP condition and wake up thread
-        if (!transaction.generateStop)
-        {
-            reStarting = true;
 
-            // waking up the waiting thread
-            IRQwakeUpWaitingThread();
-        }
+    // Transfer complete reload: setting registers for the remaining bytes
+    if (i2c->ISR & I2C_ISR_TCR)
+    {
+        setupReload();
     }
 
+    // when stop detected on the bus
     if (i2c->ISR & I2C_ISR_STOPF)
     {
-        // waking up the waiting thread after stop condition generated
+        // clearing STOPF
+        i2c->ICR |= I2C_ICR_STOPCF;
+
+        if (transaction.nBytesDone < transaction.nBytes)
+        {
+            error = 1 << 14;
+            lastError |= Errors::BERR;
+        }
+
+        // waking up the waiting thread
         IRQwakeUpWaitingThread();
+        return;
+    }
+
+    // Transfer complete (RELOAD = 0, AUTOEND = 0, NBYTES transferred)
+    if ((i2c->ISR & I2C_ISR_TC) &&
+        (transaction.nBytesDone >= transaction.nBytes))
+    {
+        if (transaction.generateStop)
+        {
+            // stop and wait for STOPF event
+            i2c->CR2 |= I2C_CR2_STOP;
+        }
+        else
+        {
+            reStarting = true;
+            // waking up the waiting thread
+            IRQwakeUpWaitingThread();
+            return;
+        }
     }
 }
 
 void I2CDriver::IRQhandleErrInterrupt()
 {
-    error = i2c->ISR | (1 << 24);
+    error = (i2c->ISR | (1 << 24));
 
     // Clearing all the errors in the register
-    i2c->ICR |= I2C_ICR_ADDRCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF |
-                I2C_ICR_OVRCF | I2C_ICR_STOPCF;
+    i2c->ICR |= (I2C_ICR_ADDRCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF |
+                 I2C_ICR_OVRCF | I2C_ICR_STOPCF);
 
     // Waking up the waiting thread
     IRQwakeUpWaitingThread();
-- 
GitLab