1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-24 13:21:23 +01:00

Add I2C retries to Wire and to non-blocking I2CManager.

This commit is contained in:
Neil McKechnie 2023-01-14 18:18:57 +00:00
parent 3c5b7bbcfe
commit 6e69df2da8
3 changed files with 47 additions and 23 deletions

View File

@ -111,6 +111,11 @@
* *
*/ */
// Maximum number of retries on an I2C operation
// A value of zero will disable retries.
// Maximum value is 254 (unsigned byte counter)
#define MAX_I2C_RETRIES 2
// Add following line to config.h to enable Wire library instead of native I2C drivers // Add following line to config.h to enable Wire library instead of native I2C drivers
//#define I2C_USE_WIRE //#define I2C_USE_WIRE
@ -265,6 +270,7 @@ private:
static volatile unsigned long startTime; static volatile unsigned long startTime;
static unsigned long timeout; // Transaction timeout in microseconds. 0=disabled. static unsigned long timeout; // Transaction timeout in microseconds. 0=disabled.
static uint8_t retryCounter; // Count of retries
void startTransaction(); void startTransaction();

View File

@ -223,16 +223,15 @@ void I2CManagerClass::handleInterrupt() {
// Update hardware state machine // Update hardware state machine
I2C_handleInterrupt(); I2C_handleInterrupt();
// Enable interrupts to minimise effect on other interrupt code
interrupts();
// Check if current request has completed. If there's a current request // Check if current request has completed. If there's a current request
// and state isn't active then state contains the completion status of the request. // and state isn't active then state contains the completion status of the request.
if (state != I2C_STATE_ACTIVE && currentRequest != NULL) { if (state != I2C_STATE_ACTIVE && currentRequest != NULL) {
// Remove completed request from head of queue // Operation has completed.
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { if (state == I2C_STATUS_OK || ++retryCounter > MAX_I2C_RETRIES) {
// Status is OK, or has failed and retry count exceeded.
// Remove completed request from head of queue
I2CRB * t = queueHead; I2CRB * t = queueHead;
if (t == queueHead) { if (t == currentRequest) {
queueHead = t->nextRequest; queueHead = t->nextRequest;
if (!queueHead) queueTail = queueHead; if (!queueHead) queueTail = queueHead;
t->nBytes = rxCount; t->nBytes = rxCount;
@ -241,12 +240,21 @@ void I2CManagerClass::handleInterrupt() {
// I2C state machine is now free for next request // I2C state machine is now free for next request
currentRequest = NULL; currentRequest = NULL;
state = I2C_STATE_FREE; state = I2C_STATE_FREE;
// Start next request (if any)
I2CManager.startTransaction();
} }
retryCounter = 0;
} else {
// Status is failed and retry permitted.
// Retry previous request.
state = I2C_STATE_FREE;
} }
} }
if (state == I2C_STATE_FREE && queueHead != NULL) {
// Allow any pending interrupts before starting the next request.
interrupts();
// Start next request
I2CManager.startTransaction();
}
} }
// Fields in I2CManager class specific to Non-blocking implementation. // Fields in I2CManager class specific to Non-blocking implementation.
@ -261,5 +269,6 @@ volatile uint8_t I2CManagerClass::bytesToSend;
volatile uint8_t I2CManagerClass::bytesToReceive; volatile uint8_t I2CManagerClass::bytesToReceive;
volatile unsigned long I2CManagerClass::startTime; volatile unsigned long I2CManagerClass::startTime;
unsigned long I2CManagerClass::timeout = 0; unsigned long I2CManagerClass::timeout = 0;
uint8_t I2CManagerClass::retryCounter = 0;
#endif #endif

View File

@ -49,9 +49,14 @@ void I2CManagerClass::_setClock(unsigned long i2cClockSpeed) {
* Initiate a write to an I2C device (blocking operation on Wire) * Initiate a write to an I2C device (blocking operation on Wire)
***************************************************************************/ ***************************************************************************/
uint8_t I2CManagerClass::write(uint8_t address, const uint8_t buffer[], uint8_t size, I2CRB *rb) { uint8_t I2CManagerClass::write(uint8_t address, const uint8_t buffer[], uint8_t size, I2CRB *rb) {
Wire.beginTransmission(address); uint8_t status = I2C_STATUS_OK;
if (size > 0) Wire.write(buffer, size); uint8_t retryCount = 0;
rb->status = Wire.endTransmission(); do {
Wire.beginTransmission(address);
if (size > 0) Wire.write(buffer, size);
status = Wire.endTransmission();
} while (!(status == I2C_STATUS_OK || ++retryCount > MAX_I2C_RETRIES));
rb->status = status;
return I2C_STATUS_OK; return I2C_STATUS_OK;
} }
@ -75,17 +80,21 @@ uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t rea
{ {
uint8_t status = I2C_STATUS_OK; uint8_t status = I2C_STATUS_OK;
uint8_t nBytes = 0; uint8_t nBytes = 0;
if (writeSize > 0) { uint8_t retryCount = 0;
Wire.beginTransmission(address); do {
Wire.write(writeBuffer, writeSize); if (writeSize > 0) {
status = Wire.endTransmission(false); // Don't free bus yet Wire.beginTransmission(address);
} Wire.write(writeBuffer, writeSize);
if (status == I2C_STATUS_OK) { status = Wire.endTransmission(false); // Don't free bus yet
Wire.requestFrom(address, (size_t)readSize); }
while (Wire.available() && nBytes < readSize) if (status == I2C_STATUS_OK) {
readBuffer[nBytes++] = Wire.read(); Wire.requestFrom(address, (size_t)readSize);
if (nBytes < readSize) status = I2C_STATUS_TRUNCATED; while (Wire.available() && nBytes < readSize)
} readBuffer[nBytes++] = Wire.read();
if (nBytes < readSize) status = I2C_STATUS_TRUNCATED;
}
} while (!(status == I2C_STATUS_OK || ++retryCount > MAX_I2C_RETRIES));
rb->nBytes = nBytes; rb->nBytes = nBytes;
rb->status = status; rb->status = status;
return I2C_STATUS_OK; return I2C_STATUS_OK;