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, ®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 @@ 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