1

I'm currently getting inconsistent data from the accelerometer of my Adafruit LSM9DS1. To be more precise I either get a value close to the correct gravity (eg. 9.496515) or some reading like it was in space (eg. -0.023928225).

This seems to happen on all three the axes (I tried rotating the LSM9DS1).

I get the same behavior with FIFO enabled or not.

This is a set of 32 consecutive samples that I read from the FIFO memory:

result = {float[32][]@5092} 
 0 = {float[3]@5100} 
  0 = -0.41754755
  1 = 0.19501504
  2 = 9.519247
 1 = {float[3]@5112} 
  0 = -0.421735
  1 = 0.19980069
  2 = 9.51805
 2 = {float[3]@5120} 
  0 = -0.4229314
  1 = 0.20757735
  2 = 9.510274
 3 = {float[3]@5128} 
  0 = -0.4127619
  1 = 0.2207379
  2 = 9.502497
 4 = {float[3]@5139} 
  0 = -0.39302114
  1 = -0.06580262
  2 = -0.0017946169
 5 = {float[3]@5140} 
  0 = -0.06939186
  1 = -0.045463633
  2 = -0.020937197
 6 = {float[3]@5141} 
  0 = -0.05742774
  1 = -0.03469593
  2 = -0.0317049
 7 = {float[3]@5142} 
  0 = -0.053838514
  1 = -0.039481573
  2 = -0.041874394
 8 = {float[3]@5143} 
  0 = -0.054436717
  1 = -0.052642096
  2 = -0.043669015
 9 = {float[3]@5144} 
  0 = -0.059820566
  1 = -0.068793654
  2 = -0.03469593
 10 = {float[3]@5145} 
  0 = -0.06819545
  1 = 0.22731815
  2 = -0.019142581
 11 = {float[3]@5146} 
  0 = -0.38644087
  1 = 0.2225325
  2 = -0.00418744
 12 = {float[3]@5147} 
  0 = -0.40079778
  1 = 0.21535406
  2 = 9.505488
 13 = {float[3]@5148} 
  0 = -0.40977085
  1 = 0.21355942
  2 = 9.525827
 14 = {float[3]@5149} 
  0 = -0.40917268
  1 = 0.21296123
  2 = 9.540783
 15 = {float[3]@5150} 
  0 = -0.40558344
  1 = 0.2207379
  2 = 9.5455675
 16 = {float[3]@5151} 
  0 = -0.39960137
  1 = 0.22671993
  2 = 9.544371
 17 = {float[3]@5152} 
  0 = -0.39302114
  1 = 0.22791636
  2 = 9.534203
 18 = {float[3]@5153} 
  0 = -0.3906283
  1 = 0.21535406
  2 = 9.517452
 19 = {float[3]@5154} 
  0 = -0.38584265
  1 = 0.19441684
  2 = 9.496515
 20 = {float[3]@5155} 
  0 = -0.384048
  1 = 0.17347965
  2 = -0.023928225
 21 = {float[3]@5156} 
  0 = -0.38883367
  1 = 0.15553348
  2 = -0.042472597
 22 = {float[3]@5157} 
  0 = -0.39541394
  1 = -0.014356935
  2 = -0.05802595
 23 = {float[3]@5158} 
  0 = -0.40259242
  1 = -0.026919257
  2 = -0.06580262
 24 = {float[3]@5159} 
  0 = -0.40977085
  1 = -0.03589234
  2 = -0.071186475
 25 = {float[3]@5160} 
  0 = -0.4151547
  1 = -0.040677987
  2 = -0.062213387
 26 = {float[3]@5161} 
  0 = -0.421735
  1 = -0.03888337
  2 = -0.04965107
 27 = {float[3]@5162} 
  0 = -0.42831525
  1 = -0.040677987
  2 = -0.032303106
 28 = {float[3]@5163} 
  0 = -0.4372883
  1 = -0.04426722
  2 = -0.014356935
 29 = {float[3]@5164} 
  0 = -0.445065
  1 = -0.04785645
  2 = 9.496515
 30 = {float[3]@5165} 
  0 = -0.44865423
  1 = -0.04666004
  2 = 9.503693
 31 = {float[3]@5166} 
  0 = -0.44865423
  1 = -0.04426722
  2 = 9.506086

As you can see 15 times the Z axis shows the correct value and 17 shows -0.

This is how I'm initializing the sensors:

        // soft reset & reboot accel/gyro
        writeRegByte(SensorType.XG, REGISTER_CTRL_REG8, (byte) 0b00000101);
        // soft reset & reboot magnetometer
        writeRegByte(SensorType.MAG, REGISTER_CTRL_REG2_M, (byte) 0b00001100);

        SystemClock.sleep(10);

        byte idXg = readRegByte(SensorType.XG, REGISTER_WHO_AM_I_XG);
        byte idMag = readRegByte(SensorType.MAG, REGISTER_WHO_AM_I_M);
        if (idXg != XG_ID || idMag != MAG_ID) {
            throw new IllegalStateException("Could not find LSM9DS1, check wiring!");
        }

        setFifoMemoryEnabled(false);

        // enable gyro continuous
        writeRegByte(SensorType.XG, REGISTER_CTRL_REG1_G, (byte) 0b11000000); // on XYZ

        // Enable the accelerometer continuous
        writeRegByte(SensorType.XG, REGISTER_CTRL_REG5_XL,
                (byte) (CTRL_REG5_XL_ZEN_XL | CTRL_REG5_XL_YEN_XL | CTRL_REG5_XL_XEN_XL)); // enable X Y and Z axis
        writeRegByte(SensorType.XG, REGISTER_CTRL_REG6_XL, (byte) 0b11000000); // 1 KHz out data rate, BW set by ODR, 408Hz anti-aliasing
        writeRegByte(SensorType.XG, REGISTER_CTRL_REG7_XL, (byte) 0b10000000); // HR - High resolution mode

        // enable mag continuous
        writeRegByte(SensorType.MAG, REGISTER_CTRL_REG3_M, (byte) 0x00); // continuous mode

And this is how I'm reading the accelerometer data:

        byte[] buffer = new byte[6];
        int[] result = new int[3];
        readRegBuffer(SensorType.XG, REGISTER_OUT_X_L_XL, buffer, buffer.length);
        result[0] = (buffer[1] << 8) | buffer[0]; // Store x-axis values
        result[1] = (buffer[3] << 8) | buffer[2]; // Store y-axis values
        result[2] = (buffer[5] << 8) | buffer[4]; // Store z-axis values
        return result;

I'm then converting the raw values with this formula (but this is a constant change so it should not matter):

rawAccelerometerData[i] * mAccelMgLsb / 1000f * mGravity;

I've also tried to disable the gyro and the magnetometer and left only the Z axis enabled and than tried to fetch the data using different output data rate but the result is the same.

The full code of my driver is available here.

I currently have only one LSM9DS1 so I have no clue if this is a defective product or an expected behavior and if this can be fixed with a different configuration.

  • Reading through the data sheet (i.e. rtfm ing) suggests that if the accelerometer and the gyroscope are both enabled then the data readout can contain both gyroscope data followed by/alternating with accelerometer data. Now I haven't looked into you code in too much detail but have you configure the device to fit with your code - it sounds possible that you have not... – SlySven Dec 21 '17 at 5:46
  • Hi @SlySven thanks for the hint, I tried to disable the gyro (writing 0 to CTRL_REG1_G), but nothing changes, I still get ½ time 0 and ½ time 1G on Z axis of the accelerometer. Anyway the gyro and accelerometer use 2 different registers to read the data (OUT_X_L_G and OUT_X_L_XL) and I can correctly see that when the gyro is disabled I get only zeros from the G register. I was not able to find the place in the datasheet where is stated that I can get mixed data when you have both enabled, could you please point me to the right page? – Roberto Leinardi Dec 21 '17 at 12:36
  • 1
    Check Section "3 LSM9DS1 functionality" on page 19 of 72 about enabling/disabling the Gyroscope (Angular acceleration!) and Section "3.3 Accelerometer and gyroscope multiple reads (burst)" on page 21... – SlySven Dec 21 '17 at 15:29
  • I went through those sections but I haven't found anything about getting unreliable data when both gyro and accel are enabled. This is what says about reading when both are enabled: "When both accelerometer and gyroscope sensors are activated at the same ODR, starting from OUT_X_G (18h - 19h) multiple reads can be performed. Once OUT_Z_XL (2Ch - 2Dh) is read, the system automatically restarts from OUT_X_G (18h - 19h)". But, anyway, as I said I tried also with only the Accelerometer enabled and the problem is still present. – Roberto Leinardi Dec 21 '17 at 20:34
2

The problem is how the accelerometer data is read, to be precise is in these lines:

result[0] = (buffer[1] << 8) | buffer[0]; // Store x-axis values
result[1] = (buffer[3] << 8) | buffer[2]; // Store y-axis values
result[2] = (buffer[5] << 8) | buffer[4]; // Store z-axis values

What I'm getting from the sensor is a 16bit in two’s complement spitted in 2 bytes, for example buffer[4] and buffer[5] where 4 is the low byte and 5 is the high.

In Java the & and | are defined to operate only on int and long values, so what happens is that the byte is always promoted to signed int:

buffer[5] = 00111100 
buffer[4] = 11110110
(int)buffer[4] = 11111111111111111111111111110110 = FF FF FF F6
(buffer[5] << 8) | buffer[4] = FF FF FF F6

With the original code both bytes are handled has two’s complement when actually only the high byte is holding the sign.

Long story short, you have to keep the sign for the high byte (cast to int) and threat the low as unsigned (autocast to int using the & and keeping only the original byte with 0xFF):

result[0] = (((int) buffer[1]) << 8) | (buffer[0] & 0xFF); // Store x-axis values
result[1] = (((int) buffer[3]) << 8) | (buffer[2] & 0xFF); // Store y-axis values
result[2] = (((int) buffer[5]) << 8) | (buffer[4] & 0xFF); // Store z-axis values
  • Would you benefit from tweaking the BLE (Big/Little Endian) bit in the CTRL_REG8 (22h) to swap to Big Endian mode so that the 16 bits of data are in the right order to be mapped into a int16_t ...? – SlySven Dec 22 '17 at 4:22
  • This is actually an interesting point: I tried it out and if I enable it I can actually use the readRegWord() to read 2 bytes and map it into a short (16 bit int in Java) so, no manual bit shifting needed. the problem is that than I had to split the single read that I do of the 6 bytes in 3 separate reads, one per each axis and I'm not sure if this will be the worth since I fear it would be slightly less performant and since the current code is now working correctly... But it would make the code for sure more readable. – Roberto Leinardi Dec 22 '17 at 11:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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