diff --git a/app/build.xml b/app/build.xml index 06cc52fd7a1..6e6f07f50ef 100644 --- a/app/build.xml +++ b/app/build.xml @@ -37,7 +37,7 @@ --> - BUFFER_LENGTH){ quantity = BUFFER_LENGTH; } // perform blocking read into buffer - uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop); + uint8_t read = twi_readFrom_timeout(address, rxBuffer, quantity, sendStop, timeout_us); // set rx buffer iterator vars rxBufferIndex = 0; rxBufferLength = read; @@ -90,6 +98,10 @@ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop return read; } +uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop){ + return requestFrom_timeout(address, quantity, sendStop, 0); +} + uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) { return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true); @@ -134,10 +146,11 @@ void TwoWire::beginTransmission(int address) // no call to endTransmission(true) is made. Some I2C // devices will behave oddly if they do not see a STOP. // -uint8_t TwoWire::endTransmission(uint8_t sendStop) + +uint8_t TwoWire::endTransmission_timeout(uint8_t sendStop, uint32_t timeout_us) { // transmit buffer (blocking) - int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop); + int8_t ret = twi_writeTo_timeout(txAddress, txBuffer, txBufferLength, 1, sendStop, timeout_us); // reset tx buffer iterator vars txBufferIndex = 0; txBufferLength = 0; @@ -146,6 +159,10 @@ uint8_t TwoWire::endTransmission(uint8_t sendStop) return ret; } +uint8_t TwoWire::endTransmission(uint8_t sendStop){ + return endTransmission_timeout(sendStop, 0); +} + // This provides backwards compatibility with the original // definition, and expected behaviour, of endTransmission // diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index a93d0f5bb30..00c2d4362f6 100755 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -49,12 +49,16 @@ class TwoWire : public Stream void begin(); void begin(uint8_t); void begin(int); + void begin_timeout(uint32_t); + void begin_timeout(uint8_t, uint32_t); void beginTransmission(uint8_t); void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); + uint8_t endTransmission_timeout(uint8_t, uint32_t); uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(uint8_t, uint8_t, uint8_t); + uint8_t requestFrom_timeout(uint8_t, uint8_t, uint8_t, uint32_t); uint8_t requestFrom(int, int); uint8_t requestFrom(int, int, int); virtual size_t write(uint8_t); diff --git a/libraries/Wire/examples/timeout_digital_potentiometer/timeout_digital_potentiometer.ino b/libraries/Wire/examples/timeout_digital_potentiometer/timeout_digital_potentiometer.ino new file mode 100644 index 00000000000..a9f2e9efc75 --- /dev/null +++ b/libraries/Wire/examples/timeout_digital_potentiometer/timeout_digital_potentiometer.ino @@ -0,0 +1,42 @@ +// I2C Digital Potentiometer +// by Nicholas Zambetti +// and Shawn Bonkowski + +// Demonstrates use of the Wire library +// Controls AD5171 digital potentiometer via I2C/TWI + +// Created 31 March 2006 +// Edited 7 december 2014 by Mauro Mombelli + +// This example code is in the public domain. + +// This example code is in the public domain. + + +#include + +const unsigned long default_wire_timeout_us = 1000UL; + +void setup() +{ + Wire.begin_timeout(default_wire_timeout_us); // join i2c bus (address optional for master) +} + +byte val = 0; + +void loop() +{ + Wire.beginTransmission(44); // transmit to device #44 (0x2c) + // device address is specified in datasheet + Wire.write(byte(0x00)); // sends instruction byte + Wire.write(val); // sends potentiometer value byte + Wire.endTransmission_timeout(true, default_wire_timeout_us); // stop transmitting + + val++; // increment value + if(val == 64) // if reached 64th position (max) + { + val = 0; // start over from lowest value + } + delay(500); +} + diff --git a/libraries/Wire/examples/timeout_master_reader/timeout_master_reader.ino b/libraries/Wire/examples/timeout_master_reader/timeout_master_reader.ino new file mode 100644 index 00000000000..3ca54ffbf4c --- /dev/null +++ b/libraries/Wire/examples/timeout_master_reader/timeout_master_reader.ino @@ -0,0 +1,35 @@ +// Wire Master Reader +// by Nicholas Zambetti + +// Demonstrates use of the Wire library +// Reads data from an I2C/TWI slave device +// Refer to the "Wire Slave Sender" example for use with this + +// Created 29 March 2006 +// Edited 7 december 2014 by Mauro Mombelli + +// This example code is in the public domain. + + +#include + +const unsigned long default_timeout_us = 1000UL; + +void setup() +{ + Wire.begin_timeout(default_timeout_us); // join i2c bus (address optional for master) + Serial.begin(9600); // start serial for output +} + +void loop() +{ + Wire.requestFrom_timeout(2, 6, true, default_timeout_us); // request 6 bytes from slave device #2 + + while(Wire.available()) // slave may send less than requested + { + char c = Wire.read(); // receive a byte as character + Serial.print(c); // print the character + } + + delay(500); +} diff --git a/libraries/Wire/examples/timeout_slave_receiver/.directory b/libraries/Wire/examples/timeout_slave_receiver/.directory new file mode 100644 index 00000000000..1da637eee65 --- /dev/null +++ b/libraries/Wire/examples/timeout_slave_receiver/.directory @@ -0,0 +1,6 @@ +[Dolphin] +Timestamp=2014,12,7,15,37,21 +Version=3 + +[Settings] +HiddenFilesShown=true diff --git a/libraries/Wire/examples/timeout_slave_receiver/timeout_slave_receiver.ino b/libraries/Wire/examples/timeout_slave_receiver/timeout_slave_receiver.ino new file mode 100644 index 00000000000..7eb52e630ce --- /dev/null +++ b/libraries/Wire/examples/timeout_slave_receiver/timeout_slave_receiver.ino @@ -0,0 +1,41 @@ +// Wire Slave Receiver +// by Nicholas Zambetti + +// Demonstrates use of the Wire library +// Receives data as an I2C/TWI slave device +// Refer to the "Wire Master Writer" example for use with this + +// Created 29 March 2006 +// Edited 7 december 2014 by Mauro Mombelli + +// This example code is in the public domain. + + +#include + +const unsigned long default_timeout_us = 1000UL; + +void setup() +{ + Wire.begin_timeout(4, default_timeout_us); // join i2c bus with address #4 + Wire.onReceive(receiveEvent); // register event + Serial.begin(9600); // start serial for output +} + +void loop() +{ + delay(100); +} + +// function that executes whenever data is received from master +// this function is registered as an event, see setup() +void receiveEvent(int howMany) +{ + while(1 < Wire.available()) // loop through all but the last + { + char c = Wire.read(); // receive byte as a character + Serial.print(c); // print the character + } + int x = Wire.read(); // receive byte as an integer + Serial.println(x); // print the integer +} diff --git a/libraries/Wire/utility/twi.c b/libraries/Wire/utility/twi.c index 201d7d1bbbf..139d92078d6 100644 --- a/libraries/Wire/utility/twi.c +++ b/libraries/Wire/utility/twi.c @@ -25,7 +25,7 @@ #include #include #include -#include "Arduino.h" // for digitalWrite +#include "Arduino.h" // for digitalWrite and micros #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) @@ -59,14 +59,19 @@ static volatile uint8_t twi_rxBufferIndex; static volatile uint8_t twi_error; +static volatile uint32_t twi_default_timeout; + /* * Function twi_init * Desc readys twi pins and sets twi bitrate * Input none * Output none */ -void twi_init(void) + +void twi_init_timeout(uint32_t default_timeout) { + twi_default_timeout = default_timeout; + // initialize state twi_state = TWI_READY; twi_sendStop = true; // default value @@ -81,15 +86,16 @@ void twi_init(void) cbi(TWSR, TWPS1); TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; - /* twi bit rate formula from atmega128 manual pg 204 - SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) - note: TWBR should be 10 or higher for master mode - It is 72 for a 16mhz Wiring board with 100kHz TWI */ + // twi bit rate formula from atmega128 manual pg 204 + //SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) + //note: TWBR should be 10 or higher for master mode + //It is 72 for a 16mhz Wiring board with 100kHz TWI // enable twi module, acks, and twi interrupt TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); } + /* * Function twi_slaveInit * Desc sets slave address and enables interrupt @@ -101,7 +107,11 @@ void twi_setAddress(uint8_t address) // set twi slave address (skip over TWGCE bit) TWAR = address << 1; } - +/* +uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop){ + return twi_readFrom_timeout(address, data, length, sendStop, 0); +} +*/ /* * Function twi_readFrom * Desc attempts to become twi bus master and read a @@ -110,9 +120,10 @@ void twi_setAddress(uint8_t address) * data: pointer to byte array * length: number of bytes to read into array * sendStop: Boolean indicating whether to send a stop at the end + * timeout_us: number of microseconds to wait for answr, 0 = no timeout * Output number of bytes read */ -uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop) +uint8_t twi_readFrom_timeout(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop, uint32_t timeout_us) { uint8_t i; @@ -121,9 +132,15 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen return 0; } - // wait until twi is ready, become master receiver + // wait until twi is ready, become master receiver, or timeout + uint32_t start_us = micros(); while(TWI_READY != twi_state){ - continue; + if (timeout_us){ + if (micros() - start_us > timeout_us){ + twi_state = TWI_ERROR; + return 0; + } + } } twi_state = TWI_MRX; twi_sendStop = sendStop; @@ -158,9 +175,15 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen // send start condition TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); - // wait for read operation to complete - while(TWI_MRX == twi_state){ - continue; + // wait for read operation to complete, or timeout + start_us = micros(); + while(TWI_MRX != twi_state){ + if (timeout_us){ + if (micros() - start_us > timeout_us){ + twi_state = TWI_ERROR; + return 0; + } + } } if (twi_masterBufferIndex < length) @@ -172,8 +195,14 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen } return length; + } +/* +uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop){ + return twi_writeTo_timeout(address, data, length, wait, sendStop, 0); +} +*/ /* * Function twi_writeTo * Desc attempts to become twi bus master and write a @@ -183,13 +212,15 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen * length: number of bytes in array * wait: boolean indicating to wait for write or not * sendStop: boolean indicating whether or not to send a stop at the end + * timeout_us: number of microseconds to wait for answr, 0 = no timeout * Output 0 .. success * 1 .. length to long for buffer * 2 .. address send, NACK received * 3 .. data send, NACK received * 4 .. other twi error (lost bus arbitration, bus error, ..) + * 5 .. timeout */ -uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop) +uint8_t twi_writeTo_timeout(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop, uint32_t timeout_us) { uint8_t i; @@ -198,10 +229,18 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait return 1; } - // wait until twi is ready, become master transmitter + // wait until twi is ready, become master transmitter, or timeout + uint32_t start_us = micros(); while(TWI_READY != twi_state){ + if (timeout_us){ + if (micros() - start_us > timeout_us){ + twi_state = TWI_ERROR; + return 5; + } + } continue; } + twi_state = TWI_MTX; twi_sendStop = sendStop; // reset error state (0xFF.. no error occured) @@ -238,8 +277,15 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait // 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)){ + // wait for write operation to complete, or timeout + start_us = micros(); + while( wait && (TWI_MTX != twi_state) ){ + if (timeout_us){ + if (micros() - start_us > timeout_us){ + twi_state = TWI_ERROR; + return 5; + } + } continue; } @@ -324,21 +370,32 @@ void twi_reply(uint8_t ack) } } +/* +void twi_stop(void){ + twi_stop_timeout(0); +} +*/ /* * Function twi_stop * Desc relinquishes bus master status - * Input none + * Input timeout_us: number of microseconds to wait for answr, 0 = no timeout * Output none */ -void twi_stop(void) +void twi_stop_timeout(uint32_t timeout_us) { // send stop condition TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); - // wait for stop condition to be exectued on bus + // wait for stop condition to be exectued on bus, or timeout // TWINT is not set after a stop condition! - while(TWCR & _BV(TWSTO)){ - continue; + uint32_t start_us = micros(); + while( TWCR & _BV(TWSTO) ){ + if (timeout_us){ + if (micros() - start_us > timeout_us){ + twi_state = TWI_ERROR; + return; + } + } } // update twi state @@ -381,7 +438,7 @@ ISR(TWI_vect) twi_reply(1); }else{ if (twi_sendStop) - twi_stop(); + twi_stop_timeout(twi_default_timeout); else { twi_inRepStart = true; // we're gonna send the START // don't enable the interrupt. We'll generate the start, but we @@ -394,11 +451,11 @@ ISR(TWI_vect) break; case TW_MT_SLA_NACK: // address sent, nack received twi_error = TW_MT_SLA_NACK; - twi_stop(); + twi_stop_timeout(twi_default_timeout); break; case TW_MT_DATA_NACK: // data sent, nack received twi_error = TW_MT_DATA_NACK; - twi_stop(); + twi_stop_timeout(twi_default_timeout); break; case TW_MT_ARB_LOST: // lost bus arbitration twi_error = TW_MT_ARB_LOST; @@ -421,7 +478,7 @@ ISR(TWI_vect) // put final byte into buffer twi_masterBuffer[twi_masterBufferIndex++] = TWDR; if (twi_sendStop) - twi_stop(); + twi_stop_timeout(twi_default_timeout); else { twi_inRepStart = true; // we're gonna send the START // don't enable the interrupt. We'll generate the start, but we @@ -432,7 +489,7 @@ ISR(TWI_vect) } break; case TW_MR_SLA_NACK: // address sent, nack received - twi_stop(); + twi_stop_timeout(twi_default_timeout); break; // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case @@ -465,7 +522,7 @@ ISR(TWI_vect) twi_rxBuffer[twi_rxBufferIndex] = '\0'; } // sends ack and stops interface for clock stretching - twi_stop(); + twi_stop_timeout(twi_default_timeout); // callback to user defined callback twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); // since we submit rx buffer to "wire" library, we can reset it @@ -520,7 +577,7 @@ ISR(TWI_vect) break; case TW_BUS_ERROR: // bus error, illegal stop/start twi_error = TW_BUS_ERROR; - twi_stop(); + twi_stop_timeout(twi_default_timeout); break; } } diff --git a/libraries/Wire/utility/twi.h b/libraries/Wire/utility/twi.h index 6526593394e..2fbe57da67d 100755 --- a/libraries/Wire/utility/twi.h +++ b/libraries/Wire/utility/twi.h @@ -37,16 +37,21 @@ #define TWI_MTX 2 #define TWI_SRX 3 #define TWI_STX 4 + #define TWI_ERROR 5 - void twi_init(void); + //void twi_init(void); + void twi_init_timeout(uint32_t); void twi_setAddress(uint8_t); - uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t); - uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t); + //uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t); + uint8_t twi_readFrom_timeout(uint8_t, uint8_t*, uint8_t, uint8_t, uint32_t); + //uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t); + uint8_t twi_writeTo_timeout(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t, uint32_t); uint8_t twi_transmit(const uint8_t*, uint8_t); void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); void twi_attachSlaveTxEvent( void (*)(void) ); void twi_reply(uint8_t); - void twi_stop(void); + //void twi_stop(void); + void twi_stop_timeout(uint32_t); void twi_releaseBus(void); #endif