gobot
gobot copied to clipboard
HMC5883L driver returns wrong values
According to the datasheet section "Data Output X Registers A and B" the values should be in 2s complement form +0xF800 ... 0x07FF (-2048 ... +2047). In case of an overflow, the value gets "-4096 (0xF000)".
In my first test case, the values of X,Y, Z jump in a high range. All values can exceed 2048 and don't lock to -4096 for a strong magnetic field.
A first investigation shows, that a missing byte-swap after reading the MSB, LSB data with "ReadWordData()" could be the root cause.
I will provide a fix after some further tests, maybe together with #805 .
Following a spontaneous idea, I investigate all calls of "ReadWordData()" and all implementations. Normally the function should return the bytes in the expected order, otherwise we would not need this function. Also it is surprising, that there is no related bug entry since the initial implementation of the HMC5883L driver with #781 in April 2021.
I found usage of "ReadWordData()" for the following devices:
- TSL2561 - format: stored order LSB-MSB, but hardware implementation seems to ensure always read MSB-LSB unsigned - no swap implemented
- INA3221 - format: MSB-LSB two's complement, swap implemented
- CCS811 - format: MSB-LSB unsigned, no swap implemented
- HMC5883L - format: MSB-LSB two's complement, no swap implemented
These implementations can be found:
- sysfs/i2c_device - use the smbusAccess directly without swap (LSB, MSB)
- firmata_i2c - read LSB,MSB no swap
- digispark_i2c - read LSB, MSB no swap
note: After reading the SMBus specification, I come to the conclusion, that the order is defined as LSB, MSB. All platforms, except firmata and digispark use this implementation.
So I assume that the following drivers will not work at the moment (independent of the used platform):
- CCS811 (not critical because only used for reading the bootloader and firmware version)
- HMC5883L
Note for TSL2561: Because the documentation is somewhat confusing about the read order (stored order is LSB, MSB), I found the arduino implementation from adafruit, which reads the values sequential (without set the address for the second value) and use the first value (little address) as LSB, this means do not swap. The same for the arduino library of sparkfun. So our driver should work fine.
I have tested the HMC5883L driver today with my digispark platform and the same problem happen. So I looked again to the digispark driver implementation, and I was wrong with my statement that the bytes become swapped, because it reads 2 bytes and the first becomes the low byte, the second the high byte. I have corrected my comments above.
To clarify, how a word read "normally" works, I installed my mcp2221 USB-to-UART/I2C serial converter and connected the HMC5883L to it. Than I initializes the device and read the data for X in different ways:
- i2cget -y 7 0x1e 0x03 --> 0xff
- i2cget -y 7 0x1e 0x04 --> 0xfd
- i2cget -y 7 0x1e 0x03 w --> 0xfdff
This means, that also the commonly used i2cget binary will return a word as LSB, MSB (little endian), which matches the implementation of all our platforms. So the HMC5883L driver (and maybe the CCS811) needs to be fixed (store values as big endian).
Looks like something must have gotten mixed up at some point. Thanks for finding this issue!
part of release v2.0.0