diff options
Diffstat (limited to 'libraries/Wire/utility/twi.c')
-rw-r--r-- | libraries/Wire/utility/twi.c | 75 |
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; |