1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-26 09:36:13 +01:00

I2CManager: Update native drivers for MUX support from the common code.

This commit is contained in:
Neil McKechnie 2023-02-10 15:47:44 +00:00
parent 553a94bf67
commit 0b307a67e4
5 changed files with 140 additions and 187 deletions

View File

@ -95,17 +95,16 @@ void I2CManagerClass::I2C_init()
* Initiate a start bit for transmission. * Initiate a start bit for transmission.
***************************************************************************/ ***************************************************************************/
void I2CManagerClass::I2C_sendStart() { void I2CManagerClass::I2C_sendStart() {
bytesToSend = currentRequest->writeLen;
bytesToReceive = currentRequest->readLen;
rxCount = 0; rxCount = 0;
txCount = 0; txCount = 0;
#if defined(I2C_EXTENDED_ADDRESS) #if defined(I2C_EXTENDED_ADDRESSXXXXXXXXXXXX)
if (currentRequest->i2cAddress.muxNumber() != I2CMux_None) { if (currentRequest->i2cAddress.muxNumber() != I2CMux_None) {
// Send request to multiplexer // Send request to multiplexer
muxPhase = MuxPhase_PROLOG; // When start bit interrupt comes in, send SLA+W to MUX muxPhase = MuxPhase_PROLOG; // When start bit interrupt comes in, send SLA+W to MUX
} else } else
muxPhase = 0; muxPhase = 0;
#endif #endif
while(TWCR & (1<<TWSTO)) {} // Wait for any in-progress stop to finish
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWEA)|(1<<TWSTA); // Send Start TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWEA)|(1<<TWSTA); // Send Start
} }
@ -138,119 +137,15 @@ void I2CManagerClass::I2C_handleInterrupt() {
uint8_t twsr = TWSR & 0xF8; uint8_t twsr = TWSR & 0xF8;
#if defined(I2C_EXTENDED_ADDRESS)
// First process the MUX state machine.
if (muxPhase > MuxPhase_OFF) {
switch (twsr) {
case TWI_MTX_ADR_ACK: // SLA+W has been transmitted and ACK received
if (muxPhase == MuxPhase_PROLOG) {
// Send MUX selecter mask to follow address
I2CSubBus subBus = currentRequest->i2cAddress.subBus();
TWDR = (subBus==SubBus_All) ? 0xff :
(subBus==SubBus_None) ? 0x00 :
1 << subBus;
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT);
return;
} else if (muxPhase == MuxPhase_EPILOG) {
TWDR = 0x00; // Disable all subbuses
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT);
return;
}
break;
case TWI_MTX_DATA_ACK: // Data byte has been transmitted and ACK received // Main I2C interrupt handler, used for the device communications.
if (muxPhase == MuxPhase_PASSTHRU && !bytesToSend && !bytesToReceive) { // The following variables are used:
if (_muxCount > 1) { // bytesToSend, bytesToReceive (R/W)
// Device transaction complete, prepare to deselect MUX by sending start bit // txCount, rxCount (W)
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWSTO)|(1<<TWSTA); // deviceAddress (R)
muxPhase = MuxPhase_EPILOG; // sendBuffer, receiveBuffer (R)
return; // operation (R)
} else { // state, completionStatus (W)
// Only one MUX so no need to deselect it. Just finish off
TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO);
state = I2C_STATE_COMPLETED;
muxPhase = MuxPhase_OFF;
return;
}
} else if (muxPhase == MuxPhase_PROLOG) {
// If device address is zero, then finish here (i.e. send mux subBus mask only)
if (currentRequest->i2cAddress.deviceAddress() == 0) {
// Send stop and post rb.
TWDR = 0xff;
TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO);
state = I2C_STATE_COMPLETED;
muxPhase = MuxPhase_OFF;
return;
} else {
// Send stop followed by start, preparing to send device address
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWSTO)|(1<<TWSTA);
muxPhase = MuxPhase_PASSTHRU;
return;
}
} else if (muxPhase == MuxPhase_EPILOG) {
// Send stop and allow RB to be posted.
TWDR = 0xff;
TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO);
state = I2C_STATE_COMPLETED;
muxPhase = MuxPhase_OFF;
return;
}
break;
case TWI_MRX_DATA_NACK: // Last data byte has been received and NACK transmitted
// We must read the data before processing the MUX, so do this here.
if (bytesToReceive > 0) {
currentRequest->readBuffer[rxCount++] = TWDR;
bytesToReceive--;
}
if (muxPhase == MuxPhase_PASSTHRU && _muxCount > 1) {
// Prepare to transmit epilog to mux - first send the stop bit and start bit
// (we don't need to reset mux if there is only one.
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWSTO)|(1<<TWSTA);
muxPhase = MuxPhase_EPILOG;
return;
} else {
// Finish up.
TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO); // Send Stop
state = I2C_STATE_COMPLETED;
muxPhase = MuxPhase_OFF;
return;
}
break;
case TWI_START: // START has been transmitted
case TWI_REP_START: // Repeated START has been transmitted
if (muxPhase == MuxPhase_PROLOG || muxPhase == MuxPhase_EPILOG) {
// Send multiplexer address first
uint8_t muxAddress = I2C_MUX_BASE_ADDRESS + currentRequest->i2cAddress.muxNumber();
TWDR = (muxAddress << 1) | 0; // MUXaddress+Write
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT);
return;
}
break;
case TWI_MTX_ADR_NACK: // SLA+W has been transmitted and NACK received
case TWI_MRX_ADR_NACK: // SLA+R has been transmitted and NACK received
case TWI_MTX_DATA_NACK: // Data byte has been transmitted and NACK received
if (muxPhase == MuxPhase_PASSTHRU) {
// Data transaction was nak'd, update RB status but continue with mux cleardown
completionStatus = I2C_STATUS_NEGATIVE_ACKNOWLEDGE;
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWSTO)|(1<<TWSTA); // Send Stop and start
muxPhase = MuxPhase_EPILOG;
return;
} else if (muxPhase > MuxPhase_EPILOG) {
// Mux Cleardown was NAK'd, send stop and then finish.
TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWSTO); // Send Stop
state = I2C_STATE_COMPLETED;
return;
}
break;
}
}
#endif
// Now the main I2C interrupt handler, used for the device communications.
// //
// Cases are ordered so that the most frequently used ones are tested first. // Cases are ordered so that the most frequently used ones are tested first.
switch (twsr) { switch (twsr) {
@ -258,14 +153,14 @@ void I2CManagerClass::I2C_handleInterrupt() {
case TWI_MTX_ADR_ACK: // SLA+W has been transmitted and ACK received case TWI_MTX_ADR_ACK: // SLA+W has been transmitted and ACK received
if (bytesToSend) { // Send first. if (bytesToSend) { // Send first.
if (operation == OPERATION_SEND_P) if (operation == OPERATION_SEND_P)
TWDR = GETFLASH(currentRequest->writeBuffer + (txCount++)); TWDR = GETFLASH(sendBuffer + (txCount++));
else else
TWDR = currentRequest->writeBuffer[txCount++]; TWDR = sendBuffer[txCount++];
bytesToSend--; bytesToSend--;
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT); TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT);
} else if (bytesToReceive) { // All sent, anything to receive? } else if (bytesToReceive) { // All sent, anything to receive?
// Don't need to wait for stop, as the interface won't send the start until // Don't need to wait for stop, as the interface won't send the start until
// any in-progress stop condition has been sent. // any in-progress stop condition from previous interrupts has been sent.
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWSTA); // Send Start TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWSTA); // Send Start
} else { } else {
// Nothing left to send or receive // Nothing left to send or receive
@ -276,7 +171,7 @@ void I2CManagerClass::I2C_handleInterrupt() {
case TWI_MRX_DATA_ACK: // Data byte has been received and ACK transmitted case TWI_MRX_DATA_ACK: // Data byte has been received and ACK transmitted
if (bytesToReceive > 0) { if (bytesToReceive > 0) {
currentRequest->readBuffer[rxCount++] = TWDR; receiveBuffer[rxCount++] = TWDR;
bytesToReceive--; bytesToReceive--;
} }
/* fallthrough */ /* fallthrough */
@ -292,7 +187,7 @@ void I2CManagerClass::I2C_handleInterrupt() {
case TWI_MRX_DATA_NACK: // Data byte has been received and NACK transmitted case TWI_MRX_DATA_NACK: // Data byte has been received and NACK transmitted
if (bytesToReceive > 0) { if (bytesToReceive > 0) {
currentRequest->readBuffer[rxCount++] = TWDR; receiveBuffer[rxCount++] = TWDR;
bytesToReceive--; bytesToReceive--;
} }
TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWEA)|(1<<TWSTO); // Send Stop TWCR = (1<<TWEN)|(1<<TWINT)|(1<<TWEA)|(1<<TWSTO); // Send Stop
@ -301,15 +196,12 @@ void I2CManagerClass::I2C_handleInterrupt() {
case TWI_START: // START has been transmitted case TWI_START: // START has been transmitted
case TWI_REP_START: // Repeated START has been transmitted case TWI_REP_START: // Repeated START has been transmitted
{
// Set up address and R/W // Set up address and R/W
uint8_t deviceAddress = currentRequest->i2cAddress;
if (operation == OPERATION_READ || (operation==OPERATION_REQUEST && !bytesToSend)) if (operation == OPERATION_READ || (operation==OPERATION_REQUEST && !bytesToSend))
TWDR = (deviceAddress << 1) | 1; // SLA+R TWDR = (deviceAddress << 1) | 1; // SLA+R
else else
TWDR = (deviceAddress << 1) | 0; // SLA+W TWDR = (deviceAddress << 1) | 0; // SLA+W
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWEA); TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWEA);
}
break; break;
case TWI_MTX_ADR_NACK: // SLA+W has been transmitted and NACK received case TWI_MTX_ADR_NACK: // SLA+W has been transmitted and NACK received
@ -336,7 +228,10 @@ void I2CManagerClass::I2C_handleInterrupt() {
#if defined(I2C_USE_INTERRUPTS) #if defined(I2C_USE_INTERRUPTS)
ISR(TWI_vect) { ISR(TWI_vect) {
I2CManagerClass::handleInterrupt(); // pinMode(2,OUTPUT);
// digitalWrite(2,1);
I2CManager.handleInterrupt();
// digitalWrite(2,0);
} }
#endif #endif

View File

@ -68,8 +68,6 @@ void I2CManagerClass::I2C_init()
* Initiate a start bit for transmission, followed by address and R/W * Initiate a start bit for transmission, followed by address and R/W
***************************************************************************/ ***************************************************************************/
void I2CManagerClass::I2C_sendStart() { void I2CManagerClass::I2C_sendStart() {
bytesToSend = currentRequest->writeLen;
bytesToReceive = currentRequest->readLen;
txCount = 0; txCount = 0;
rxCount = 0; rxCount = 0;
@ -123,11 +121,11 @@ void I2CManagerClass::I2C_handleInterrupt() {
} else if (bytesToSend) { } else if (bytesToSend) {
// Acked, so send next byte (don't need to use GETFLASH) // Acked, so send next byte (don't need to use GETFLASH)
TWI0.MDATA = currentRequest->writeBuffer[txCount++]; TWI0.MDATA = sendBuffer[txCount++];
bytesToSend--; bytesToSend--;
} else if (bytesToReceive) { } else if (bytesToReceive) {
// Last sent byte acked and no more to send. Send repeated start, address and read bit. // Last sent byte acked and no more to send. Send repeated start, address and read bit.
TWI0.MADDR = (currentRequest->i2cAddress << 1) | 1; TWI0.MADDR = (deviceAddress << 1) | 1;
} else { } else {
// No more data to send/receive. Initiate a STOP condition. // No more data to send/receive. Initiate a STOP condition.
TWI0.MCTRLB = TWI_MCMD_STOP_gc; TWI0.MCTRLB = TWI_MCMD_STOP_gc;
@ -136,7 +134,7 @@ void I2CManagerClass::I2C_handleInterrupt() {
} else if (currentStatus & TWI_RIF_bm) { } else if (currentStatus & TWI_RIF_bm) {
// Master read completed without errors // Master read completed without errors
if (bytesToReceive) { if (bytesToReceive) {
currentRequest->readBuffer[rxCount++] = TWI0.MDATA; // Store received byte receiveBuffer[rxCount++] = TWI0.MDATA; // Store received byte
bytesToReceive--; bytesToReceive--;
} }
if (bytesToReceive) { if (bytesToReceive) {
@ -155,7 +153,7 @@ void I2CManagerClass::I2C_handleInterrupt() {
* Interrupt handler. * Interrupt handler.
***************************************************************************/ ***************************************************************************/
ISR(TWI0_TWIM_vect) { ISR(TWI0_TWIM_vect) {
I2CManagerClass::handleInterrupt(); I2CManager.handleInterrupt();
} }
#endif #endif

View File

@ -79,7 +79,7 @@ for ( MY_ATOMIC_RESTORESTATE, _done = my_iCliRetVal(); \
enum MuxPhase: uint8_t { enum MuxPhase: uint8_t {
MuxPhase_OFF = 0, MuxPhase_OFF = 0,
MuxPhase_PROLOG, MuxPhase_PROLOG,
MuxPhase_PASSTHRU, MuxPhase_PAYLOAD,
MuxPhase_EPILOG, MuxPhase_EPILOG,
} ; } ;
@ -107,7 +107,7 @@ void I2CManagerClass::_setClock(unsigned long i2cClockSpeed) {
} }
/*************************************************************************** /***************************************************************************
* Helper function to start operations, if the I2C interface is free and * Start an I2C transaction, if the I2C interface is free and
* there is a queued request to be processed. * there is a queued request to be processed.
* If there's an I2C clock speed change pending, then implement it before * If there's an I2C clock speed change pending, then implement it before
* starting the operation. * starting the operation.
@ -126,9 +126,47 @@ void I2CManagerClass::startTransaction() {
startTime = micros(); startTime = micros();
currentRequest = queueHead; currentRequest = queueHead;
rxCount = txCount = 0; rxCount = txCount = 0;
// Copy key fields to static data for speed.
operation = currentRequest->operation & OPERATION_MASK;
// Start the I2C process going. // Start the I2C process going.
#if defined(I2C_EXTENDED_ADDRESS)
I2CMux muxNumber = currentRequest->i2cAddress.muxNumber();
if (muxNumber != I2CMux_None) {
muxPhase = MuxPhase_PROLOG;
uint8_t subBus = currentRequest->i2cAddress.subBus();
muxData[0] = (subBus == SubBus_All) ? 0xff :
(subBus == SubBus_None) ? 0x00 :
#if defined(I2CMUX_PCA9547)
0x08 | subBus;
#elif defined(I2CMUX_PCA9542) || defined(I2CMUX_PCA9544)
0x04 | subBus; // NB Only 2 or 4 subbuses respectively
#else
// Default behaviour for most MUXs is to use a mask
// with a bit set for the subBus to be enabled
1 << subBus;
#endif
deviceAddress = I2C_MUX_BASE_ADDRESS + muxNumber;
sendBuffer = &muxData[0];
bytesToSend = 1;
bytesToReceive = 0;
operation = OPERATION_SEND;
} else {
// Send/receive payload for device only.
muxPhase = MuxPhase_OFF;
deviceAddress = currentRequest->i2cAddress;
sendBuffer = currentRequest->writeBuffer;
bytesToSend = currentRequest->writeLen;
receiveBuffer = currentRequest->readBuffer;
bytesToReceive = currentRequest->readLen;
operation = currentRequest->operation & OPERATION_MASK;
}
#else
deviceAddress = currentRequest->i2cAddress;
sendBuffer = currentRequest->writeBuffer;
bytesToSend = currentRequest->writeLen;
receiveBuffer = currentRequest->readBuffer;
bytesToReceive = currentRequest->readLen;
operation = currentRequest->operation & OPERATION_MASK;
#endif
I2C_sendStart(); I2C_sendStart();
} }
} }
@ -194,7 +232,7 @@ uint8_t I2CManagerClass::read(I2CAddress i2cAddress, uint8_t *readBuffer, uint8_
* reset before the read. * reset before the read.
***************************************************************************/ ***************************************************************************/
void I2CManagerClass::setTimeout(unsigned long value) { void I2CManagerClass::setTimeout(unsigned long value) {
timeout = value; _timeout = value;
}; };
/*************************************************************************** /***************************************************************************
@ -205,12 +243,12 @@ void I2CManagerClass::setTimeout(unsigned long value) {
void I2CManagerClass::checkForTimeout() { void I2CManagerClass::checkForTimeout() {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
I2CRB *t = queueHead; I2CRB *t = queueHead;
if (state==I2C_STATE_ACTIVE && t!=0 && t==currentRequest && timeout > 0) { if (state==I2C_STATE_ACTIVE && t!=0 && t==currentRequest && _timeout > 0) {
// Check for timeout // Check for timeout
unsigned long elapsed = micros() - startTime; unsigned long elapsed = micros() - startTime;
if (elapsed > timeout) { if (elapsed > _timeout) {
#ifdef DIAG_IO #ifdef DIAG_IO
//DIAG(F("I2CManager Timeout on %s, I2CRB=%s"), t->i2cAddress.toString(), currentRequest); //DIAG(F("I2CManager Timeout on %s"), t->i2cAddress.toString());
#endif #endif
// Excessive time. Dequeue request // Excessive time. Dequeue request
queueHead = t->nextRequest; queueHead = t->nextRequest;
@ -267,19 +305,58 @@ void I2CManagerClass::handleInterrupt() {
// 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_COMPLETED && currentRequest != NULL) {
// Operation has completed. // Operation has completed.
if (completionStatus == I2C_STATUS_OK || ++retryCounter > MAX_I2C_RETRIES if (completionStatus == I2C_STATUS_OK || ++retryCounter > MAX_I2C_RETRIES
|| currentRequest->operation & OPERATION_NORETRY) || currentRequest->operation & OPERATION_NORETRY)
{ {
// Status is OK, or has failed and retry count exceeded, or retries disabled. // Status is OK, or has failed and retry count exceeded, or retries disabled.
#if defined(I2C_EXTENDED_ADDRESS)
if (muxPhase == MuxPhase_PROLOG ) {
overallStatus = completionStatus;
uint8_t rbAddress = currentRequest->i2cAddress.deviceAddress();
if (completionStatus == I2C_STATUS_OK && rbAddress != 0) {
// Mux request OK, start handling application request.
muxPhase = MuxPhase_PAYLOAD;
deviceAddress = rbAddress;
sendBuffer = currentRequest->writeBuffer;
bytesToSend = currentRequest->writeLen;
bytesToReceive = currentRequest->readLen;
operation = currentRequest->operation & OPERATION_MASK;
state = I2C_STATE_ACTIVE;
I2C_sendStart();
return;
}
} else if (muxPhase == MuxPhase_PAYLOAD) {
// Application request completed, now send epilogue to mux
overallStatus = completionStatus;
currentRequest->nBytes = rxCount; // Save number of bytes read into rb
muxPhase = MuxPhase_EPILOG;
deviceAddress = I2C_MUX_BASE_ADDRESS + currentRequest->i2cAddress.muxNumber();
muxData[0] = 0x00;
sendBuffer = &muxData[0];
bytesToSend = 1;
bytesToReceive = 0;
operation = OPERATION_SEND;
state = I2C_STATE_ACTIVE;
I2C_sendStart();
return;
} else if (muxPhase == MuxPhase_EPILOG) {
// Epilog finished, ignore completionStatus
muxPhase = MuxPhase_OFF;
} else
overallStatus = completionStatus;
#else
overallStatus = completionStatus;
currentRequest->nBytes = rxCount;
#endif
// Remove completed request from head of queue // Remove completed request from head of queue
I2CRB * t = queueHead; I2CRB * t = queueHead;
if (t == currentRequest) { if (t == currentRequest) {
queueHead = t->nextRequest; queueHead = t->nextRequest;
if (!queueHead) queueTail = queueHead; if (!queueHead) queueTail = queueHead;
t->nBytes = rxCount; t->status = overallStatus;
t->status = completionStatus;
// I2C state machine is now free for next request // I2C state machine is now free for next request
currentRequest = NULL; currentRequest = NULL;
@ -295,28 +372,10 @@ void I2CManagerClass::handleInterrupt() {
if (state == I2C_STATE_FREE && queueHead != NULL) { if (state == I2C_STATE_FREE && queueHead != NULL) {
// Allow any pending interrupts before starting the next request. // Allow any pending interrupts before starting the next request.
interrupts(); //interrupts();
// Start next request // Start next request
I2CManager.startTransaction(); I2CManager.startTransaction();
} }
} }
// Fields in I2CManager class specific to Non-blocking implementation.
I2CRB * volatile I2CManagerClass::queueHead = NULL;
I2CRB * volatile I2CManagerClass::queueTail = NULL;
I2CRB * volatile I2CManagerClass::currentRequest = NULL;
volatile uint8_t I2CManagerClass::state = I2C_STATE_FREE;
uint8_t I2CManagerClass::completionStatus;
volatile uint8_t I2CManagerClass::txCount;
volatile uint8_t I2CManagerClass::rxCount;
volatile uint8_t I2CManagerClass::operation;
volatile uint8_t I2CManagerClass::bytesToSend;
volatile uint8_t I2CManagerClass::bytesToReceive;
volatile unsigned long I2CManagerClass::startTime;
uint8_t I2CManagerClass::retryCounter = 0;
#if defined(I2C_EXTENDED_ADDRESS)
volatile uint8_t I2CManagerClass::muxPhase = 0;
#endif
#endif #endif

View File

@ -153,8 +153,6 @@ void I2CManagerClass::I2C_init()
void I2CManagerClass::I2C_sendStart() { void I2CManagerClass::I2C_sendStart() {
// Set counters here in case this is a retry. // Set counters here in case this is a retry.
bytesToSend = currentRequest->writeLen;
bytesToReceive = currentRequest->readLen;
txCount = 0; txCount = 0;
rxCount = 0; rxCount = 0;
@ -220,11 +218,11 @@ void I2CManagerClass::I2C_handleInterrupt() {
state = I2C_STATE_COMPLETED; // Completed with error state = I2C_STATE_COMPLETED; // Completed with error
} else if (bytesToSend) { } else if (bytesToSend) {
// Acked, so send next byte // Acked, so send next byte
s->I2CM.DATA.bit.DATA = currentRequest->writeBuffer[txCount++]; s->I2CM.DATA.bit.DATA = sendBuffer[txCount++];
bytesToSend--; bytesToSend--;
} else if (bytesToReceive) { } else if (bytesToReceive) {
// Last sent byte acked and no more to send. Send repeated start, address and read bit. // Last sent byte acked and no more to send. Send repeated start, address and read bit.
s->I2CM.ADDR.bit.ADDR = (currentRequest->i2cAddress << 1) | 1; s->I2CM.ADDR.bit.ADDR = (deviceAddress << 1) | 1;
} else { } else {
// No more data to send/receive. Initiate a STOP condition // No more data to send/receive. Initiate a STOP condition
I2C_sendStop(); I2C_sendStop();
@ -235,12 +233,12 @@ void I2CManagerClass::I2C_handleInterrupt() {
if (bytesToReceive == 1) { if (bytesToReceive == 1) {
s->I2CM.CTRLB.bit.ACKACT = 1; // NAK final byte s->I2CM.CTRLB.bit.ACKACT = 1; // NAK final byte
I2C_sendStop(); // send stop I2C_sendStop(); // send stop
currentRequest->readBuffer[rxCount++] = s->I2CM.DATA.bit.DATA; // Store received byte receiveBuffer[rxCount++] = s->I2CM.DATA.bit.DATA; // Store received byte
bytesToReceive = 0; bytesToReceive = 0;
state = I2C_STATE_COMPLETED; // Completed OK state = I2C_STATE_COMPLETED; // Completed OK
} else if (bytesToReceive) { } else if (bytesToReceive) {
s->I2CM.CTRLB.bit.ACKACT = 0; // ACK all but final byte s->I2CM.CTRLB.bit.ACKACT = 0; // ACK all but final byte
currentRequest->readBuffer[rxCount++] = s->I2CM.DATA.bit.DATA; // Store received byte receiveBuffer[rxCount++] = s->I2CM.DATA.bit.DATA; // Store received byte
bytesToReceive--; bytesToReceive--;
} }
} }

View File

@ -38,7 +38,7 @@
***************************************************************************/ ***************************************************************************/
#if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32) #if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32)
void I2C1_IRQHandler() { void I2C1_IRQHandler() {
I2CManagerClass::handleInterrupt(); I2CManager.handleInterrupt();
} }
#endif #endif
@ -151,8 +151,7 @@ void I2CManagerClass::I2C_init()
void I2CManagerClass::I2C_sendStart() { void I2CManagerClass::I2C_sendStart() {
// Set counters here in case this is a retry. // Set counters here in case this is a retry.
bytesToSend = currentRequest->writeLen; rxCount = txCount = 0;
bytesToReceive = currentRequest->readLen;
uint8_t temp; uint8_t temp;
// On a single-master I2C bus, the start bit won't be sent until the bus // On a single-master I2C bus, the start bit won't be sent until the bus
@ -168,7 +167,7 @@ void I2CManagerClass::I2C_sendStart() {
s->CR1 |= (1<<10); // Enable the ACK s->CR1 |= (1<<10); // Enable the ACK
s->CR1 |= (1<<8); // Generate START s->CR1 |= (1<<8); // Generate START
// Send address with read flag (1) or'd in // Send address with read flag (1) or'd in
s->DR = (currentRequest->i2cAddress << 1) | 1; // send the address s->DR = (deviceAddress << 1) | 1; // send the address
while (!(s->SR1 & (1<<1))); // wait for ADDR bit to set while (!(s->SR1 & (1<<1))); // wait for ADDR bit to set
// Special case for 1 byte reads! // Special case for 1 byte reads!
if (bytesToReceive == 1) if (bytesToReceive == 1)
@ -185,7 +184,7 @@ void I2CManagerClass::I2C_sendStart() {
s->CR1 |= (1<<10); // Enable the ACK s->CR1 |= (1<<10); // Enable the ACK
s->CR1 |= (1<<8); // Generate START s->CR1 |= (1<<8); // Generate START
// Send address with write flag (0) or'd in // Send address with write flag (0) or'd in
s->DR = (currentRequest->i2cAddress << 1) | 0; // send the address s->DR = (deviceAddress << 1) | 0; // send the address
while (!(s->SR1 & (1<<1))); // wait for ADDR bit to set while (!(s->SR1 & (1<<1))); // wait for ADDR bit to set
temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit
} }
@ -219,44 +218,48 @@ void I2CManagerClass::I2C_close() {
***************************************************************************/ ***************************************************************************/
void I2CManagerClass::I2C_handleInterrupt() { void I2CManagerClass::I2C_handleInterrupt() {
if (!s) return;
if (s->SR1 && (1<<9)) { if (s->SR1 && (1<<9)) {
// Arbitration lost, restart // Arbitration lost, restart
I2C_sendStart(); // Reinitiate request I2C_sendStart(); // Reinitiate request
} else if (s->SR1 && (1<<8)) { } else if (s->SR1 && (1<<8)) {
// Bus error // Bus error
state = I2C_STATUS_BUS_ERROR; completionStatus = I2C_STATUS_BUS_ERROR;
state = I2C_STATE_COMPLETED;
} else if (s->SR1 && (1<<7)) { } else if (s->SR1 && (1<<7)) {
// Master write completed // Master write completed
if (s->SR1 && (1<<10)) { if (s->SR1 && (1<<10)) {
// Nacked, send stop. // Nacked, send stop.
I2C_sendStop(); I2C_sendStop();
state = I2C_STATUS_NEGATIVE_ACKNOWLEDGE; completionStatus = I2C_STATUS_NEGATIVE_ACKNOWLEDGE;
state = I2C_STATE_COMPLETED;
} else if (bytesToSend) { } else if (bytesToSend) {
// Acked, so send next byte // Acked, so send next byte
s->DR = currentRequest->writeBuffer[txCount++]; s->DR = sendBuffer[txCount++];
bytesToSend--; bytesToSend--;
} else if (bytesToReceive) { } else if (bytesToReceive) {
// Last sent byte acked and no more to send. Send repeated start, address and read bit. // Last sent byte acked and no more to send. Send repeated start, address and read bit.
// s->I2CM.ADDR.bit.ADDR = (currentRequest->i2cAddress << 1) | 1; // s->I2CM.ADDR.bit.ADDR = (deviceAddress << 1) | 1;
} else { } else {
// Check both TxE/BTF == 1 before generating stop // Check both TxE/BTF == 1 before generating stop
while (!(s->SR1 && (1<<7))); // Check TxE while (!(s->SR1 && (1<<7))); // Check TxE
while (!(s->SR1 && (1<<2))); // Check BTF while (!(s->SR1 && (1<<2))); // Check BTF
// No more data to send/receive. Initiate a STOP condition. // No more data to send/receive. Initiate a STOP condition and finish
I2C_sendStop(); I2C_sendStop();
state = I2C_STATUS_OK; // Done state = I2C_STATE_COMPLETED;
} }
} else if (s->SR1 && (1<<6)) { } else if (s->SR1 && (1<<6)) {
// Master read completed without errors // Master read completed without errors
if (bytesToReceive == 1) { if (bytesToReceive == 1) {
// s->I2CM.CTRLB.bit.ACKACT = 1; // NAK final byte // s->I2CM.CTRLB.bit.ACKACT = 1; // NAK final byte
I2C_sendStop(); // send stop I2C_sendStop(); // send stop
currentRequest->readBuffer[rxCount++] = s->DR; // Store received byte receiveBuffer[rxCount++] = s->DR; // Store received byte
bytesToReceive = 0; bytesToReceive = 0;
state = I2C_STATUS_OK; // done state = I2C_STATE_COMPLETED;
} else if (bytesToReceive) { } else if (bytesToReceive) {
// s->I2CM.CTRLB.bit.ACKACT = 0; // ACK all but final byte // s->I2CM.CTRLB.bit.ACKACT = 0; // ACK all but final byte
currentRequest->readBuffer[rxCount++] = s->DR; // Store received byte receiveBuffer[rxCount++] = s->DR; // Store received byte
bytesToReceive--; bytesToReceive--;
} }
} }