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

Workaround for TWI driver freeze
This commit is contained in:
JF002 2020-10-27 19:48:39 +01:00 committed by GitHub
commit 45e65b66b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 23 deletions

View file

@ -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;

View file

@ -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;
};

View file

@ -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, &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 @@ 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;
}

View file

@ -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};
};
}
}