InfiniTime.git

commit 45e65b66b11b6b8533b225067ce4c2a4b5eac653

Author: JF002 <JF002@users.noreply.github.com>

Merge pull request #111 from JF002/fix-twi-hang

Workaround for TWI driver freeze

 src/drivers/Cst816s.cpp | 4 +
 src/drivers/Cst816s.h | 14 +++---
 src/drivers/TwiMaster.cpp | 95 ++++++++++++++++++++++++++++++++++++----
 src/drivers/TwiMaster.h | 16 ++++--


diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp
index f6816545f80d134bd98982c4d9b85dbdc593841e..94db3b34b23b98be53b266635eaa341b6134ed6c 100644
--- a/src/drivers/Cst816s.cpp
+++ b/src/drivers/Cst816s.cpp
@@ -37,7 +37,9 @@
 Cst816S::TouchInfos Cst816S::GetTouchInfo() {
   Cst816S::TouchInfos info;
 
-  twiMaster.Read(twiAddress, 0, touchData, 63);
+  auto ret = twiMaster.Read(twiAddress, 0, touchData, 63);
+  if(ret != TwiMaster::ErrorCodes::NoError) return {};
+
   auto nbTouchPoints = touchData[2] & 0x0f;
 
 //  uint8_t i = 0;




diff --git a/src/drivers/Cst816s.h b/src/drivers/Cst816s.h
index b115a6880978316e014e2a2079f201bda880c0f6..4569e82fa6a8782e8016dd734dada3ad0dc3ba8a 100644
--- a/src/drivers/Cst816s.h
+++ b/src/drivers/Cst816s.h
@@ -18,13 +18,13 @@             DoubleTap = 0x0B,
             LongPress = 0x0C
         };
         struct TouchInfos {
-          uint16_t x;
-          uint16_t y;
-          uint8_t action;
-          uint8_t finger;
-          uint8_t pressure;
-          uint8_t area;
-          Gestures gesture;
+          uint16_t x = 0;
+          uint16_t y = 0;
+          uint8_t action = 0;
+          uint8_t finger = 0;
+          uint8_t pressure = 0;
+          uint8_t area = 0;
+          Gestures gesture = Gestures::None;
           bool isTouch = false;
         };
 




diff --git a/src/drivers/TwiMaster.cpp b/src/drivers/TwiMaster.cpp
index a9eb5d0c6d9cc33bafd24276999c25bf116c13d7..3ff8a9520e8009a6f4e22cafeb539ba424de2221 100644
--- a/src/drivers/TwiMaster.cpp
+++ b/src/drivers/TwiMaster.cpp
@@ -60,24 +60,53 @@   xSemaphoreGive(mutex);
 
 }
 
-void TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
+TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
   xSemaphoreTake(mutex, portMAX_DELAY);
-  Write(deviceAddress, &registerAddress, 1, false);
-  Read(deviceAddress, data, size, true);
+  auto ret = ReadWithRetry(deviceAddress, registerAddress, data, size);
   xSemaphoreGive(mutex);
+
+  return ret;
 }
 
-void TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
+TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
   ASSERT(size <= maxDataSize);
   xSemaphoreTake(mutex, portMAX_DELAY);
+
+  auto ret = WriteWithRetry(deviceAddress, registerAddress, data, size);
+  xSemaphoreGive(mutex);
+  return ret;
+}
+
+/* Execute a read transaction (composed of a write and a read operation). If one of these opeartion fails,
+ * it's retried once. If it fails again, an error is returned */
+TwiMaster::ErrorCodes TwiMaster::ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
+  TwiMaster::ErrorCodes ret;
+  ret = Write(deviceAddress, &registerAddress, 1, false);
+  if(ret != ErrorCodes::NoError)
+    ret = Write(deviceAddress, &registerAddress, 1, false);
+
+  if(ret != ErrorCodes::NoError) return ret;
+
+  ret = Read(deviceAddress, data, size, true);
+  if(ret != ErrorCodes::NoError)
+    ret = Read(deviceAddress, data, size, true);
+
+  return ret;
+}
+
+/* Execute a write transaction. If it fails, it is retried once. If it fails again, an error is returned. */
+TwiMaster::ErrorCodes TwiMaster::WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
   internalBuffer[0] = registerAddress;
   std::memcpy(internalBuffer+1, data, size);
-  Write(deviceAddress, internalBuffer, size+1, true);
-  xSemaphoreGive(mutex);
+  auto ret = Write(deviceAddress, internalBuffer, size+1, true);
+  if(ret != ErrorCodes::NoError)
+    ret = Write(deviceAddress, internalBuffer, size+1, true);
+
+  return ret;
 }
 
 
-void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) {
+TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) {
   twiBaseAddress->ADDRESS = deviceAddress;
   twiBaseAddress->TASKS_RESUME = 0x1UL;
   twiBaseAddress->RXD.PTR = (uint32_t)buffer;
@@ -88,7 +117,15 @@
   while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
   twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
 
-  while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR);
+  txStartedCycleCount = DWT->CYCCNT;
+  uint32_t currentCycleCount;
+  while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR) {
+    currentCycleCount = DWT->CYCCNT;
+    if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
+      FixHwFreezed();
+      return ErrorCodes::TransactionFailed;
+    }
+  }
   twiBaseAddress->EVENTS_LASTRX = 0x0UL;
 
   if (stop || twiBaseAddress->EVENTS_ERROR) {
@@ -105,9 +142,10 @@
   if (twiBaseAddress->EVENTS_ERROR) {
     twiBaseAddress->EVENTS_ERROR = 0x0UL;
   }
+  return ErrorCodes::NoError;
 }
 
-void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) {
+TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) {
   twiBaseAddress->ADDRESS = deviceAddress;
   twiBaseAddress->TASKS_RESUME = 0x1UL;
   twiBaseAddress->TXD.PTR = (uint32_t)data;
@@ -118,7 +156,15 @@
   while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
   twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
 
-  while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR);
+  txStartedCycleCount = DWT->CYCCNT;
+  uint32_t currentCycleCount;
+  while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR) {
+    currentCycleCount = DWT->CYCCNT;
+    if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
+      FixHwFreezed();
+      return ErrorCodes::TransactionFailed;
+    }
+  }
   twiBaseAddress->EVENTS_LASTTX = 0x0UL;
 
   if (stop || twiBaseAddress->EVENTS_ERROR) {
@@ -137,6 +183,8 @@     twiBaseAddress->EVENTS_ERROR = 0x0UL;
     uint32_t error = twiBaseAddress->ERRORSRC;
     twiBaseAddress->ERRORSRC = error;
   }
+
+  return ErrorCodes::NoError;
 }
 
 void TwiMaster::Sleep() {
@@ -152,3 +200,30 @@ void TwiMaster::Wakeup() {
   Init();
   NRF_LOG_INFO("[TWIMASTER] Wakeup");
 }
+
+/* Sometimes, the TWIM device just freeze and never set the event EVENTS_LASTTX.
+ * This method disable and re-enable the peripheral so that it works again.
+ * This is just a workaround, and it would be better if we could find a way to prevent
+ * this issue from happening.
+ * */
+void TwiMaster::FixHwFreezed() {
+  NRF_LOG_INFO("I2C device frozen, reinitializing it!");
+  // Disable I²C
+  uint32_t twi_state = NRF_TWI1->ENABLE;
+  twiBaseAddress->ENABLE = TWIM_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
+
+  NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input      << GPIO_PIN_CNF_DIR_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect    << GPIO_PIN_CNF_INPUT_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup      << GPIO_PIN_CNF_PULL_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1       << GPIO_PIN_CNF_DRIVE_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled   << GPIO_PIN_CNF_SENSE_Pos);
+
+  NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input        << GPIO_PIN_CNF_DIR_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_INPUT_Connect    << GPIO_PIN_CNF_INPUT_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_PULL_Pullup      << GPIO_PIN_CNF_PULL_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1       << GPIO_PIN_CNF_DRIVE_Pos)
+                         | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled   << GPIO_PIN_CNF_SENSE_Pos);
+
+  // Re-enable I²C
+  twiBaseAddress->ENABLE = twi_state;
+}




diff --git a/src/drivers/TwiMaster.h b/src/drivers/TwiMaster.h
index 9b6b5070e49ef407c3c84a27da78bdf7b3f94fb9..52e39098aef2239f194db013d61f6acd08c839bb 100644
--- a/src/drivers/TwiMaster.h
+++ b/src/drivers/TwiMaster.h
@@ -10,6 +10,7 @@     class TwiMaster {
       public:
         enum class Modules { TWIM1 };
         enum class Frequencies {Khz100, Khz250, Khz400};
+        enum class ErrorCodes {NoError, TransactionFailed};
         struct Parameters {
           uint32_t frequency;
           uint8_t pinSda;
@@ -19,15 +20,19 @@
         TwiMaster(const Modules module, const Parameters& params);
 
         void Init();
-        void Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
-        void Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
+        ErrorCodes Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
+        ErrorCodes Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
 
         void Sleep();
         void Wakeup();
 
       private:
-        void Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
-        void Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
+        ErrorCodes ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
+        ErrorCodes WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
+
+        ErrorCodes Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
+        ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
+        void FixHwFreezed();
         NRF_TWIM_Type* twiBaseAddress;
         SemaphoreHandle_t mutex;
         const Modules module;
@@ -35,7 +40,8 @@         const Parameters params;
         static constexpr uint8_t maxDataSize{8};
         static constexpr uint8_t registerSize{1};
         uint8_t internalBuffer[maxDataSize + registerSize];
-
+        uint32_t txStartedCycleCount = 0;
+        static constexpr uint32_t HwFreezedDelay{161000};
     };
   }
 }
\ No newline at end of file