I2C Repeated Start

Hello,
I had a quick look over the LTC2489's specs and I think it would be very easy to interface it with the Due board.

  • You WRITE to the device's input register to select the input channels for the next selection. Input register is 8bit long.
  • You READ from the device's output register the value of the last conversion. The output register is 24bit long
  • The device starts a new conversion after the output register is READ and the master has generated a STOP CONDITION
  • If you want to select a different channel and read the previously converted value, in a single transaction, then you use a REPEATED START condition

Check out the attachment.
I'll have a look to the PCA9539, later this evening.

Thanks Dan,

I'm on the road right now. I'll check it all out in one week.

-Chris

Hi Dan,
(DUE, 1.5.1r2)

I also have confirmed that a 'Stop' is always sent regardless of including true/false in endTransmission().

I can supply logic traces if anyone's interested.

Now, perhaps this neophyte could have a bit more help?

How does one invoke 'TWI_Startread' in code? Specifically, what would I include for the first parameter:
TWI_StartRead(???, i2c_addr, i2c_register, 8 );

Thanks,

Chris

The first param is a pointer to a Twi structure denoting the I2C bus that you want to use.
In the case of arduino due there are 2 I2C buses, therefore you can use WIRE_INTERFACE_ID or WIRE1_INTERFACE_ID. Both of them are defined in hardware\arduino\sam\variants\arduino_due_x\variant.h

Dan,

Please forgive my ignorance on this matter.

However:

TWI_StartRead(WIRE_INTERFACE_ID, i2c_addr, i2c_register, 8 );

results in:

error: invalid conversion from 'int' to 'Twi*'
error: initializing argument 1 of 'void TWI_StartRead(Twi*, uint8_t, uint32_t, uint8_t)'

I have this uncomfortable feeling of not knowing the right questions to ask, and where to look to figure things out...

Regards,

Chris

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.