Search code examples
c++arduinoaccelerometer

How to get usable accelerometer readings


I have an arduino based device (Teensy 3.2) and I am trying to read data from a H3LIS331DL accelerometer breakout board. I can write to the control registers on the accelerometer and read from the data registers, but I'm having trouble getting stable, useful data.

First, useful data. The data sheet for the acclelerometer chip just says for the data registers "X-axis acceleration data. The value is expressed as two’s complement." The SparkFun LIS331 library says "The data that comes out is 12-bit data, left justified, so the lower four bits of the data are always zero." If the data is 12 bits per axis I would guess that the output (properly conditioned) should be 0-4095 with zero Gs being at something like 2048, but this doesn't match what I'm getting. With the accelerometer sitting still at my desk right now the X and Y axes are giving readings at the high and low end (either 0-19 or 4063-4095) and the Z axis is giving readings from 9-80 (always below 2048 in any orientation).

Are my expectations for the data wrong, or am I doing something to not be getting the correct data?

Second, stable data. The low setting on this accelerometer is +/- 100G range. My current bench testing is at -1G to +1G (1% of the full range), so I would expect the output data to have proportionally small range, but the output range just from a constant position is already almost 2% of the full scale.

What can I do to get more stable output data?

#include <i2c_t3.h>

#define ACCEL_NEW_PIN    16

#define CTRL_REG1        0x20
#define CTRL_REG2        0x21
#define CTRL_REG3        0x22
#define CTRL_REG4        0x23
#define OUT_X_L          0x28
#define OUT_X_H          0x29
#define OUT_Y_L          0x2A
#define OUT_Y_H          0x2B
#define OUT_Z_L          0x2C
#define OUT_Z_H          0x2D

#define accelAddress     0x19

volatile bool accelNew = true;
int16_t x, y, z;

void setup() {
  delay(1000);
  Serial.begin(115200);
  Serial.println("hello, world!");
  accelSetup();
}

void loop(){
  if(accelNew){
    getAccel(x, y, z);
    Serial.println(x);
    Serial.println(y);
    Serial.println(z);
    accelNew = false;
  }
}

void accelSetup(){
  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 1800000, I2C_OP_MODE_IMM);
  writeAccel(CTRL_REG1, B00111111); //normal mode, 1000Hz, X, Y and Z enabled
  writeAccel(CTRL_REG2, B00110000); //high pass filter enabled
  writeAccel(CTRL_REG3, B00000010); //Data ready output pin enabled
  writeAccel(CTRL_REG4, B10000000); //Block data update enabled, 100G range
  pinMode(ACCEL_NEW_PIN,INPUT);
  attachInterrupt(digitalPinToInterrupt(ACCEL_NEW_PIN), accelISR, RISING);
}

void accelISR(){
  accelNew = true;
}

void getAccel(int16_t& xAccel, int16_t& yAccel, int16_t& zAccel){
  uint8_t data[6];
  readAccel(OUT_X_L, data, 6);
  xAccel = (data[0] | data[1] << 8) >> 4;
  yAccel = (data[2] | data[3] << 8) >> 4;
  zAccel = (data[4] | data[5] << 8) >> 4;
}

void readAccel(byte address, byte* data, uint8_t bytes)
{
  Wire.beginTransmission(accelAddress);
  Wire.write(address | 0x80);//
  Wire.sendTransmission();
  Wire.sendRequest(accelAddress, bytes);
  while(Wire.available()) *(data++) = Wire.readByte();
}

void writeAccel(byte address, byte data)
{
  Wire.beginTransmission(accelAddress);
  Wire.write(address);
  Wire.write(data);
  Wire.endTransmission();
}

Solution

  • I mostly found the answer to my question. The data register bytes do need to be combined the way I did and have a right shift, but it needs to be a signed right shift whereas my code was amounting to an unsigned right shift. The sign bit was being misinterpreted as being a value bit and was sending the resulting value to the top of the range whenever the number was supposed to be negative. Casting the combined unsigned bytes as a signed int before shifting solved that issue and made the output data make much more sense.

    xAccel = (int16_t)(data[0] | data[1] << 8) >> 4;

    The good news is that all 3 axes now spit out meaningful data with the same linear slope based on the orientation.

    The bad news is that each axis has a different offset from 0 at 0G, and each axis still has a variance/jitter of about +/- 18 from the mean value (which is higher than I would like).