i2c总线上LSM303DLHC传感器的磁力计输出不稳定。 问题写作gobot飞行员

I use the Adafruit LSM303DLHC sensor. It is made of 2 sensors, an accelerometer and a magnetometer. I am currently writing a driver for this sensor to work with the gobot.io package through i2c interface on a Raspberry Pi 2.

Problem: The accelerometer part works. The magnetometer sensor part doesn't. I can read the magnetic field registers but I get nonsensical values. Those values are updating between each reading cycle, but they are not varying much and doesn't make sense.

Devices used:

  • LSM303DLHC sensor - Datasheet
  • Raspberry Pi 2
  • Arduino Uno

Details about reading the magnetometer output:

LSM303DLHC output 6 bytes representing 3 magnetic field values along the 3 axis. Each value is made of 2 bytes (16bits) for each axis. The output order is the following:

  • X high byte
  • X low byte
  • Z high byte
  • Z low byte
  • Y high byte
  • Y low byte

In order to set up the sensor we write in the following registers in that order:

  • Reset the magnetometer gain: write 0x00 in CRB_REG_M register(0x01)
  • Set the magnetometer gain: Write 0x60 (+/- 2.5 gauss) to CRB_REG_M register(0x01)
  • Set the output data rate: Write 0x05 (30 Hz) in CRA_REG_M register(0x00)
  • Enable continuous mode: Write 0x00 in MR_REG_M register

After setting up the sensor we can read the output. In order to read it, we write to the 1st of the 6 output registers. Then we read the 6 registers output in one sweep, putting the 6 bytes in a buffer.

Test already done

  1. Using Adafruit's library and Arduino platform: OK - output is normal
  2. Using Raspberry Pi 2 + python example: OK - output is normal
  3. Using Raspberry Pi 2 + gobot.io + lsm303DLHC driver: Error
  4. Using io.ReadFull() instead of io.Read() as suggested in the comment: Error

The first 2 tests (1 & 2) tell me that the sensor work. It's not broken. The i2c speed is not the culprit here because the python program (2) works too.

I suspect something is wrong in my code when it comes to properly form int16 values from the bytes. My driver code part to read the sensor output and form the resulting values

This code live in ~/go/src/gobot.io/x/gobot/drivers/i2c/lsm303DLHC.go (a.k.a the driver)

func (d *LSM303Driver) MagneticField() (x, z, y float32, err error) {
    // Write to the first output register to start the reading procedure
    if _, err = d.Magnetometer.connection.Write([]byte{lsm303RegisterMagOutXLSB}); err != nil {
        return 0, 0, 0, err

    // create a buffer to put the output bytes
    measurements := make([]byte, 6)
    // read the 6 output bytes
    if _, err = d.Magnetometer.connection.Read(measurements); err != nil {
        return 0, 0, 0, err

    var rawXh uint8
    var rawXl uint8
    var rawZh uint8
    var rawZl uint8
    var rawYh uint8
    var rawYl uint8

    buf := bytes.NewBuffer(measurements)

    binary.Read(buf, binary.BigEndian, &rawXh)
    binary.Read(buf, binary.BigEndian, &rawXl)
    binary.Read(buf, binary.BigEndian, &rawZh)
    binary.Read(buf, binary.BigEndian, &rawZl)
    binary.Read(buf, binary.BigEndian, &rawYh)
    binary.Read(buf, binary.BigEndian, &rawYl)

    rawX := int16((uint16(rawXh) << 8) | uint16(rawXl))
    rawZ := int16((uint16(rawZh) << 8) | uint16(rawZl))
    rawY := int16((uint16(rawYh) << 8) | uint16(rawYl))

    // Gain is set to +/- 2.5 LSB/Gauss (Least Significant Byte)
    // Datasheet page 38
    // Unit convertion: gaussToMicroTesla = 100
    gainXY, gainZ := d.getGainXYZ()

    x = float32(rawX) / float32(gainXY) * float32(gaussToMicroTesla)
    z = float32(rawZ) / float32(gainZ) * float32(gaussToMicroTesla)
    y = float32(rawY) / float32(gainXY) * float32(gaussToMicroTesla)

    fmt.Printf("DEBUG rawX %016b ---> %v \t\t|\t X %v
", rawX, rawX, x)
    fmt.Printf("DEBUG rawZ %016b ---> %v \t\t|\t Z %v
", rawZ, rawZ, z)
    fmt.Printf("DEBUG rawY %016b ---> %v \t\t|\t Y %v

", rawY, rawY, y)

    return x, z, y, nil

Here is the output of my little program which use this function:

DEBUG rawX 0000001100101011 ---> 811        |    X 121.04478
DEBUG rawZ 0000001011110111 ---> 759        |    Z 126.5
DEBUG rawY 0000001100110000 ---> 816        |    Y 121.79104

DEBUG rawX 0000001100101011 ---> 811        |    X 121.04478
DEBUG rawZ 0000001011110111 ---> 759        |    Z 126.5
DEBUG rawY 0000001100110000 ---> 816        |    Y 121.79104

DEBUG rawX 0000001100100111 ---> 807        |    X 120.44777
DEBUG rawZ 0000001011110110 ---> 758        |    Z 126.33333
DEBUG rawY 0000001100101100 ---> 812        |    Y 121.19403

You can see on each line the rawX(Y-Z) binary and normal representation then the definitive value in micro-tesla. In all cases those values are way off. They do not vary much even when I turn the device in every direction.

I looked closely to Adafruits C++ library for arduino and I don't see any major difference. Here is the Adafruit code for reading the magnetometer output:

 void Adafruit_LSM303_Mag_Unified::read()
   // Read the magnetometer


   Wire.requestFrom((byte)LSM303_ADDRESS_MAG, (byte)6);

  // Wait around until enough data is available
   while (Wire.available() < 6);

  // Note high before low (different than accel)
    uint8_t xhi = Wire.receive();
    uint8_t xlo = Wire.receive();
    uint8_t zhi = Wire.receive();
    uint8_t zlo = Wire.receive();
    uint8_t yhi = Wire.receive();
    uint8_t ylo = Wire.receive();

  // Shift values to create properly formed integer (low byte first)
  raw.x = (int16_t)(xlo | ((int16_t)xhi << 8));
  raw.y = (int16_t)(ylo | ((int16_t)yhi << 8));
  raw.z = (int16_t)(zlo | ((int16_t)zhi << 8));

Am I missing something huge? (I hope so...)

Honestly I spend a crazy amount of time on this problem and I am nowhere. I did learn a lot of interesting things about the linux kernel and i2c protocol, ioctl and more... but I am still not able to make the magnetometer work in golang with gobot.io, even if the accelerometer works...

I thanks in advance those who will spend the time to read me.

dougu7546 是lsm303RegisterMagOutXLSB=0x03。我也尝试使用0x03|0x80地址。没变化。我尝试已经交换了rawXh和rawXl,但也没有太大的成功。
接近 2 年之前 回复
duan02468 您确定lsm303RegisterMagOutXLSB与LSM303_REGISTER_MAG_OUT_X_H_M(0x03)相同吗?您是否尝试过交换高字节和低字节?C代码中的#if对我来说似乎很奇怪。也许那些应该交换的地方?
接近 2 年之前 回复
dsnrixf6765 C代码Wire.beginTransmission((byte)LSM303_ADDRESS_MAG)在gobot.io程序包中具有等效功能。在我的代码中不可见,但是所有启动i2c传输的步骤均由gobot.io接口管理,该接口直接位于ioctl()i2c/SMBUS的电话
接近 2 年之前 回复
dongxia527680 开始读取过程的初始步骤是,您只写一个字节值(lsm303RegisterMagOutXLSB),而假定等效的C代码也调用Wire.beginTransmission((byte)LSM303_ADDRESS_MAG);,不确定这样做是什么,但是C代码可以不仅仅是发送一个字节。您代码的其余部分(尽管远非最佳)应该按照您的意思执行,因此错误可能出在初始步骤中,或是代码中未发布的其他地方。
接近 2 年之前 回复
doushua7737 您正在读取单个字节,如果是1个字节(仅具有多字节值),则没有字节顺序的含义。
接近 2 年之前 回复
doupian6118 我使用io.ReadFull()没有成功。我什至检查io.ReadFull()返回的字节数。它不会改变结果。我仍然有相同的行为。
接近 2 年之前 回复
doucan8521 可能引起问题的一件事是通过单个Read()调用读取输出:不能保证读取6个字节(即使没有返回错误,尤其是对于慢速设备)。而是像这样使用io.ReadFull():_,err=io.ReadFull(d.Magnetometer.connection,measurement)。请对此进行测试,并告诉它是否可以解决您的问题。
接近 2 年之前 回复
Csdn user default icon