aboutsummaryrefslogtreecommitdiff
path: root/libraries/Wire/utility/twi.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/Wire/utility/twi.c')
-rw-r--r--libraries/Wire/utility/twi.c75
1 files changed, 66 insertions, 9 deletions
diff --git a/libraries/Wire/utility/twi.c b/libraries/Wire/utility/twi.c
index d80114b..6b2db3c 100644
--- a/libraries/Wire/utility/twi.c
+++ b/libraries/Wire/utility/twi.c
@@ -15,6 +15,8 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
*/
#include <math.h>
@@ -37,14 +39,16 @@
#include "twi.h"
static volatile uint8_t twi_state;
-static uint8_t twi_slarw;
+static volatile uint8_t twi_slarw;
+static volatile uint8_t twi_sendStop; // should the transaction end with a stop
+static volatile uint8_t twi_inRepStart; // in the middle of a repeated start
static void (*twi_onSlaveTransmit)(void);
static void (*twi_onSlaveReceive)(uint8_t*, int);
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_masterBufferIndex;
-static uint8_t twi_masterBufferLength;
+static volatile uint8_t twi_masterBufferLength;
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_txBufferIndex;
@@ -65,6 +69,8 @@ void twi_init(void)
{
// initialize state
twi_state = TWI_READY;
+ twi_sendStop = true; // default value
+ twi_inRepStart = false;
// activate internal pullups for twi.
digitalWrite(SDA, 1);
@@ -103,9 +109,10 @@ void twi_setAddress(uint8_t address)
* Input address: 7bit i2c device 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
* Output number of bytes read
*/
-uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
+uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{
uint8_t i;
@@ -119,6 +126,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
continue;
}
twi_state = TWI_MRX;
+ twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
@@ -135,8 +143,20 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
twi_slarw = TW_READ;
twi_slarw |= address << 1;
- // send start condition
- TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
+ if (true == twi_inRepStart) {
+ // if we're in the repeated start state, then we've already sent the start,
+ // (@@@ 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
+ // 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;
+ TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START
+ }
+ else
+ // 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){
@@ -162,13 +182,14 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length)
* data: pointer to byte array
* 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
* 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, ..)
*/
-uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait)
+uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
{
uint8_t i;
@@ -182,6 +203,7 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
continue;
}
twi_state = TWI_MTX;
+ twi_sendStop = sendStop;
// reset error state (0xFF.. no error occured)
twi_error = 0xFF;
@@ -198,8 +220,23 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;
- // send start condition
- TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
+ // if we're in a repeated start, then we've already sent the START
+ // in the ISR. Don't do it again.
+ //
+ if (true == twi_inRepStart) {
+ // if we're in the repeated start state, then we've already sent the start,
+ // (@@@ 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
+ // 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;
+ 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)){
@@ -343,7 +380,16 @@ SIGNAL(TWI_vect)
TWDR = twi_masterBuffer[twi_masterBufferIndex++];
twi_reply(1);
}else{
- twi_stop();
+ if (twi_sendStop)
+ twi_stop();
+ else {
+ twi_inRepStart = true; // we're gonna send the START
+ // 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_MT_SLA_NACK: // address sent, nack received
@@ -374,6 +420,17 @@ SIGNAL(TWI_vect)
case TW_MR_DATA_NACK: // data received, nack sent
// put final byte into buffer
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
+ if (twi_sendStop)
+ twi_stop();
+ else {
+ twi_inRepStart = true; // we're gonna send the START
+ // 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();
break;