1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-23 12:51:24 +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
//#define I2C_USE_WIRE
@ -265,6 +270,7 @@ private:
static volatile unsigned long startTime;
static unsigned long timeout; // Transaction timeout in microseconds. 0=disabled.
static uint8_t retryCounter; // Count of retries
void startTransaction();

View File

@ -223,16 +223,15 @@ void I2CManagerClass::handleInterrupt() {
// Update hardware state machine
I2C_handleInterrupt();
// Enable interrupts to minimise effect on other interrupt code
interrupts();
// 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.
if (state != I2C_STATE_ACTIVE && currentRequest != NULL) {
// Remove completed request from head of queue
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// Operation has completed.
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;
if (t == queueHead) {
if (t == currentRequest) {
queueHead = t->nextRequest;
if (!queueHead) queueTail = queueHead;
t->nBytes = rxCount;
@ -241,12 +240,21 @@ void I2CManagerClass::handleInterrupt() {
// I2C state machine is now free for next request
currentRequest = NULL;
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.
@ -261,5 +269,6 @@ volatile uint8_t I2CManagerClass::bytesToSend;
volatile uint8_t I2CManagerClass::bytesToReceive;
volatile unsigned long I2CManagerClass::startTime;
unsigned long I2CManagerClass::timeout = 0;
uint8_t I2CManagerClass::retryCounter = 0;
#endif

View File

@ -49,9 +49,14 @@ void I2CManagerClass::_setClock(unsigned long i2cClockSpeed) {
* 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) {
Wire.beginTransmission(address);
if (size > 0) Wire.write(buffer, size);
rb->status = Wire.endTransmission();
uint8_t status = I2C_STATUS_OK;
uint8_t retryCount = 0;
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;
}
@ -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 nBytes = 0;
if (writeSize > 0) {
Wire.beginTransmission(address);
Wire.write(writeBuffer, writeSize);
status = Wire.endTransmission(false); // Don't free bus yet
}
if (status == I2C_STATUS_OK) {
Wire.requestFrom(address, (size_t)readSize);
while (Wire.available() && nBytes < readSize)
readBuffer[nBytes++] = Wire.read();
if (nBytes < readSize) status = I2C_STATUS_TRUNCATED;
}
uint8_t retryCount = 0;
do {
if (writeSize > 0) {
Wire.beginTransmission(address);
Wire.write(writeBuffer, writeSize);
status = Wire.endTransmission(false); // Don't free bus yet
}
if (status == I2C_STATUS_OK) {
Wire.requestFrom(address, (size_t)readSize);
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->status = status;
return I2C_STATUS_OK;