mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-22 15:46:14 +01:00
I2CManager: Update native drivers for MUX support from the common code.
This commit is contained in:
parent
553a94bf67
commit
0b307a67e4
157
I2CManager_AVR.h
157
I2CManager_AVR.h
|
@ -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,134 +137,30 @@ 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) {
|
||||||
case TWI_MTX_DATA_ACK: // Data byte has been transmitted and ACK received
|
case TWI_MTX_DATA_ACK: // Data byte has been transmitted and ACK received
|
||||||
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
|
if (operation == OPERATION_READ || (operation==OPERATION_REQUEST && !bytesToSend))
|
||||||
uint8_t deviceAddress = currentRequest->i2cAddress;
|
TWDR = (deviceAddress << 1) | 1; // SLA+R
|
||||||
if (operation == OPERATION_READ || (operation==OPERATION_REQUEST && !bytesToSend))
|
else
|
||||||
TWDR = (deviceAddress << 1) | 1; // SLA+R
|
TWDR = (deviceAddress << 1) | 0; // SLA+W
|
||||||
else
|
TWCR = (1<<TWEN)|ENABLE_TWI_INTERRUPT|(1<<TWINT)|(1<<TWEA);
|
||||||
TWDR = (deviceAddress << 1) | 0; // SLA+W
|
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user