LIS3DH Accelerometer SPI

I have to make this sensor (LIS3DH) work on a ClearCore controller and I couldn’t use the Adafruit libraries so I went through the Adafruit libraries to understand them and write this. This does not need any other library except SPI. It has been tested on an Arduino Mega

I had to go through many many pages online and through the Adafruit code for a week to make this work. Hopefully, this helps others.

The writeToRegister and mask code seems to be a bit buggy but it works for what I need so I won’t be spending time on that.

#include <SPI.h>

//#define DEBUG_SERIAL Serial
//#define DEBUG_SERIAL_X_RESULT Serial
//#define DEBUG_SERIAL_AVERAGE Serial

// Used for hardware
#define LIS3DH_CS 53

//Registers to use
#define WHO_AM_I_WRITE 0xF
#define EMPTY_STRING 0xFF
#define CTRL_REG1 0x20
#define CTRL_REG4 0x23

//X-axis acceleration data. The value is expressed in two’s complement.
const uint8_t OUT_X_L = 0x28;
const uint8_t OUT_X_H = 0x29;
const uint8_t OUT_Y_L = 0x2A;
const uint8_t OUT_Y_H = 0x2B;
const uint8_t OUT_Z_L = 0x2C;
const uint8_t OUT_Z_H = 0x2D;

//Data
#define RANGE_2G 0b00
#define RANGE_2G_HIGH_REZ 0b001
#define RANGE_4G 0b01
#define RANGE_8G 0b10
#define RANGE_16G 0b11
#define RANGE_16G_HIGH_REZ 0b111
#define ENABLE_X_Y_Z 0x07
#define DATARATE_1_HZ 0b0001
#define DATARATE_100_HZ 0b0101
#define DATARATE_400_HZ 0b0111
#define ENABLE_HIGH_REZ 0b1


//Config
SPISettings *_spiSetting = new SPISettings(500000, MSBFIRST, SPI_MODE0);
boolean foundLIS3DH = false;
const int NORMALIZE_MIN_SCALE = -25;
const int NORMALIZE_MAX_SCALE = 25;
//this value will need to be changed if the range is changed from RANGE_2G to something else
const int NORMALIZE_MIN_VALUE = -16000;
const int NORMALIZE_MAX_VALUE = 16000;


void setup(void) {
  Serial.begin(9600);
  while (!Serial) delay(10);     // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("LIS3DH test!");

  setupSPI();
  setupLIS3DH();
}

void setupSPI() {
  //Initializes SPI bus and sets CS pin high
  pinMode(LIS3DH_CS, OUTPUT);
  digitalWrite(LIS3DH_CS, HIGH);
  //it is important to call begin
  SPI.begin();
}

void setupLIS3DH() {
  uint8_t result = readRegister(WHO_AM_I_WRITE);
  if (result == 0) {
    foundLIS3DH = false;
    Serial.println("LIS3DH not found");
  } else {
    foundLIS3DH = true;
    Serial.println("LIS3DH found");


    enableXYZAxis();
    changeRange();
    changeDataRate();
    displayDataRate();
  }
}

void changeRange() {
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****changeRange()******");
#endif

  writeToRegister(CTRL_REG4, RANGE_2G_HIGH_REZ, 3);
  //writeToRegister(CTRL_REG4, RANGE_16G, 4);
  readRegister(CTRL_REG4);

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****changeRange() end***");
#endif
}

void enableHighResolution() {
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****enableHighResolution()******");
#endif

  writeToRegister(CTRL_REG4, ENABLE_HIGH_REZ, 3);
  readRegister(CTRL_REG4);

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****enableHighResolution() end***");
#endif
}


int readAxisNormalized(uint8_t axisAdressLow, uint8_t axisAddressHigh) {
  int newValue = readAxis(axisAdressLow, axisAddressHigh);
  int normalizedValue = map(newValue, NORMALIZE_MIN_VALUE, NORMALIZE_MAX_VALUE, NORMALIZE_MIN_SCALE, NORMALIZE_MAX_SCALE);

  return normalizedValue;
}

int16_t readAxis(uint8_t axisAdressLow, uint8_t axisAddressHigh) {
  uint8_t resultLow = readRegister(axisAdressLow);
  uint8_t resultHigh = readRegister(axisAddressHigh);

#ifdef DEBUG_SERIAL_X_RESULT
  DEBUG_SERIAL_X_RESULT.print("resultLow = readRegister(axisAdressLow); ");
  DEBUG_SERIAL_X_RESULT.println(resultLow);
  DEBUG_SERIAL_X_RESULT.println(resultLow, BIN);
  DEBUG_SERIAL_X_RESULT.print("resultHigh = readRegister(axisAddressHigh); ");
  DEBUG_SERIAL_X_RESULT.println(resultHigh);
  DEBUG_SERIAL_X_RESULT.println(resultHigh, BIN);
#endif

  //combining the values as is done in void Adafruit_LIS3DH::read(void)
  int16_t resultCombined = resultLow;
  resultCombined |= ((uint16_t)resultHigh) << 8;

#ifdef DEBUG_SERIAL_X_RESULT
  DEBUG_SERIAL_X_RESULT.print("resultCombined ");
  DEBUG_SERIAL_X_RESULT.println(resultCombined);
#endif

  return resultCombined;
}

void enableXYZAxis() {
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****enableXYZAxis()******");
#endif

  writeToRegister(CTRL_REG1, ENABLE_X_Y_Z, 0);

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****enableXYZAxis() end***");
#endif
}

void changeDataRate() {
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****changeDataRate()******");
#endif

  writeToRegister(CTRL_REG1, DATARATE_100_HZ, 4);

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****changeDataRate() end***");
#endif
}

void displayDataRate() {
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****displayDataRate()******");
#endif
  uint8_t result = readRegister(CTRL_REG1);

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.println("*****displayDataRate() end***");
#endif
}

uint8_t readRegister(uint8_t readAddress) {
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.print("readRegister(0x");
  DEBUG_SERIAL.print(readAddress, HEX);
  DEBUG_SERIAL.println(")");
#endif

  //Example: The register CTRL_REG4 has a HEX value of 0x23.  23 = 00100011 in binary.
  //Adafruit did an OR with HEX 0x80.  It sets the first bit to 1.
  //This seems to be needed to make things work, but only on the read, not the write.
  uint8_t eightBitReadAddress = readAddress | 0x80;

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.print("eightBitReadAddress 0x");
  DEBUG_SERIAL.println(eightBitReadAddress, HEX);
#endif

  //write to the register and then read
  uint8_t result = 0;
  SPI.beginTransaction(*_spiSetting);
  {
    digitalWrite(LIS3DH_CS, LOW);
    //set the register we are interested in
    SPI.transfer(eightBitReadAddress);
    result = SPI.transfer(EMPTY_STRING);
    digitalWrite(LIS3DH_CS, HIGH);
  }
  SPI.endTransaction();

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.print("Result in binary from register ");
  DEBUG_SERIAL.println(result, BIN);
  DEBUG_SERIAL.print("Result in HEX from register ");
  DEBUG_SERIAL.println(result, HEX);
#endif


  return result;
}

void writeToRegister(uint8_t writeAddress, uint8_t valueToWrite, uint8_t shift) {
  //get the current value in the register
  uint8_t valueInRegister = readRegister(writeAddress);

#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.print("writeToRegister(0x");
  DEBUG_SERIAL.print(writeAddress, HEX);
  DEBUG_SERIAL.print(", 0x");
  DEBUG_SERIAL.print(valueToWrite, HEX);
  DEBUG_SERIAL.println(")");
#endif

  //from Adafruit bool Adafruit_BusIO_RegisterBits::write(uint32_t data)
  //populate the register with the new value, only modify the bits that need to be changed
  uint8_t mask = (1 << (valueToWrite)) - 1;
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.print("mask ");
  DEBUG_SERIAL.println(mask, BIN);
#endif

  valueInRegister &= mask;
#ifdef DEBUG_SERIAL
  DEBUG_SERIAL.print("valueInRegister &= mask ");
  DEBUG_SERIAL.println(valueInRegister, BIN);
#endif

  mask <<= shift;
  valueInRegister &= ~mask;          // remove the current data at that spot
  valueInRegister |= valueToWrite << shift; // and add in the new data
  //*****************************************************


  //write to the register and then read
  SPI.beginTransaction(*_spiSetting);
  {
    digitalWrite(LIS3DH_CS, LOW);
    //set the register we are interested in
    SPI.transfer(writeAddress);
    //write the new value to the register
    SPI.transfer(valueInRegister);
    digitalWrite(LIS3DH_CS, HIGH);
  }
  SPI.endTransaction();
}

void loop() {
  long resultX = readAxisNormalized(OUT_X_L, OUT_X_H);
  long resultY = readAxisNormalized(OUT_Y_L, OUT_Y_H);
  long resultZ = readAxisNormalized(OUT_Z_L, OUT_Z_H);
  Serial.print("X:"); Serial.print(resultX);  Serial.print("   Y:  "); Serial.print(resultY);  Serial.print("   Z:  "); Serial.println(resultZ);

  delay(100);
}

I couldn't use the Adafruit libraries

Might be worth to tell why.