(I2C) Wire.endTransmission always returns non-zero.

DUE, 1.5.1r2

I've noticed that the return byte from endTransmission always returns a non-zero value, even when the transmission appears to function properly.

Can anyone else confirm this?

    //Service note: Wire.endTransmission(false) does not work as advertised.  A STOP is always generated.  
    Wire.endTransmission();  
    //result = Wire.endTransmission();
    // result always returns non-zero, even though everything seems to work.  Strange.
    //if (result != 0) {
    //  DBG_PRINT("xmitt result: ");
    //  DBG_PRINTLN(result);
    //}

-Chris

So what it is it returning? Have you tried with another I2C device?

Unfortunately, I have only 1 device powered up at the moment (hence my request for confirmation).

I always get a result of '2'.

Perhaps the shown glitch is causing some confusion? In any case, I'm wondering what others see.

See attached plot.

0:success
1:data too long to fit in transmit buffer
2:received NACK on transmit of address
3:received NACK on transmit of data
4:other error

-Chris

It looks like endTransmission returns the number of bytes sent from the transmit buffer, not an indication of success, as Wire - Arduino Reference suggests.

from \arduino-1.5.1r2\hardware\arduino\sam\system\libsam\source\twi.c :

        uint8_t TwoWire::endTransmission(uint8_t sendStop) {
	// transmit buffer (blocking)
	TWI_StartWrite(twi, txAddress, 0, 0, txBuffer[0]);
	TWI_WaitByteSent(twi, XMIT_TIMEOUT);
	int sent = 1;
	while (sent < txBufferLength) {
		TWI_WriteByte(twi, txBuffer[sent++]);
		TWI_WaitByteSent(twi, XMIT_TIMEOUT);
	}
	TWI_Stop( twi);
	TWI_WaitTransferComplete(twi, XMIT_TIMEOUT);

	// empty buffer
	txBufferLength = 0;

	status = MASTER_IDLE;
	return sent;
        }

Correct?

-Chris

chriskner

yes, exactly, it's a wrong implementation of the Wire API.

Could you please file an issue here

to keep track of this bug

Hello, i also noticed this bug, but how can we get this working? Is there a work-around or something?

All the examples for i2c especially the MPU6050 example here:
http://playground.arduino.cc/Main/MPU-6050#sketch

uses the old? wire.endtransmission function to determine if there was success or an error and to hold/release the bus, but i have no idea how to change this.

Does anyone have a working I2C for arduino 1.51r2 and could send me the source files,please?

Perhaps the best you can hope for is to use a version of:
(From : \arduino-1.5.1r2\hardware\arduino\sam\libraries\Wire\Wire.cpp)

static inline bool TWI_WaitTransferComplete(Twi *_twi, uint32_t _timeout)

and/or

static inline bool TWI_WaitByteSent(Twi *_twi, uint32_t _timeout)

This checks for the byte being sent, and if an NACK hasn't been received (which is good). You would need to invert the return value...

ALSO (noticed in MPU6050_read, maybe other places too),

Wire.endTransmission(false);

does not work as expected in 1.5r2. I'm fighting this in my code now.
see:
http://arduino.cc/forum/index.php/topic,137607.0.html

-Chris

Ya, I think (in wire.cpp):

static inline bool TWI_WaitTransferComplete(Twi *_twi, uint32_t _timeout) {
	while (!TWI_TransferComplete(_twi)) {
		if (TWI_FailedAcknowledge(_twi))
			return false;
		if (--_timeout == 0)
			return false;
	}
	return true;

}
might be broken too.

Please correct me... I think the act of reading the status register during TWI_TransferComplete() will reset the NACK flag in TWI_SR.

If that's really the case, then TWI_FailedAcknowledge() will never trip.

I've spun my own where I load TWI_SR into a holder to allow me to check for NACK and TXCOMP (both flags are set simultaneously by the MCU).

So, the upshot is that for 1.5.1r2 on the Due, you should stay well-away from endTransmission(), and code your own.

It seems the entire 'wire' is a bit hosed.

My current scheme:

    //Service note: Wire.endTransmission(false) does not work as advertised.  A STOP is always generated.  
    // not only that, but NACK's are ignored too.
    //
    //The Problem:  A NACK (slave not found) error is only available after a STOP bit has been issued.
    // Cannot detect a bad address until we complete the whole process.
    //Worse: The NACK flag is reset everytime any part of the Status register is read by code.
    TWI_StartWrite(WIRE_INTERFACE, i2c_addr, 0, 0, i2c_register);
    Embiic_WaitByteSent();
    i=0;
    while (i < numOfBytes) {
      TWI_WriteByte(WIRE_INTERFACE, params[i++]);
      if (i < (numOfBytes - 1)) {  //cannot check the i2c status register on the last transmit, need to
        Embiic_WaitByteSent();    // preserve any flagged NACK's
      }
    }
    TWI_Stop(WIRE_INTERFACE);
    if (!Embiic_CheckTXCOMP()) {  //look for TX Completion, and check the NACK flag.
      Error_Code = 2110;
    }
boolean Embiic_CheckTXCOMP() {
  unsigned long start, now;
  uint32_t sr, tw_nack, tw_txcomp;
  
  tw_txcomp = 0;
  start = micros();
  
  while (tw_txcomp == 0) {
    sr = WIRE_INTERFACE->TWI_SR;
    now = micros();
    if (now < start) {  // handle overflow, at at the expense of a longer timeout
      start = now;
      now++;
    } else {
      now = now - start;
    }
    if (now > I2C_ACK_TIMEOUTuS){
      DBG_PRINTLN("Embiic_CheckTXCOMP: Timeout.");
      return false;
    }
    tw_nack = ((sr & TWI_SR_NACK) & TWI_SR_NACK);
    if (tw_nack != 0) {
      DBG_PRINTLN("Embiic_CheckTXCOMP: NACK. Natch.");
      return false;
    }
    tw_txcomp = ((sr & TWI_SR_TXCOMP) & TWI_SR_TXCOMP);
  }
  return true;
}

-Chris

I'm fiddling with the Wire.cpp code myself now, based on Chris' notes. One thing that stands out on top of everything else (and which doesn't appear to be specifically mentioned in these threads) is that sendStop is being ignored entirely right now... endTransmission() is effectively identical to endTransmission(sendStop) for all values of sendStop when it comes to the Due "sam" library.

Marty,

This has already been mentioned in:

I2C Repeated Start
http://arduino.cc/forum/index.php/topic,137607.0.html by cdan.

If you need to do anything but the most-basic I2C's, then you'll need to ignore Wire.cpp, and construct your own.

-Chris

That thread didn't match the particular search I did so it was missed (though I'd stumbled upon it before).

I'm looking at how to fix Wire.cpp itself (at least getting it back to the original functionality for the Due). If I learn anything useful I'll share it.

Note, this is a confirmed bug in the Wire library for Due. There is a fix here, just waiting to be merged into the Arduino Github repo:

So hopefully they will merge this soon and it will be available in the next beta 1.5 release.