0

I am confused... I'm using the i2c_write_blocking function, but getting a strange result:

If I do this - everything works as it should; i.e. I can read the values back from CONFIG_REG, and they match with the values I write below.

#define ADDR _u(0x40)
#define CONFIG_REG _u(0x00)
i2c_init(i2c_default, 100000);
uint8_t condata[3];
condata[0] = CONFIG_REG; 
condata[1] = 97
condata[2] = 37
i2c_write_blocking (i2c_default, ADDR, condata, 3, false);

But if I do this - things do not work as they should; the values I read back do not match with the values I write below.

#define ADDR _u(0x40)
#define CONFIG_REG _u(0x00)
i2c_init(i2c_default, 100000);
uint8_t reg = CONFIG_REG;  
uint8_t condt[2];
condt[0] = 97;
condt[1] = 37;
i2c_write_blocking (i2c_default, ADDR, &reg, 1, true);
i2c_write_blocking (i2c_default, ADDR, condt, 2, false);

And here's how I read the register:

#define ADDR _u(0x40)
#define CONFIG_REG _u(0x00)
i2c_init(i2c_default, 100000);
uint8_t reg = CONFIG_REG;  
uint8_t rcvdata[2];
i2c_write_blocking (i2c_default, ADDR, &reg, 1, true);
i2c_read_blocking (i2c_default, ADDR, rcvdata, 2, false);

I've checked the return values from the two i2c_write_blocking calls, and I get a 1 from the first, and a 2 from the second (i.e. what I should see for the commands above). I'm using a recently-installed SDK for the Pico, and running that on an RPi5.

Can someone explain what is happening?

1 Answer 1

1

The I2C functions you're using all perform a single transaction which either writes data or requests (reads) data from the nodes on the bus.

The data you read or write from the perspective of the standard is completely opaque, it's a stream of bytes that the nodes receive or can respond with. However most devices like sensors will use I2C in a transactional manner, meaning you do a write (start, data, stop) the slave then changes it's internal state and perhaps prepare the response for a register read operation.

i2c_write_blocking (i2c_default, ADDR, &reg, 1, true);
i2c_write_blocking (i2c_default, ADDR, condt, 2, false);

When using nostop the result is a repeated start which depending on how the device's internal state machine is implemented might not work properly. For example the device might start filling up it's internal command buffer on the start condition but with the repeated start it might start again from the beginning.

Repeated start like the way you're using it to read a register is fine, in this case the device will prepare the response when it senses the repeated start. If you disabled nostop the device might empty it's response buffer, but this is device specific.

You already have the solution, you will have to prepare the complete data to send including the register number.

These links will show you how such operations are usually implemented, in case of the Arduino Wire interface it's only sending the data when using endTransmission.

Arduino Wire interface implementation for RP2040

BMA456 driver interface

6
  • Thanks for the answer, but I'm still confused. Wrt nostop: AFAIK, I don't use a nostop call in my code, and therefore its relevance is unknown to me. BTW, I asked this question on the Pico GitHub site, and the answer was (something like), "I don't know - just don't do it that way". Also, I think the Arduino Wire interface is different than what they have in the C libs for the Pico - is that not true?
    – Seamus
    Commented Apr 3 at 22:13
  • The last parameter in both i2c_write_blocking and i2c_read_blocking is the nostop argument. when set to true the bus will not be released and the master can do it's repeated start. The Arduino Wire interface works different in that most functions will only prepare the transaction and it needs to be finalized, like: Wire.begin(devAddr) WIre.write(regAddr) Wire.write(newValue) WIre.endTransmission() Commented Apr 4 at 16:48
  • OK - I was not aware it was called nostop. And I appreciate your explanation of the Arduino interface... IMHO, it's more logical than the one that was developed for the RPi! I wish you'd add something to this Pico GitHub thread - it appears to me the RPi bunch would benefit from the enlightenment!
    – Seamus
    Commented Apr 4 at 21:11
  • I accepted & upvoted yr answer - thanks again. Just one more Q: Given that the I2C functions in Pico's C SDK are built up from more primitive functions, do you think it would be possible to write a more rational i2c_write_blocking function from these primitive functions?
    – Seamus
    Commented Apr 4 at 21:37
  • A drawback of the Wire interface is that it requires a preallocated buffer for data, where as the pico-sdk interface just asks for a pointer and therefore it could be on the stack. It's definitely possible to write your own wrapper around this, you could implement an alternative to i2c_write_blocking_internal that takes the register number as a parameter, but this would be device specific because not all I2C devices work like this, an EEPROM for example might use 16 bit addresses. Commented Apr 5 at 14:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.