1
votes

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();
}
1
Ryan, are you sure that your code in getAccel() is properly piecing together the 16-bit data in the proper endianness? (Perhaps you should do (data[1] | data[0] << 8) >> 4 ?)phonetagger
I think that I have it right, I basically adapted the getAccel() code from the corresponding SparkFun library function. Printing the two sequential Z bytes (data[4] and data[5]) gives me something like 96 (01100000) and 2 (00000010), combining them and shifting 4 bits like in the function gives me 38 (00000010 01100000 -> 0000000000100110).Ryan Clingman
Remember that with the accelerometer stationary and parallel or orthogonal to Earth’s horizon, one of the axes will read 1.0G and the others will read 0.0G. Try printing 6 sample sets of the raw (un-shifted) data, in two orientations: once with the PCB flat on your desk, and again with it flipped 180°. One of the axes’ data should be of the same magnitude but of opposite sign (with perhaps 2048 being “zero”). You might play with the data you get for these two sample sets until you find how to correctly handle it.phonetagger

1 Answers

2
votes

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).