[Solved] I2C master reciever from 3rd party

Hello.

I’m trying to read some register values from an LED driver.

Driver I2C sequence: The white rectangles come from master and the gray ones from the slave.

My attempt at a code:

for (int i = 0; i <  reg_count_OTP0; i++){   //reg_count_OTP0 - # of used register lines

    memset(reg_temp_buffer, 0, sizeof(reg_temp_buffer)); // Resetting the buffer
    n = 0;   // Resetting the temp array byte possition
    
    Wire.begin();
    Wire.beginTransmission(I2C_OTP0_address);    // I2C address of the driver for OTP area 0
    Wire.write(Reg_add_OTP0[i]);                 // Register address for OTP0
    Wire.write(CRC6_prvi_OTP0);                  // Combined byte - 2 data bits + CRC6 6 bits
    Wire.endTransmission(false);                 // I'm hoping this makes the "SR" bit for "repeat start condition"
    Wire.requestFrom(I2C_OTP0_address, 3);       // I2C address with the "1" read bit at the end?
    while(Wire.available()) {
     reg_temp_buffer[n] = Wire.read();             // Not sure about this segment
     n++;
    }  
    Wire.endTransmission();
              
    Reg_read_OTP0_dat1[i] = reg_temp_buffer[0];         // recieved bytes
    Reg_read_OTP0_dat2[i] = reg_temp_buffer[1];  
    CRC6_read_OTP0 = reg_temp_buffer[2];
    
    Serial.print(Reg_read_OTP0_dat1[i], HEX);    // Displays the value in HEX format for easier checking
    Serial.println(Reg_read_OTP0_dat2[i], HEX);
    }

I’m not an expert on I2C with arduino, obviously. :-[ I’m wondering if this code would match the sequence in the picture? The aim is to read the 2 data bytes and a CRC byte from the driver per register address. The dirver is capable of sending multiple byte “strings”, but I only need one just as seen in the picture.

Not sure how the while(Wire.available()) loop. Did I set it up correctly to read out the 3 bytes?

Extra info:

The complete cketch passes the “verify”.

I’m using arrays for register addresses and data. Since I can read only a specific register address data at a time, I’m using the “for” loop to cycle through all the addresses. Once It’s done I use the arrays for comparison with the ones I sent.

Also I don’t have access to the LED driver yet so I cannot do “live” testing. So I want to learn as much as possible before it gets hectic.

I’d appreciate any suggestions/solutions. I hope I don’t need a “hold by the hand” type of explenation. A short one with a link will also be appreciated :slight_smile:

Edit: I’ve seen the active topic with a very similar title. It’s not quite what I’m looking for.

Edit2: Attached the complete code. - It’s still rought around the edges, also work in progress :slight_smile:

sketch_jun07a_LEDdriver_english.ino (7.6 KB)

Not sure how the while(Wire.available()) loop. Did I set it up correctly to read out the 3 bytes?

Yes, that should work although you should check the return value of Wire.requestFrom() as a number smaller than the requested byte count shows an error in the I2C protocol.
The code is not complete, so to make it work you have to implement the CRC routines additionally and construction of the last byte written is also not in your code yet.

Use the "Wire.begin()" in the setup() function, not in that for-loop.
Do not use a Wire.endTransmission() after a Wire.requestFrom().
Show a complete sketch: http://snippets-r-us.com/. By not showing a complete sketch, we do not know what "reg_temp_buffer", "Reg_read_OTP0_dat1", and "Reg_read_OTP0_dat2" are.

Thanks for the repplies.

Koepel:
Use the “Wire.begin()” in the setup() function, not in that for-loop.
Do not use a Wire.endTransmission() after a Wire.requestFrom().

I didn’t include the complete code because I wanted to focus mainly on this section. Also all the comments are in my native language :smiley:
Read the manual for attaching the complete code and using snippets. Will edit and make sure to use it in the future :slight_smile:

Had the Wire.begin() in setup, forgot to remove it from the I2C loop, thanks for pointing it out :slight_smile:

I assume now that Wire.requestFrom() sends the “stop” bit after it reads the requested bytes?

As for the CRC6 calculation I still have some questions for the supplier to be sure how exactly it is calculated. I’ve tried several online calculators and haven’t been able to come to the same result. They only give the polynomial value 0x2C = x^6+x^4+x^3+1 for MHD of 4. I’ve seen some calculators needing some initial value and some XOR value… Need to read up on that a bit more.
If anyone knows of a nice guide I’d be happy for any links they can share.

Attached the .ino file.

Edit: In the code I’m calling OTP0_write. The function has already been changed to just OTP0 but forgot to change it in the switch case.

sketch_jun07a_LEDdriver_english.ino (7.6 KB)

The Wire.requestFrom() sends a START, puts the I2C address on the bus, reads the data and sends a STOP.
To allow a repeated start after the Wire.requestFrom(), a third parameter has been added to be able to omit the STOP. The default is that the STOP is send, just as it was before that parameter was added.
https://www.arduino.cc/en/Reference/WireRequestFrom

My Wiki explains the functions in a different way than the Arduino documentation: https://github.com/Koepel/How-to-use-the-Arduino-Wire-library/wiki/Explanation-of-the-functions-of-the-Wire-library.

Thanks for the links.

Wire.begin();
    Wire.beginTransmission(I2C_OTP0_address);    // I2C address of the driver for OTP area 0
    Wire.write(Reg_add_OTP0[i]);                 // Register address for OTP0
    Wire.write(CRC6_prvi_OTP0);                  // Combined byte - 2 data bits + CRC6 6 bits
    //Wire.endTransmission(false);                 // I'm hoping this makes the "SR" bit for "repeat start condition"
    Wire.requestFrom(I2C_OTP0_address, 3);       // I2C address with the "1" read bit at the end?
    while(Wire.available()) {
     reg_temp_buffer[n] = Wire.read();             // Not sure about this segment
     n++;
    }

I hope I understood correctly. The start bit from Wire.requestFrom(I2C_OTP0_address, 3) replaces the "repeat start" bit I sent with the Wire.endTransmission(false).

The Wire.beginTransmission() is not a start !
The Wire.endTransmission() is not a stop !

Remove those comments to put that Wire.endTransmission() there to write the register address please.

If you want, you can use the parameter "false" (as you have) to omit the STOP for the Wire.endTransmission(). In that case a repeated is created when the Wire.requestFrom() sends a START.

Can I improve my Wiki ?
Should I explain it better than: Wire.beginTransmission(), Wire.write(), Wire.endTransmission(). These three functions work together. The Wire.beginTransmission() and Wire.endTransmission() should never be used on their own.

The Arduino Wire library for AVR and SAMD chips uses a buffer for writing or reading data.
The Wire.beginTransmission() clears the buffer.
The Wire.write() writes data to that buffer.
The Wire.endTransmission() sends a START, writes the data, and sends a STOP (the STOP can be omitted with the parameter).

Therefor, if you remove the Wire.endTransmission(), then nothing happens.

Note: I like to push others in the right direction to use the Wire library correctly. However, I don't even like the Wire library. I don't like the internal code (blocking), I don't like the functions (confusing) and I don't like the way it should be used (could be easier). The Wire library is used wrong so many times, that I'm waiting for the day that someone of the Arduino team changes the official documentation and changes it for the wrong use of the Wire library.

Koepel:
Can I improve my Wiki ?

Thanks for the repply. Also hard to say since the Wire library only gets "troublesome" if one wants to use it for "non-standard" devices. Your explaination of Wire.beginTransmission(), Wire.write(), Wire.endTransmission() if pretty clear I think.

If I understood correctly you use Wire.write() to fill the buffer of Wire.beginTransmission() and with **Wire.endTransmission()**you tell that it's recieved all data and can send the I2C block? In a sense it's a "fragmented" Wire.requestFrom() but for sending data.

// Wire.begin(); is in setup

    Wire.beginTransmission(I2C_OTP0_address);    // I2C address of the driver
    Wire.write(Reg_add_OTP0[i]);                 // Register address for OTP0
    Wire.write(CRC6_prvi_OTP0);                  // Combined byte - 2 data bits + CRC6 6 bits
    Wire.endTransmission(false);                 // Repeat start condition
    Wire.requestFrom(I2C_OTP0_address, 3);       // Reads the requsted bytes/data and takes care of the START and END bit.

    while(Wire.available()) {
     reg_temp_buffer[n] = Wire.read();             // Reading from buffer
     n++;
    }

I hope I've managed to fix the code correctly.

Another issue - do I need to add a condition to the while(Wire.available()) or will it stop when it the buffer reaches 0 bytes remaining by default. Usually a while has a condition like while(x > 0).

Yes, fragmented. There are other Wire-compatible libraries that do other things in those functions. But as long as those three stay together, then it should work.

You can do other things instead of the while.

This is what you have now:

n = 0;

Wire.requestFrom(I2C_OTP0_address, 3);       // Reads the requsted bytes/data and takes care of the START and END bit.

while(Wire.available()) {
  reg_temp_buffer[n] = Wire.read();             // Reading from buffer
  n++;
}

The while(…) tests if the condition is true or false. Since false is zero, it is allowed.
You can also do: “while ( Wire.available() > 0 )”. I prefer it that way, it shows better what is going on.

These are alternative ways:

Wire.requestFrom(I2C_OTP0_address, 3);       // Reads the requsted bytes/data and takes care of the START and END bit.
reg_temp_buffer[0] = Wire.read();
reg_temp_buffer[1] = Wire.read();
reg_temp_buffer[2] = Wire.read();
Wire.requestFrom(I2C_OTP0_address, 3);       // Reads the requsted bytes/data and takes care of the START and END bit.
for( int i=0; i<3; i++)
{
  reg_temp_buffer[n] = Wire.read();             // Reading from buffer
}
Wire.requestFrom(I2C_OTP0_address, 3);       // Reads the requsted bytes/data and takes care of the START and END bit.
Wire.readBytes( reg_temp_buffer, 3);

When 3 bytes are requested, a check can be added if 3 bytes are also received. There are also a few different options for that.

Koepel:
Yes, fragmented.

Thanks for your help.

I also prefer adding the condition to "while".

After getting to know the "wire.lib" your Wire.read solutions do look logical. Hindsight and all that.
Will be able to further optimize the code. :slight_smile:

I think the code is ready for testing. I hope the devices will comunicate. Ironing out the details should be easy after that.