Merge pull request #111 from JF002/fix-twi-hang
Workaround for TWI driver freeze
This commit is contained in:
		
						commit
						45e65b66b1
					
				@ -37,7 +37,9 @@ void Cst816S::Init() {
 | 
				
			|||||||
Cst816S::TouchInfos Cst816S::GetTouchInfo() {
 | 
					Cst816S::TouchInfos Cst816S::GetTouchInfo() {
 | 
				
			||||||
  Cst816S::TouchInfos info;
 | 
					  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;
 | 
					  auto nbTouchPoints = touchData[2] & 0x0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//  uint8_t i = 0;
 | 
					//  uint8_t i = 0;
 | 
				
			||||||
 | 
				
			|||||||
@ -18,13 +18,13 @@ namespace Pinetime {
 | 
				
			|||||||
            LongPress = 0x0C
 | 
					            LongPress = 0x0C
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        struct TouchInfos {
 | 
					        struct TouchInfos {
 | 
				
			||||||
          uint16_t x;
 | 
					          uint16_t x = 0;
 | 
				
			||||||
          uint16_t y;
 | 
					          uint16_t y = 0;
 | 
				
			||||||
          uint8_t action;
 | 
					          uint8_t action = 0;
 | 
				
			||||||
          uint8_t finger;
 | 
					          uint8_t finger = 0;
 | 
				
			||||||
          uint8_t pressure;
 | 
					          uint8_t pressure = 0;
 | 
				
			||||||
          uint8_t area;
 | 
					          uint8_t area = 0;
 | 
				
			||||||
          Gestures gesture;
 | 
					          Gestures gesture = Gestures::None;
 | 
				
			||||||
          bool isTouch = false;
 | 
					          bool isTouch = false;
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -60,24 +60,53 @@ void TwiMaster::Init() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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);
 | 
					  xSemaphoreTake(mutex, portMAX_DELAY);
 | 
				
			||||||
  Write(deviceAddress, ®isterAddress, 1, false);
 | 
					  auto ret = ReadWithRetry(deviceAddress, registerAddress, data, size);
 | 
				
			||||||
  Read(deviceAddress, data, size, true);
 | 
					 | 
				
			||||||
  xSemaphoreGive(mutex);
 | 
					  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);
 | 
					  ASSERT(size <= maxDataSize);
 | 
				
			||||||
  xSemaphoreTake(mutex, portMAX_DELAY);
 | 
					  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, ®isterAddress, 1, false);
 | 
				
			||||||
 | 
					  if(ret != ErrorCodes::NoError)
 | 
				
			||||||
 | 
					    ret = Write(deviceAddress, ®isterAddress, 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;
 | 
					  internalBuffer[0] = registerAddress;
 | 
				
			||||||
  std::memcpy(internalBuffer+1, data, size);
 | 
					  std::memcpy(internalBuffer+1, data, size);
 | 
				
			||||||
  Write(deviceAddress, internalBuffer, size+1, true);
 | 
					  auto ret = Write(deviceAddress, internalBuffer, size+1, true);
 | 
				
			||||||
  xSemaphoreGive(mutex);
 | 
					  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->ADDRESS = deviceAddress;
 | 
				
			||||||
  twiBaseAddress->TASKS_RESUME = 0x1UL;
 | 
					  twiBaseAddress->TASKS_RESUME = 0x1UL;
 | 
				
			||||||
  twiBaseAddress->RXD.PTR = (uint32_t)buffer;
 | 
					  twiBaseAddress->RXD.PTR = (uint32_t)buffer;
 | 
				
			||||||
@ -88,7 +117,15 @@ void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool s
 | 
				
			|||||||
  while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
 | 
					  while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
 | 
				
			||||||
  twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
 | 
					  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;
 | 
					  twiBaseAddress->EVENTS_LASTRX = 0x0UL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (stop || twiBaseAddress->EVENTS_ERROR) {
 | 
					  if (stop || twiBaseAddress->EVENTS_ERROR) {
 | 
				
			||||||
@ -105,9 +142,10 @@ void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool s
 | 
				
			|||||||
  if (twiBaseAddress->EVENTS_ERROR) {
 | 
					  if (twiBaseAddress->EVENTS_ERROR) {
 | 
				
			||||||
    twiBaseAddress->EVENTS_ERROR = 0x0UL;
 | 
					    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->ADDRESS = deviceAddress;
 | 
				
			||||||
  twiBaseAddress->TASKS_RESUME = 0x1UL;
 | 
					  twiBaseAddress->TASKS_RESUME = 0x1UL;
 | 
				
			||||||
  twiBaseAddress->TXD.PTR = (uint32_t)data;
 | 
					  twiBaseAddress->TXD.PTR = (uint32_t)data;
 | 
				
			||||||
@ -118,7 +156,15 @@ void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, b
 | 
				
			|||||||
  while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
 | 
					  while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
 | 
				
			||||||
  twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
 | 
					  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;
 | 
					  twiBaseAddress->EVENTS_LASTTX = 0x0UL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (stop || twiBaseAddress->EVENTS_ERROR) {
 | 
					  if (stop || twiBaseAddress->EVENTS_ERROR) {
 | 
				
			||||||
@ -137,6 +183,8 @@ void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, b
 | 
				
			|||||||
    uint32_t error = twiBaseAddress->ERRORSRC;
 | 
					    uint32_t error = twiBaseAddress->ERRORSRC;
 | 
				
			||||||
    twiBaseAddress->ERRORSRC = error;
 | 
					    twiBaseAddress->ERRORSRC = error;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return ErrorCodes::NoError;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void TwiMaster::Sleep() {
 | 
					void TwiMaster::Sleep() {
 | 
				
			||||||
@ -152,3 +200,30 @@ void TwiMaster::Wakeup() {
 | 
				
			|||||||
  Init();
 | 
					  Init();
 | 
				
			||||||
  NRF_LOG_INFO("[TWIMASTER] Wakeup");
 | 
					  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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ namespace Pinetime {
 | 
				
			|||||||
      public:
 | 
					      public:
 | 
				
			||||||
        enum class Modules { TWIM1 };
 | 
					        enum class Modules { TWIM1 };
 | 
				
			||||||
        enum class Frequencies {Khz100, Khz250, Khz400};
 | 
					        enum class Frequencies {Khz100, Khz250, Khz400};
 | 
				
			||||||
 | 
					        enum class ErrorCodes {NoError, TransactionFailed};
 | 
				
			||||||
        struct Parameters {
 | 
					        struct Parameters {
 | 
				
			||||||
          uint32_t frequency;
 | 
					          uint32_t frequency;
 | 
				
			||||||
          uint8_t pinSda;
 | 
					          uint8_t pinSda;
 | 
				
			||||||
@ -19,15 +20,19 @@ namespace Pinetime {
 | 
				
			|||||||
        TwiMaster(const Modules module, const Parameters& params);
 | 
					        TwiMaster(const Modules module, const Parameters& params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void Init();
 | 
					        void Init();
 | 
				
			||||||
        void Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
 | 
					        ErrorCodes 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 Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void Sleep();
 | 
					        void Sleep();
 | 
				
			||||||
        void Wakeup();
 | 
					        void Wakeup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      private:
 | 
					      private:
 | 
				
			||||||
        void Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
 | 
					        ErrorCodes ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size);
 | 
				
			||||||
        void Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
 | 
					        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;
 | 
					        NRF_TWIM_Type* twiBaseAddress;
 | 
				
			||||||
        SemaphoreHandle_t mutex;
 | 
					        SemaphoreHandle_t mutex;
 | 
				
			||||||
        const Modules module;
 | 
					        const Modules module;
 | 
				
			||||||
@ -35,7 +40,8 @@ namespace Pinetime {
 | 
				
			|||||||
        static constexpr uint8_t maxDataSize{8};
 | 
					        static constexpr uint8_t maxDataSize{8};
 | 
				
			||||||
        static constexpr uint8_t registerSize{1};
 | 
					        static constexpr uint8_t registerSize{1};
 | 
				
			||||||
        uint8_t internalBuffer[maxDataSize + registerSize];
 | 
					        uint8_t internalBuffer[maxDataSize + registerSize];
 | 
				
			||||||
 | 
					        uint32_t txStartedCycleCount = 0;
 | 
				
			||||||
 | 
					        static constexpr uint32_t HwFreezedDelay{161000};
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user