I2C Repeated Start

It's my mistake. Use WIRE_INTERFACE or WIRE1_INTERFACE
You can find both defined in variant.h

Thanks Dan... Onward!

After some turmoil, I have retreated to the state where only 1 byte at a time may be read from a slave.

Figure 34-20. "TWI Read Operation with Multiple Data Bytes with or without Internal Address" in the SAM3X8E datasheet seems out of reach. (Page 729, of the 28-May-12 revision)

I have found that a NACK is always generated after the 1st read by some unidentified mechanism, making multiple byte reads impossible.
For the desired protocol, see Figure 34-10. "Master Read with Multiple Data Bytes" (Page 721).

It seems that the Arduino community has been driving in circles over this problem for years. For a sample:
http://code.google.com/p/arduino/issues/detail?id=28

I haven't been able to find a way to specify how the master (the Due) should respond for multiple sequential reads.

I have resorted to supporting only 1 byte reads for the foreseeable future. This, of course, is a hack in my 50+ device I2C network.

Any ideas/suggestions?

    TWI_StartRead(WIRE_INTERFACE, i2c_addr, i2c_register, 1);
    Embiic_WaitByteXferComplete();
    for(i = 0; i < numOfBytes; i++) {
      //Need to set STOP condition WHILE reading the last byte...
      if ((i+1) == numOfBytes) {
        TWI_SendSTOPCondition(WIRE_INTERFACE);
        //DBG_PRINT("Sending last-byte STOP.");
      }
      Embiic_WaitByteReceived();
      //DBG_PRINTLN("Read_I2C_Values: Byte Ready");
      params[i] = TWI_ReadByte(WIRE_INTERFACE);
    }
    if (Embiic_WaitByteXferComplete()) {
      DBG_PRINTLN("Read_I2C_Values: Transaction completed successfully.");
    } else {
      DBG_PRINTLN("Read_I2C_Values: Transaction nottacompleted.");
    }

Can you please post a screenshot of the output from running your algorithm with numOfBytes=2 ?

The traces above are the same with any numOfBytes value. That one was generated with nOB=3.

However, I get the same result for nOB= 1.

Of course, as you know, we expect the NACK on only the last read, and ACK's on all 1..n-1 reads.

The way that it is now, all reads beyond the first byte result in a timeout. (code below)

I suspect that there's a state machine somewhere (that I can't find), that I would like to look over.

I did find this:

/arduino-1.5.1r2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/html/TWI0.html

which lists the TW registers in a nice format. However, there isn't much to play with there.

boolean Embiic_WaitByteReceived() {
  unsigned long start, now;
  start = millis();
  while (!TWI_ByteReceived(WIRE_INTERFACE)) {
    //if (WIRE_INTERFACE->TWI_SR & TWI_SR_NACK) { // Failed ACK
    //  DBG_PRINTLN("Embiic_WaitByteReceived: Failed ACK.");
    //  Error_Code = 9010;
    //  return false;
    //}
    now = millis();
    if (now < start) {  // cheap! handle overflow, at at the expense of a longer timeout
      start = now;
      now++;
    } else {
      now = now - start;
    }
    if (now > I2C_READ_TIMEOUTmS){
      DBG_PRINTLN("Embiic_WaitByteReceived: Timeout.");
      Error_Code = 9011;
      return false;
    }
  }
  return true;
}

I have just order a Bus Pirate from a retailer in RO. I'll have it tomorrow and I'll give it a try too.
I have to look closer to this issue.

Hello,
I use this code to read the temperature from the MLX90614 IR sensor (see attachment).
The sensore has address 0x5A and I read 3 bytes from internal address 0x07.
As you can see in the captured signal, the controller sends 2 ACK and one NACK just before the STOP condition.

I suppose there is something wrong in your code, can you send it to me to have a look? Or share it somewhere on the internet.

Best regards,
Dan.

MLX90614_Direct_Access.ino (3.41 KB)

Great Dan!

I'll check it out on Monday.

Dan,

You got it. Coding error. It looks like I had two instances of twi going on...

Everything is working fine now.

Many thanks!

    //start read, then get n-1 responses
    TWI_StartRead(pTwi, i2c_addr, i2c_register+i, 1);
    if (!Embiic_WaitByteSent()) {//A NACK indicates a bad address, or broken com.  Exit.
      Error_Code = 100;
      TWI_SendSTOPCondition(pTwi);
      break;
    }
    for(i = 0; i < numOfBytes-1; i++) {
      if (Embiic_WaitByteReceived()){
        params[i] = TWI_ReadByte(pTwi);
      } else {
        params[i] = 0;
        Error_Code = 101;
        TWI_SendSTOPCondition(pTwi);
        break;
      }
    }
    if (Error_Code > 0) {  //in case error from for loop above
      break;
    }

    //now send stop before last receive request
    TWI_SendSTOPCondition(pTwi);
    if (Embiic_WaitByteReceived()){
      params[i] = TWI_ReadByte(pTwi);
    } else {
      params[i] = 0;
      Error_Code = 101;
      TWI_SendSTOPCondition(pTwi);
      break;
    }

-Chris

Hi Dan and Chris,

Do you mind sharing the code for the use of a MLX90614? I'm also trying to get one running on a Due, but unfortunatelly I'm really not into (machine)code. What I've seen in your posts is really to much for me.

regards,
Jelle

You can find the code attached to one of the posts above.
The name of the attachment is MLX90614_Direct_Access.ino

Feel free to use it in any way you like.

I2C now working on the Due, many thanks cdan , a few address changes and the MPU6050 who am I reg is answering to your MLX90614 code.

Can you please share your code for the MPU6050?

Chris, can you share the function Embiic_WaitByteSent()?
I tested the method MLX90614_Direct_Access.ino on DUE + IR sensor TI TMP006.
Object temperature is not displayed correctly :frowning:

Sure... It's more than you asked for, but here are some checking routines:
(Embiic is the name of the project this code was used in)

uint32_t I2C_Get_Status_Register(){
  return pTwi->TWI_SR;
}

boolean Embiic_Check_NACK(uint32_t sr) {
  return ((sr & TWI_SR_NACK) & TWI_SR_NACK);
}

boolean Embiic_Check_TXCOMP(uint32_t sr) {
  return ((sr & TWI_SR_TXCOMP) == TWI_SR_TXCOMP);
}
boolean Embiic_Wait_ACK_TXCOMP() {
  unsigned long start, now;
  uint32_t sr, tw_nack, tw_txcomp;
  
  tw_txcomp = 0;
  start = micros();
  
  while (tw_txcomp == 0) {
    sr = I2C_Get_Status_Register();
    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.");
      I2C_Reset_Request = 1;
      return false;
    }
    tw_nack = Embiic_Check_NACK(sr);
    if (tw_nack != 0) {
      DBG_PRINTLN("Embiic_CheckTXCOMP: NACK. Natch.");
      return false;
    }
    tw_txcomp = Embiic_Check_TXCOMP(sr);
  }
  return true;
}
boolean Embiic_Wait_TXCOMP() {
  unsigned long start, now;
  uint32_t sr, tw_txcomp;
  
  tw_txcomp = 0;
  start = micros();
  
  while (tw_txcomp == 0) {
    sr = I2C_Get_Status_Register();
    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.");
      I2C_Reset_Request = 1;
      return false;
    }
    tw_txcomp = Embiic_Check_TXCOMP(sr);
  }
  return true;
}
boolean Embiic_WaitByteSent() {
  uint32_t sr;
  //bad: TXCOMP is 1, NACK is 1
  unsigned long start, now;
  start = micros();
  while (1) {
    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_WaitByteSent: Timeout.");
      I2C_Reset_Request = 1;
      return false;
    }
    sr = I2C_Get_Status_Register();
    if ((sr & TWI_SR_NACK) & TWI_SR_NACK) {
      DBG_PRINTLN("Embiic_WaitByteSent: NACK. Bad Address?");
      return false;
    }
    if ((sr & TWI_SR_TXRDY) == TWI_SR_TXRDY) {
      return true;
    }
  }
  return true;
}
boolean Embiic_WaitByteReceived() {
  unsigned long start, now;
  start = millis();
  while (!TWI_ByteReceived(WIRE_INTERFACE)) {
    now = millis();
    if (now < start) {  // handle overflow, at at the expense of a longer timeout
      start = now;
      now++;
    } else {
      now = now - start;
    }
    if (now > I2C_READ_TIMEOUTmS){
      DBG_PRINTLN("Embiic_WaitByteReceived: Timeout.");
      I2C_Reset_Request = 1;
      return false;
    }
  }
  return true;
}

-Chris
I'm not a coder. I just play one on the internets.

Thank you Chris!
Unfortunately, did not work :frowning:
With MEGA R3 sensor indicates correct.
I will continue to investigate the cause...

TI TMP006.png

Hy
I tried the sketch and it work fine.
I would tell if is possible change the SA address, for multi MLX90614
Somebody has some example? thanks

Can someone please help with the libray for MLX90614_Direct_Access.ino

Hello cdan,

I've the following issue: Arduino DUE I2C Issues - Arduino Due - Arduino Forum

Reading your post:

cdan:
Hello,
what board are you going to use ? What are the specific I2C transactions that you'll need?

With the Due board, I was able to read/write from/to an internal register of a I2C slave device (MLX90614), using the API in the libsam library. You can find the source in
arduino-1.5.1r2\hardware\arduino\sam\system\libsam\source. Look for twi.c and twi.h
The functions of interest are void TWI_StartRead and void TWI_StartWrite. They both have a parameter called uint32_t iaddress where you can put the internal address/register you want to access. If you specify it, then the controller will automatically send a REPEATED START condition after writing the address of the slave device and of the internal register on the bus and before reading/writing the data bytes.

Best regards,
Dan.

...I think you mean that I need to use this format?:

requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop)

What is my iaddress in this case?

Thanks!

cdan:
Hello,
what board are you going to use ? What are the specific I2C transactions that you'll need?

With the Due board, I was able to read/write from/to an internal register of a I2C slave device (MLX90614), using the API in the libsam library. You can find the source in
arduino-1.5.1r2\hardware\arduino\sam\system\libsam\source. Look for twi.c and twi.h
The functions of interest are void TWI_StartRead and void TWI_StartWrite. They both have a parameter called uint32_t iaddress where you can put the internal address/register you want to access. If you specify it, then the controller will automatically send a REPEATED START condition after writing the address of the slave device and of the internal register on the bus and before reading/writing the data bytes.

Best regards,
Dan.

Thank you, this works fine, but there is another problem, I have "iaddress" has a length of "isize" equal to 4 bytes, and in the library "TWI.C" the limit is no more than 3 bytes, what should I do in this case? I need to read data on the 10 - bit bus I2C.