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();
}
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).