SPI Sensors Values Way Off

I have 3 sensors on a board I built (328p-au, 16MHz crystal), all 3 sensors by ST Microelectronics:
(1) L3GD20 Gyro (datasheet)
(1) H3LIS331DL Accelerometer (datasheet)
(1) LIS331DH Accelerometer (datasheet)

Since I am running the 328p at 16MHz/5V, I am using a SN74AHC244 to level-shift the MOSI, SCK, and CS from 5V down to 3.3V. I also have the MISO line running through the level-shifter (yes, not necessary...). I've attached a capture of the portion of the circuit in question (there are 2 other SPI devices not shown, I am setting those CS pins to High and not using them here).

  • All 3 devices report the proper device IDs when the "Who_Am_I" register (0x0F) is sent (0xD4 for the gyro, 0x32 for both accelerometers).

  • I can set the configuration registers, and reading back the registers afterwards reveals the newly-set values

  • The gyro also seems to return more-or-less reasonable values (smaller than +/- 1dps on all 3 axes when sitting on my desk at +/-250dps scaling), increasing in magnitude when I rotate it about any of the 3 axes.

  • But neither accelerometer is returning at all reasonable values:

  • The high-G accelerometer (H3LIS331DL, set to +/-100g scaling) is returning a couple of g magnitude (1-10 mostly) on X and Y axes, and a couple dozen g magnitude (24-40 g) on the Z axis.

  • The low-G accelerometer (LIS331DLH, set to +/-2g scaling) is returning roughly 1/10 of a g on the X and y axes (sometimes a little more but under 1 g, it might be slightly tilted, but not such as to give a 1/4-g reading on such a fine scale setting), but 16g on the z-axis!

I've printed out the untouched MSB and LSB to see if the problem was in my re-assembly of the MSB and LSB, but the individual bytes were definitely supporting the high g-values I am seeing.

Also, my disassembly/re-assembly of the micros() time seems to be wrong, as the value bounces all over the map.

Any thoughts as to what the problem might be? Code is below.

#include <SPI.h>

#define L3G4200_CS 8
#define H3LIS331_CS 6
#define LIS331_CS 7

#define BUFFERS_PIN 4

int L3G4200_range = 250;
int H3LIS331_range = 100;
int LIS331_range = 2;
float L3G4200_scaling;
float H3LIS331_scaling;
float LIS331_scaling;

byte dataArray[22];
unsigned long theTime;
float tempVar;

byte SPIRead8(byte ssPin, byte reg)
{
  // read a byte from register "reg" on SPI device on "ssPin"
  byte data;
  digitalWrite(ssPin, LOW);
  SPI.transfer(reg | 0x80); // register to read
  data = SPI.transfer(0xFF); // clock in data from device by clocking out a dummy data byte (ignored)
  digitalWrite (ssPin, HIGH);
  return data;
}

void SPIWrite8(byte ssPin, byte reg, byte data)
{
  // write a byte "data" to register "reg" on SPI device on "ssPin"
  digitalWrite(ssPin, LOW);
  SPI.transfer(reg); // register to write to
  SPI.transfer(data); // data to write to register
  digitalWrite (ssPin, HIGH);
}

void setup()
{
  Serial.begin(115200);
  
  /* ------------------- INITIALIZATION --------------------*/
  Serial.println("Initializing...");
  
  // Set CS pins for sensors to output and ensure they are all high
  pinMode(L3G4200_CS, OUTPUT);
  pinMode(H3LIS331_CS, OUTPUT);
  pinMode(LIS331_CS, OUTPUT);
  pinMode(BUFFERS_PIN, OUTPUT);
  digitalWrite(L3G4200_CS, HIGH);
  digitalWrite(H3LIS331_CS, HIGH);
  digitalWrite(LIS331_CS, HIGH);
  
  // Set CS pins for other SPI devices to output and set high to ensure no cross-talk
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  
  // Initialize SPI settings
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); // MSBFirst for these sensors
  SPI.setDataMode(SPI_MODE3); // Proper SPI mode for these sensors
  //SPI.setClockDivider(SPI_CLOCK_DIV16); // Set SPI clock speed
  
  digitalWrite(BUFFERS_PIN, LOW);  // enable the SN74AHC244 buffer
  
  Serial.print("Gyro Device ID on pin ");
  Serial.print(L3G4200_CS);
  Serial.print(" ");
  Serial.println(SPIRead8(L3G4200_CS, 0x0F), HEX);
  
  Serial.print("High-G Device ID on pin ");
  Serial.print(H3LIS331_CS);
  Serial.print(" ");
  Serial.println(SPIRead8(H3LIS331_CS, 0x0F), HEX);
  
  Serial.print("Low-G Device ID on pin ");
  Serial.print(LIS331_CS);
  Serial.print(" ");
  Serial.println(SPIRead8(LIS331_CS, 0x0F), HEX);
  
  switch(L3G4200_range)
  {
    case 250:
      SPIWrite8(L3G4200_CS, 0x23, 0x00); // set Gyro scaling to 250dps
      L3G4200_scaling = 0.00875;
      break;
    case 500:
      SPIWrite8(L3G4200_CS, 0x23, 0x10); // set Gyro scaling to 500dps
      L3G4200_scaling = 0.01750;
      break;
    case 2000:
      SPIWrite8(L3G4200_CS, 0x23, 0x20); // set Gyro scaling to 2000dps;
      L3G4200_scaling = 0.07000;
      break;
  }
  switch(H3LIS331_range)
  {
    case 100:
      SPIWrite8(H3LIS331_CS, 0x23, 0x00); // set Accel scaling to 100g
      H3LIS331_scaling = 0.049;
      break;
    case 200:
      SPIWrite8(H3LIS331_CS, 0x23, 0x10); // set Accel scaling to 200g
      H3LIS331_scaling = 0.098;
      break;
    case 400:
      SPIWrite8(H3LIS331_CS, 0x23, 0x30); // set Accel scaling to 400g
      H3LIS331_scaling = 0.195;
      break;
  }
  switch(LIS331_range)
  {
    case 2:
      SPIWrite8(LIS331_CS, 0x23, 0x00); // set Accel scaling to 2g
      LIS331_scaling = 0.001;
      break;
    case 4:
      SPIWrite8(LIS331_CS, 0x23, 0x10); // set Accel scaling to 4g
      LIS331_scaling = 0.002;
      break;
    case 8:
      SPIWrite8(LIS331_CS, 0x23, 0x30); // set Accel scaling to 8g
      LIS331_scaling = 0.0039;
      break;
  }
  
  SPIWrite8(H3LIS331_CS, 0x20, B00111111);  // set accelerometer to normal mode, 1000Hz ODR, enable all axes
  SPIWrite8(LIS331_CS, 0x20, B00111111);  // set accelerometer to normal mode, 1000Hz ODR, enable all axes
  SPIWrite8(L3G4200_CS, 0x20, B11001111);  // set gyro to 760Hz ODR, 30 CutOff,  enable all axes, normal mode, enable all axes
  /* -------------- END INITIALIZATION --------------*/
  
  Serial.print("L3G4200 Scaling: "); Serial.println(L3G4200_scaling,5);
  Serial.print("H3LIS331 Scaling: "); Serial.println(H3LIS331_scaling,5);
  Serial.print("LIS331 Scaling: "); Serial.println(LIS331_scaling,5);
}

void loop()
{
  theTime = micros();
  dataArray[0] = theTime;
  dataArray[1] = theTime >> 8;
  dataArray[2] = theTime >> 16;
  dataArray[3] = theTime >> 24;
  dataArray[4] = SPIRead8(L3G4200_CS, 0x28);  // gyro x LSB
  dataArray[5] = SPIRead8(L3G4200_CS, 0x29);  // gyro x MSB
  dataArray[6] = SPIRead8(L3G4200_CS, 0x2A);  // gyro y LSB
  dataArray[7] = SPIRead8(L3G4200_CS, 0x2B);  // gyro y MSB
  dataArray[8] = SPIRead8(L3G4200_CS, 0x2C);  // gyro z LSB
  dataArray[9] = SPIRead8(L3G4200_CS, 0x2D);  // gyro z MSB
  dataArray[10] = SPIRead8(H3LIS331_CS, 0x28);  // accel x LSB
  dataArray[11] = SPIRead8(H3LIS331_CS, 0x29);  // accel x MSB
  dataArray[12] = SPIRead8(H3LIS331_CS, 0x2A);  // accel y LSB
  dataArray[13] = SPIRead8(H3LIS331_CS, 0x2B);  // accel y MSB
  dataArray[14] = SPIRead8(H3LIS331_CS, 0x2C);  // accel z LSB
  dataArray[15] = SPIRead8(H3LIS331_CS, 0x2D);  // accel z MSB
  dataArray[16] = SPIRead8(LIS331_CS, 0x28);  // accel x LSB
  dataArray[17] = SPIRead8(LIS331_CS, 0x29);  // accel x MSB
  dataArray[18] = SPIRead8(LIS331_CS, 0x2A);  // accel y LSB
  dataArray[19] = SPIRead8(LIS331_CS, 0x2B);  // accel y MSB
  dataArray[20] = SPIRead8(LIS331_CS, 0x2C);  // accel z LSB
  dataArray[21] = SPIRead8(LIS331_CS, 0x2D);  // accel z MSB
  
  theTime = ((unsigned long)dataArray[0] << 24) + ((unsigned long)dataArray[1] << 16) + ((unsigned long)dataArray[2] << 8) + (unsigned long)dataArray[3];
  Serial.print("Time: "); Serial.println(theTime);
  tempVar = (((int)dataArray[5] << 8) + dataArray[4]) * L3G4200_scaling;
  Serial.print("  Gyro---X: "); Serial.print(tempVar);
  tempVar = (((int)dataArray[7] << 8) + dataArray[6]) * L3G4200_scaling;
  Serial.print(" Y: "); Serial.print(tempVar);
  tempVar = (((int)dataArray[9] << 8) + dataArray[8]) * L3G4200_scaling;
  Serial.print(" Z: "); Serial.println(tempVar);
  tempVar = (((int)dataArray[11] << 8) + dataArray[10]) * H3LIS331_scaling;
  Serial.print("High-Accel---X: "); Serial.print(tempVar);
  tempVar = (((int)dataArray[13] << 8) + dataArray[12]) * H3LIS331_scaling;
  Serial.print(" Y: "); Serial.print(tempVar);
  tempVar = (((int)dataArray[15] << 8) + dataArray[14]) * H3LIS331_scaling;
  Serial.print(" Z: "); Serial.println(tempVar);
  tempVar = (((int)dataArray[17] << 8) + dataArray[16]) * LIS331_scaling;
  Serial.print("  Low-Accel---X: "); Serial.print(tempVar);
  tempVar = (((int)dataArray[19] << 8) + dataArray[18]) * LIS331_scaling;
  Serial.print(" Y: "); Serial.print(tempVar);
  tempVar = (((int)dataArray[21] << 8) + dataArray[20]) * LIS331_scaling;
  Serial.print(" Z: "); Serial.println(tempVar);
  Serial.println(" ");
  delay(1000);
}

 theTime = ((unsigned long)dataArray[0] << 24) + ((unsigned long)dataArray[1] << 16) + ((unsigned long)dataArray[2] << 8) + (unsigned long)dataArray[3];

You are re-assembling the time backwards. Element 0 is the LSB. Try:
theTime = ((unsigned long)dataArray[3] << 24) + ((unsigned long)dataArray[2] << 16) + ((unsigned long)dataArray[1] << 8) + (unsigned long)dataArray[0];

johnwasser:
You are re-assembling the time backwards. Element 0 is the LSB.

Woops, yes I definitely am assembling backwards, I must have mixed some old code segments together.

Anyone have an idea on the mis-behaving sensors?

I know this is almost a year old now, but I recently finally figured this out after squashing other bugs and developing the code, PCB, etc, under the assumption that it was either bad sensors or bad math.

It turned out that both accelerometers (and it appears many other ST accelerometers), while outputting 16-bit data, are outputting in 12-bit representation (this part is the key). The actual data is in the high 12 bits, and so the last 4 bits of the low byte are always 0; thus the assembled value is scaled by 16. Assembling the two bytes and then bit-shifting the result 4 bits to the right fixes this. I am now getting ~1g on the vertical axis of the low-g accelerometer, and ~3g on the high-g, which I'll just chalk up to an offset induced by the PCB reflow process.

The below code works on both sensors (element 15 is a high-byte, 14 is a low-byte, the array elements are of type byte).

float sampleData;
sampleData = ((((int)dataArray[15] << 8) + dataArray[14])) >> 4;
sampleData = sampleData * LOWG_scaling;
dataFile.print(sampleData);