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 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;
 | 
			
		||||
 | 
			
		||||
@ -18,13 +18,13 @@ namespace Pinetime {
 | 
			
		||||
            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;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
  Write(deviceAddress, ®isterAddress, 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, ®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;
 | 
			
		||||
  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 @@ void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool s
 | 
			
		||||
  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 @@ void TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool s
 | 
			
		||||
  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 @@ void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, b
 | 
			
		||||
  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 @@ void TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, b
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ namespace Pinetime {
 | 
			
		||||
      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 @@ namespace Pinetime {
 | 
			
		||||
        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 @@ namespace Pinetime {
 | 
			
		||||
        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};
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user