Skip to content

TWI timeout #1842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 58 additions & 34 deletions hardware/arduino/avr/libraries/Wire/utility/twi.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ static volatile uint8_t twi_rxBufferIndex;

static volatile uint8_t twi_error;

/*
volatile uint32_t twi_iter_count;

/*
* Function twi_init
* Desc readys twi pins and sets twi bitrate
* Input none
Expand All @@ -71,7 +73,7 @@ void twi_init(void)
twi_state = TWI_READY;
twi_sendStop = true; // default value
twi_inRepStart = false;

// activate internal pullups for twi.
digitalWrite(SDA, 1);
digitalWrite(SCL, 1);
Expand All @@ -90,7 +92,7 @@ void twi_init(void)
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}

/*
/*
* Function twi_slaveInit
* Desc sets slave address and enables interrupt
* Input none
Expand All @@ -102,7 +104,7 @@ void twi_setAddress(uint8_t address)
TWAR = address << 1;
}

/*
/*
* Function twi_readFrom
* Desc attempts to become twi bus master and read a
* series of bytes from a device on the bus
Expand All @@ -122,7 +124,8 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
}

// wait until twi is ready, become master receiver
while(TWI_READY != twi_state){
twi_timeout_guard(1);
while((TWI_READY != twi_state) && !twi_timeout_guard(0)){
continue;
}
twi_state = TWI_MRX;
Expand All @@ -134,7 +137,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
twi_masterBufferIndex = 0;
twi_masterBufferLength = length-1; // This is not intuitive, read on...
// On receive, the previously configured ACK/NACK setting is transmitted in
// response to the received byte before the interrupt is signalled.
// response to the received byte before the interrupt is signalled.
// Therefor we must actually set NACK when the _next_ to last byte is
// received, causing that NACK to be sent in response to receiving the last
// expected byte of data.
Expand All @@ -148,7 +151,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent outselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
TWDR = twi_slarw;
Expand All @@ -159,7 +162,8 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

// wait for read operation to complete
while(TWI_MRX == twi_state){
twi_timeout_guard(1);
while((TWI_MRX == twi_state) && !twi_timeout_guard(0)){
continue;
}

Expand All @@ -170,11 +174,11 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen
for(i = 0; i < length; ++i){
data[i] = twi_masterBuffer[i];
}

return length;
}

/*
/*
* Function twi_writeTo
* Desc attempts to become twi bus master and write a
* series of bytes to a device on the bus
Expand All @@ -199,7 +203,8 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
}

// wait until twi is ready, become master transmitter
while(TWI_READY != twi_state){
twi_timeout_guard(1);
while((TWI_READY != twi_state) && !twi_timeout_guard(0)){
continue;
}
twi_state = TWI_MTX;
Expand All @@ -210,16 +215,16 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
// initialize buffer iteration vars
twi_masterBufferIndex = 0;
twi_masterBufferLength = length;

// copy data to twi buffer
for(i = 0; i < length; ++i){
twi_masterBuffer[i] = data[i];
}

// build sla+w, slave device address + w bit
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;

// if we're in a repeated start, then we've already sent the START
// in the ISR. Don't do it again.
//
Expand All @@ -228,21 +233,22 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
// We need to remove ourselves from the repeated start state before we enable interrupts,
// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
// up. Also, don't enable the START interrupt. There may be one pending from the
// up. Also, don't enable the START interrupt. There may be one pending from the
// repeated start that we sent outselves, and that would really confuse things.
twi_inRepStart = false; // remember, we're dealing with an ASYNC ISR
TWDR = twi_slarw;
TWDR = twi_slarw;
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
}
else
// send start condition
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs

// wait for write operation to complete
while(wait && (TWI_MTX == twi_state)){
twi_timeout_guard(1);
while((wait && (TWI_MTX == twi_state)) && !twi_timeout_guard(0)){
continue;
}

if (twi_error == 0xFF)
return 0; // success
else if (twi_error == TW_MT_SLA_NACK)
Expand All @@ -253,7 +259,7 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
return 4; // other twi error
}

/*
/*
* Function twi_transmit
* Desc fills slave tx buffer with data
* must be called in slave tx event callback
Expand All @@ -271,22 +277,22 @@ uint8_t twi_transmit(const uint8_t* data, uint8_t length)
if(TWI_BUFFER_LENGTH < length){
return 1;
}

// ensure we are currently a slave transmitter
if(TWI_STX != twi_state){
return 2;
}

// set length and copy data into tx buffer
twi_txBufferLength = length;
for(i = 0; i < length; ++i){
twi_txBuffer[i] = data[i];
}

return 0;
}

/*
/*
* Function twi_attachSlaveRxEvent
* Desc sets function called before a slave read operation
* Input function: callback function to use
Expand All @@ -297,7 +303,7 @@ void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
twi_onSlaveReceive = function;
}

/*
/*
* Function twi_attachSlaveTxEvent
* Desc sets function called before a slave write operation
* Input function: callback function to use
Expand All @@ -308,7 +314,7 @@ void twi_attachSlaveTxEvent( void (*function)(void) )
twi_onSlaveTransmit = function;
}

/*
/*
* Function twi_reply
* Desc sends byte or readys receive line
* Input ack: byte indicating to ack or to nack
Expand All @@ -324,7 +330,7 @@ void twi_reply(uint8_t ack)
}
}

/*
/*
* Function twi_stop
* Desc relinquishes bus master status
* Input none
Expand All @@ -337,15 +343,16 @@ void twi_stop(void)

// wait for stop condition to be exectued on bus
// TWINT is not set after a stop condition!
while(TWCR & _BV(TWSTO)){
twi_timeout_guard(1);
while((TWCR & _BV(TWSTO)) && !twi_timeout_guard(0)){
continue;
}

// update twi state
twi_state = TWI_READY;
}

/*
/*
* Function twi_releaseBus
* Desc releases bus control
* Input none
Expand Down Expand Up @@ -374,7 +381,7 @@ ISR(TWI_vect)
// Master Transmitter
case TW_MT_SLA_ACK: // slave receiver acked address
case TW_MT_DATA_ACK: // slave receiver acked data
// if there is data to send, send it, otherwise stop
// if there is data to send, send it, otherwise stop
if(twi_masterBufferIndex < twi_masterBufferLength){
// copy data to output register and ack
TWDR = twi_masterBuffer[twi_masterBufferIndex++];
Expand All @@ -384,7 +391,7 @@ ISR(TWI_vect)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
Expand Down Expand Up @@ -424,12 +431,12 @@ ISR(TWI_vect)
twi_stop();
else {
twi_inRepStart = true; // we're gonna send the START
// don't enable the interrupt. We'll generate the start, but we
// don't enable the interrupt. We'll generate the start, but we
// avoid handling the interrupt until we're in the next transaction,
// at the point where we would normally issue the start.
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
}
break;
case TW_MR_SLA_NACK: // address sent, nack received
twi_stop();
Expand Down Expand Up @@ -478,7 +485,7 @@ ISR(TWI_vect)
// nack back at master
twi_reply(0);
break;

// Slave Transmitter
case TW_ST_SLA_ACK: // addressed, returned ack
case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack
Expand Down Expand Up @@ -507,7 +514,7 @@ ISR(TWI_vect)
twi_reply(0);
}
break;
case TW_ST_DATA_NACK: // received nack, we are done
case TW_ST_DATA_NACK: // received nack, we are done
case TW_ST_LAST_DATA: // received ack, but we are done already!
// ack future responses
twi_reply(1);
Expand All @@ -525,3 +532,20 @@ ISR(TWI_vect)
}
}

uint8_t twi_timeout_guard(uint8_t init)
{
if(init)
twi_iter_count = 0;
else
{
twi_iter_count++;
if(twi_iter_count > TWI_MAX_ITERS)
{
twi_init();
TWCR = 0;
return 1;
}
}
return 0;
}

5 changes: 4 additions & 1 deletion hardware/arduino/avr/libraries/Wire/utility/twi.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
#define TWI_MTX 2
#define TWI_SRX 3
#define TWI_STX 4


#define TWI_MAX_ITERS 100000UL

void twi_init(void);
void twi_setAddress(uint8_t);
uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t);
Expand All @@ -48,6 +50,7 @@
void twi_reply(uint8_t);
void twi_stop(void);
void twi_releaseBus(void);
uint8_t twi_timeout_guard(uint8_t);

#endif