For what it is worth, I think using a scope to check the transitions and levels first is a good approach. When those are ruled fine, using a cheap logic analyzer with sigrok's Pulseview –– for which you can write your own protocol analyzers in Python, if it does not already have one; it does for I2C –– means you can catch even rare errors by running it continuously for quite some time. And I suggested this before OP posted the scope traces, showing their scope already has a I2C protocol analyzer.
It's not like there is just one way to do this stuff.
Also, I kinda-sorta assumed hpmaxim did check read() and write() return values, because in Linux,
they have to be. Only when you do not care if the data is read or written at all or if an error occurs, is it okay to not check the return value. (The
ioctl(fd, I2C_RDWR, &op) returns the number of successfully completed messages, not the byte count.) I sometimes have heated arguments with people who claim it shouldn't be necessary with say ordinary files; I say it always is necessary, because even running out of disk space is something you need to be prepared to deal with (and cannot verify reliably beforehand,
TOCTOU). I hate the idea of a program I'd use just silently ignore an error, losing or garbling my precious data.
The device in question is an ST LSM6DSM. Datasheet can be found here: https://www.st.com/resource/en/datasheet/lsm6dsm.pdf
Page 43 states: "The slave address is completed with a Read/Write bit. If the bit is ‘1’ (Read), a repeated START (SR) condition must be issued after the two sub-address bytes; if the bit is ‘0’ (Write) the master will transmit to the slave with direction unchanged."
I'll take it you think that's the operative sentence there... That sending the write followed by a separate read is a violation that the ioctl(I2C_RDWR) corrects, rather than either the I2C_SLAVE or I2C_RDWR were invalid I2C messages?
Yes, exactly.
If you think about how the state machine is implemented in the LSM6DSM, it is much easier to require the write and the following read to be a single transaction; otherwise, it would have to save the sub-address bytes somewhere, and deal with partial sub-address bytes and such.
Now, it is a completely separate question whether I'm right or wrong. As a honest uncle bumblefug hobbyist, I'm often wrong.
(I love being pointed out exactly how I'm wrong, because that's the best opportunity to learn. I only get irate, when I'm told I'm wrong, but not exactly why or how. I can't learn anything from that, just wastes my time forcing me to re-examine what I said.. and as nctnico recently noted elsewhere, I have my own pitfalls I can get stuck into.)
What cv007 wrote does apply, I believe. It's just that because I believe the separate transactions produces unreliable results in all cases anyway, I focused on the I2C_RDWR ioctl instead. (Logic being, it is of tertiary interest at best as to what kind of "data" one obtains via the erroneous approach. Make sure the approach itself is correct first, then deal with any coding details involved.)
The reason I posted my own interpretations of the traces is so that if my understanding is wrong, it would be easier for others to point it out.
Is there a way to adjust SCL? I appear to be transmitting at 100kHz, and that actually seems to occupy a lot of time. Pretty much everything seems to support 400kHz.
Yes, by modifying the Device Tree. In your case, it would be the
&i2c1 section,
clock-frequency parameter (defaults to
100000).
I believe you have a
/boot/dtbs/sun7i-a20-cubieboard2.dtb file.
You copy this somewhere for backup purposes, then run
dtc -I dtb -O dts /boot/dtbs/sun7i-a20-cubieboard2.dtb > sun7i-a20-cubieboard2.dtsto decompile (the "device tree blob" back into "device tree source").
Edit it in your favourite editor (vim, emacs, nano), changing the
clock-frequency = <100000>; to
clock-frequency = <400000>; in the
&i2c1 section, then run
dtc -I dts -O dtb sun71-a20-cubieboard2.dts /boot/dtbs/sun71-a20-cubieboard2.dtbto compile the source back to blob at the expected place. Reboot, and
/dev/i2c-1 should run at 400 kHz.
Before you ask: No, I do not believe there is a way to change it programmatically. If you consider the typical use cases of single-board computers (SBCs), it is typically safer to choose the I2C bus clocks only once during boot, preferably as early as possible, and not trust individual applications or kernel I2C-using code to do it correctly. It is easy enough to modify the device tree blob instead.