Search code examples
arduinoaccelerometerspi

ADXL373 Accelerometer SPI Communication Issue


As a preface, I'm new to Arduino and electronic communication. I'm in the initial stages of an Arduino based accelerometer setup to record high acceleration spikes from impacts. I'm communicating with a single ADXL373 accelerometer to start via SPI.

I've been stuck for a while on the SPI code in that it only will read 0 from all 3 axis. The issue I expect remains with how I'm coding my reads/writes. Does anything standout as egregiously wrong in how I'm executing them?

ADXL373 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl373.pdf

// ADXL373 400g Accelerometer sensor connected to Arduino 2560 Mega Board

// V5: pin 3.3V
// GND: pin GND
// MOSI: pin 51
// MISO: pin 50
// SCLK: pin 52
// CS: pin 53

// the sensor communicates using SPI, so include the library:
#include <SPI.h>

const int CS = 53; //Chip Select (Arduino Mega Pin 53)

/////////////////////////////////////////////////////////////////////////////////
///////////////////////////// Register Addresses ////////////////////////////////

byte XDATA_H = 0x08; // (0b00001000) X Data Register, High 8 MSB 
byte XDATA_L = 0x09; // (0b00001001) X Data Register, Low 4 MSB 
byte YDATA_H = 0x0A; // (0b00001010) Y Data Register, High 8 MSB 
byte YDATA_L = 0x0B; // (0b00001011) Y Data Register, Low 4 MSB 
byte ZDATA_H = 0x0C; // (0b00001100) Z Data Register, High 8 MSB 
byte ZDATA_L = 0x0D; // (0b00001101) Z Data Register, Low 4 MSB 
byte OFFSET_X = 0x20; // X Data Offset Register, Lower 4 bits
byte OFFSET_Y = 0x21; // Y Data Offset Register, Lower 4 bits
byte OFFSET_Z = 0x22; // Z Data Offset Register, Lower 4 bits
byte TIME_CTRL = 0x3D; // (0b00111101) Timing Control Register -> Select ODR (0b10000000) for 5120 Hz
byte MEASR_CTRL = 0x3E; // (0b00111110) Measurement Control -> Bandwidth set (0b00000100) for 2560 Hz
byte POWER_CTRL = 0x3F; // (0b00111111) Power Control Register -> Op. Mode and HPF off (0b00000111)
const byte WRITE = 0b11111110; // Reads with a 1, high
const byte READ = 0b00000001; // Writes with a 0, low

////////////////////////////////////////////////////////////////////////////////

// Establish variables to identify x, y, and z axis accelerations
int x_axis = 1;
int y_axis = 2;
int z_axis = 3;

void setup() 
{
  SPI.begin(); // Initialize SPI
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST); // Data for the device is sent MSB first, RW is last bit
  Serial.begin(115200);   // Establish a serial connection to display data through terminal
  pinMode(CS, OUTPUT);   // Set CS Pin Direction
  digitalWrite(CS, LOW);
  writeRegister(MEASR_CTRL, 0b00000100); // Set Measurement Mode to 2560Hz bandwidth (0x04)
  writeRegister(POWER_CTRL, 0b00000111); // Set full bandwidth measurement mode, HPF disabled (0x07)
  writeRegister(TIME_CTRL, 0b10000000); // Set ODR to 5120 Hz
  digitalWrite(CS, HIGH);
  delay(1);
}

void loop()
{
  Serial.print(" x = "); 
  Serial.print(getValue(x_axis));
  Serial.print(" y = "); 
  Serial.print(getValue(y_axis));
  Serial.print(" z = "); 
  Serial.println(getValue(z_axis));
  delay(0.2);
}

int getValue(int axis)
{
  int AccelData = 0;
  int Offset = 0;
  int high, low; 
  if (axis == 1)
  {
    high = readRegister(XDATA_H);
    low = readRegister(XDATA_L);
  }
  else if (axis == 2)
  {
    high = readRegister(YDATA_H);
    low = readRegister(YDATA_L);
  }
  else if (axis == 3)
  {
    high = readRegister(ZDATA_H);
    low = readRegister(ZDATA_L);
  }
  AccelData = (high << 4) | (low >> 4); // Merge 8 bits from 'high' with upper 4 of 'low' 
  AccelData = (AccelData - Offset)*200; // (Reading-Offset)*ScaleFactor --> 200mg/LSB for ADXL373
  return AccelData;
}

byte readRegister(byte thisRegister)
{
  byte result = 0;   // predeclare result to return
  // ADXL373 expects the register address in the upper 7 bits of the transmitted byte
  // Shift the register bits left by 1 to apply READ bit:
  thisRegister = thisRegister << 1;
  byte dataToSend = thisRegister | READ; // Combine register address with READ command
  digitalWrite(CS,LOW);  //Set the Chip Select pin low to start an SPI packet
  result = SPI.transfer(dataToSend);  // Tell device to read register and save response
  digitalWrite(CS, HIGH);  //Set CS high to close communcation
  return result;
}

void writeRegister(byte thisRegister, byte thisValue)
{
 // ADXL373 expects the register address in the upper 7 bits of the transmitted byte
 // Shift the register bits left by 1 bit to apply WRITE bit:
 thisRegister = thisRegister << 1;
 byte dataWrite = thisRegister & WRITE; // Combine the register address and WRITE command
 byte dataToSend = ((dataWrite << 8) | thisValue);
 digitalWrite(CS,LOW); //Set CS pin low to signal SPI packet start
 SPI.transfer(dataToSend); //Transfer the register address, RW, and desired register value over SPI
 digitalWrite(CS,HIGH); //Set the Chip Select pin high to signal the end of an SPI packet.
}

Any guidance is greatly appreciated.


Solution

  • You need to use two SPI.transfer() statements, like in the example in the Arduino SPI Tutorial for a Barometric Pressure Sensor. See lines 160 - 164:

    158   // send the device the register you want to read:
    159 
    160   SPI.transfer(dataToSend);
    161 
    162   // send a value of 0 to read the first byte returned:
    163 
    164   result = SPI.transfer(0x00);
    

    The first statement tells the sensor which register you want to read. The second statement returns the data (the sensor in this example does not work exactly like your ADXL, so I don't think you have to send a 0x00 to start a read, but the point is the same).

    The description for SPI.transfer() says it performs a "simultaneous send and receive", but that can also be used as a send or receive.