3

I am using ADXL373Z accelerometer with Arduino Uno for a project.

When I run the code, I am getting incorrect values which are probably not even acceleration data. I am thinking it might be a wiring problem, I attached circuit diagram below. This is my first Arduino project, and I couldn't find any other applications with the sensor I am using so any help would be much appreciated. Thanks in advance.

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

Code ( for x-axis data only) :

// ADXL373 Accelerometer Test

#include <Wire.h>

/* -------- REGISTERS -------- */
#define ADDRESS_ADXL373 0x53 // sensor adress (assumes MISO pin high)
#define ADDRESS_POWER_CTL 0x3F // power control
#define ADDRESS_MEASURE 0x0E // measurement control
#define ADDRESS_TIMING 0x3D // output data rate and extarnal timing triggers
#define ADDRESS_XDATA_H 0x08 // x-axis acceleration data [11:4]
#define ADDRESS_XDATA_L 0x09 // x-axis acceleration data [3:0]
#define ADDRESS_YDATA_H 0x0A // y-axis acceleration data [11:4]
#define ADDRESS_YDATA_L 0x0B // y-axis acceleration data [3:0]
#define ADDRESS_ZDATA_H 0x0C // z-axis acceleration data [11:4]
#define ADDRESS_ZDATA_L 0x0D // z-axis acceleration data [3:0]
/* -------- REGISTERS -------- */

#define SCALE_FACTOR 0.2f // 200 mg per LSB scaling factor
#define GRAVITY_EARTH 9.80665f // gravity of earth in m/s^2

void setup() {
  Serial.begin(115200);
  while(!Serial);
  Serial.println("ADXL373 Accelerometer Test");
  Wire.begin();
  Wire.beginTransmission(ADDRESS_ADXL373);
  Wire.write(ADDRESS_POWER_CTL);
  Wire.write(0x03); // activate full bandwith measurement mode
  Wire.endTransmission(true);
  delay(50);
  Wire.beginTransmission(ADDRESS_ADXL373);
  Wire.write(ADDRESS_MEASURE);
  Wire.write(0x04); // set output signal bandwith to 2560 Hz
  Wire.endTransmission(true);
  delay(50);
  Wire.beginTransmission(ADDRESS_ADXL373);
  Wire.write(ADDRESS_TIMING);
  Wire.write(0x80); // set output data rate to 5120 Hz
  Wire.endTransmission(true);
  delay(50);
}

void loop() {
  byte xAxisH, xAxisL;
  Wire.beginTransmission(ADDRESS_ADXL373);
  Wire.write(ADDRESS_XDATA_H);
  Wire.endTransmission(false);
  Wire.requestFrom(ADDRESS_ADXL373, 1, true);
  xAxisH = Wire.read();
  Wire.beginTransmission(ADDRESS_ADXL373);
  Wire.write(ADDRESS_XDATA_L);
  Wire.endTransmission(false);
  Wire.requestFrom(ADDRESS_ADXL373, 1, true);
  xAxisL = Wire.read();
  int16_t xAxisFinal;
  xAxisFinal = (uint16_t) xAxisH << 8 | xAxisL; // bit shift and combine two components
  xAxisFinal = xAxisFinal >> 4; // 12-bit resolution
  float xAccel;
  xAccel = xAxisFinal * SCALE_FACTOR * GRAVITY_EARTH;
  Serial.print("Xa = "); Serial.println(xAccel);
  delay(200);
}

Circuit Diagram :

Circuit Diagram

Results from Serial Monitor (in m/s^2) :

Serial Monitor

1
  • 1
    xAxisFinal = xAxisH << 4 | xAxisL; I think you mean << 8.
    – timemage
    Jun 19 at 18:58

1 Answer 1

5

According to the datasheet, the 12 bits of the X acceleration are stored into the registers XDATA_H and XDATA_L as follows:

XDATA_H:   b₁₁b₁₀b₉ b₈ b₇ b₆ b₅ b₄
XDATA_L:   b₃ b₂ b₁ b₀ x  x  x  x

where x means “reserved”.

Now, there are two issues with this code:

xAxisFinal = xAxisH << 4 | xAxisL; // bit shift 12-bit output data

The first issue is that, by shifting xAxisH by four places, the bits b₇–b₄ end up in the same positions as the bits b₃–b₀ in xAxisL. Then, when those values are combined with |, the bit b₇ gets mixed with b₃, b₆ with b₂, etc. If you really want to shift xAxisH left by four places, you should also shift xAxisL right by four places.

The second issue is that the x data is a signed number represented as two's complement, and the sign bit (b₁₁) ends up in the wrong place. For example, if the measured value is −16 (XDATA_H:XDATA_L = 0xff:0x00), you end up reading 4980 (0x0ff0). In order to avoid this issue you could sign extend the 12-bit number to 16 bits.

I would suggest, though, a completely different approach. The number is left justified in the sensor registers, so just keep it like this:

xAxisFinal = (uint16_t) xAxisH << 8 | xAxisL;

The intermediate cast to uint16_t is here to avoid an undefined behavior, the final result is signed. If there is some risk that the reserved bits are non-zero (the datasheet is not very clear in this respect), you could

xAxisFinal &= 0xfff0;  // clear last 4 bits

Note that now the unit of the acceleration is 1/16 of the resolution of the sensor, and all values you get are multiples of 16. I don't expect this to be an issue. If you really want to use the sensor resolution as your unit, you could now shift xAxisFinal four places to the right, which would automatically perform the sign extension.


Edit: Addressing the edited code from the OP:

I modified the code according to your suggestions. But I am still getting incorrect values from serial monitor.

What I see in your post is very small values, around ±3 LSBs of the sensor. This could very well be sensor noise. But then, there is the occasional larger value. Interestingly, those larger values seem to be very close to ±16 LSBs, hinting at some kind of communication problem.

I guess the answer is in this warning from the sensor's datasheet:

Always read acceleration data using multibyte transfers to ensure that a concurrent and complete set of x, y, and z acceleration data is read.

Imagine the sensor is outputting only noise, and the noise fluctuates between −1 and +1 LSB. What you expect then is:

value  XDATA_H   XDATA_L
  −1   11111111  11110000
  +1   00000000  00010000

If you read XDATA_H and XDATA_L in two different transactions, you may well read 0b11111111 for XDATA_H (as the sensor is reading −1), then read 00010000 for XDATA_L (as it is getting +1). When you combine these two readings together, you get

value  XDATA_H   XDATA_L
 −15   11111111  00010000

I suggest you follow the recommendation from the datasheet and read both registers in one go:

Wire.beginTransmission(ADDRESS_ADXL373);
Wire.write(ADDRESS_XDATA_H);
Wire.endTransmission(false);
Wire.requestFrom(ADDRESS_ADXL373, 2, true);
xAxisH = Wire.read();
xAxisL = Wire.read();
3
  • Heh. Your answer came in about when my comment did.
    – timemage
    Jun 19 at 19:01
  • Thanks, I modified the code according to your suggestions. But I am still getting incorrect values from serial monitor. I edited the code and results in my post. If you have any other suggestions I would appreciate it.
    – Maxxie
    Jun 20 at 8:11
  • @Maxxie: See expanded answer. Jun 20 at 14:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.