Writing to MMA8452 Q via I2C

Hello All,

Does anyone have any experience writing to the MMA8452Q?

I am trying to configure the "Transient" settings of the MMA8452Q and I cannot figure out what I am doing wrong. This is the first time that I have tried writing to registers and addresses via I2C, and I am pretty new to I2C as well.

I believe the syntax, addresses and values themselves are correct, but the values that are reported to the debug are not decoupled. Said another way by enabling the "Transient" setting the MMA8452Q should report back only relative acceleration and ignore the acceleration due to gravity, but this is not the case. Here is the bit of code that I am concerned with.

#include <Wire.h>

void setup()
{
Wire.begin();
Wire.beginTransmission(56);  // Begins transmission to 0x38 address of MMA8452Q
  
Wire.write(14);  // 0x0E HPF_OUT register of MMA8452Q
Wire.write(16);  // Sends 00010000 to MMA8452Q 0x0E register

Wire.write(29);  // 0x0D Transient_CFG register of MMA8452Q
Wire.write(14);  // Sends 00001110 to MMA8452Q 0x0D register

Wire.write(32);  // 0x20 Transient_Count register of MMA8452Q  
Wire.write(10);  // Sends 00001010 to MMA8452Q 0x20 register
  
Wire.endTransmission();
}

This is the actual product link...

Thanks in advance,

  • Z

Run a I2C Scanner to find its I2C address. Can it find the chip at 0x38 ? Often the hexadecimal value 0x38 is used instead of the decimal value 56.

When you read the datasheet, you will not find that it is possible to write to three different registers in a single transaction.
You must use three seperate register writes. The first one is this:

Wire.beginTransmission(0x38);  // address of MMA8452Q
Wire.write(14);  // 0x0E HPF_OUT register of MMA8452Q
Wire.write(16);  // Sends 00010000 to MMA8452Q 0x0E register
Wire.endTransmission();

I did not fully read the datasheet. Some chips need a special sequence for certain settings.

Thanks Koepel,

I tried hex in place of all the decimal values, and tried to write to the respective registers one at a time before I posted, with no luck obviously.

Good idea on the scanner. I'll try this tonight.

Thanks again,

Z

The scanner identified 0x1D, but still the output is unchanged. Any ideas on what else I am missing here?

ZeusDashing:
The scanner identified 0x1D, but still the output is unchanged. Any ideas on what else I am missing here?

0x1D = 2910

Show your sketch.

As requested...

#include <Wire.h>  // Must include Wire library for I2C
#include <SparkFun_MMA8452Q.h>  // Includes the SFE_MMA8452Q library

MMA8452Q accel;

void setup()
{
  Serial.begin(9600);

  Wire.begin();  // Join i2c bus (address optional for master)

  Wire.beginTransmission((byte)0x1D);     // Begins transmission to 0x38 address of MMA8452Q

  Wire.write((byte)0x0E);  // Sends 00000000 (14) to MMA8452Q 0x0D register (0x0D = TRANSIENT_CFG register)
  Wire.write((byte)0x00);  // Sends 00000000 (0x00) to MMA8452Q 0x1F register (0x1F = TRANSIENT_THS register)
  Wire.write((byte)0x0A);  // Sends 00001010 (10) to MMA8452Q 0x20 register (0x20 = TRANSIENT_COUNT register)

  Wire.endTransmission();

I am at a loss and can't understand how one is supposed to write to multiple registers. I2C Scan identified the only sensor (MMA8452Q) on the Bus as 0x1D, and if I am interpreting the data sheet correctly the three registers above are automatically written to sequentially. Can anyone explain what I am missing? I have tried numerous variation and posted the last bit of code that I tried.

Thanks,

Z

You have to send the register-address and then the value to write. You can not just write 0x0E, 0x00 and 0x0A and assume that they somehow magically happen to be written to the right register-address.

Always show your sketch, so we can see your progress.

I will try again, I don't know if the registers and data is right. I used binairy notation for the register values, maybe that is easier for you than hexadecimal values.

Wire.beginTransmission(0x1D);  // address of MMA8452Q
Wire.write(0x1D);              // TRANSIENT_CFG
Wire.write(0b00010000);        // Sends 00010000
Wire.endTransmission();

Wire.beginTransmission(0x1D);  // address of MMA8452Q
Wire.write(0x1F);              // TRANSIENT_THS
Wire.write(0b00001110);        // Sends 00001110
Wire.endTransmission();

Wire.beginTransmission(0x1D);  // address of MMA8452Q
Wire.write(0x20);              // TRANSIENT_COUNT
Wire.write(0b00001010);        // Sends 00001010
Wire.endTransmission();

In the datasheet is also something about the Standby Mode. It is possible that you have to wake it up.

Why all this trouble ? Sparkfun has a library for it: GitHub - sparkfun/SparkFun_MMA8452Q_Arduino_Library: SparkFun Triple Axis Accelerometer Breakout - MMA8452Q Arduino Library with functions to read and write registers.

Hello Koepel,

Thanks for the reply and thanks for sticking with this, but unfortunately your code did not work. I say this because gravity is still coupled with my measurements. When I plot X, Y & Z, Z still reads 1g.

The reason that I thought that the bytes would automatically write to the next register is because of this from the data sheet...

Multiple Byte Write
The MMA8452Q automatically increments the received register address commands after a write command is received.
Therefore, after following the steps of a single byte write, multiple bytes of data can be written to sequential registers after each MMA8452Q acknowledgment (ACK) is received.

I can post all my code, but it is pretty ugly as it is a "working sketch" at best. (I had to trim a bunch to post, too large)

I will look at safety mode again.

I have the Sparkfun library but did not find anything that worked or was helpful, am I missing something obvious?

  • Z
/******************************************************************************
 
#include <Wire.h>  // Must include Wire library for I2C
#include <SparkFun_MMA8452Q.h>  // Includes the SFE_MMA8452Q library

// Begin using the library by creating an instance of the MMA8452Q
//  class. We'll call it "accel". That's what we'll reference from
//  here on out.
MMA8452Q accel;

// The setup function simply starts serial and initializes the accelerometer.

const byte LED = 13;
const byte Brake = 11;  // LED that "Brake" function triggers.

static float smoothedValue = 0.0;

const float alpha = 0.99;  //  Smoothing variable, 0 = No smoothing, 1 = maximum smooting.

float Past = 0 ;

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
long previousMillis = 0;        // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 125;           // interval at which to blink (milliseconds)

//char tmpstring[10];

const float Deg2Rad = 0.01745329251994329576923690768489;

int toprint = 0;
//float TrueAccX = 0;

const float Lim = 1.00;
const float Lim2 = 0.1;

void setup()
{
  Serial.begin(9600);

  Wire.begin();  // Join i2c bus (address optional for master)

  Wire.beginTransmission(0x1D);  // address of MMA8452Q
  Wire.write(0x1D);              // TRANSIENT_CFG
  Wire.write(0b00010000);        // Sends 00010000
  Wire.endTransmission();

  Wire.beginTransmission(0x1D);  // address of MMA8452Q
  Wire.write(0x1F);              // TRANSIENT_THS
  Wire.write(0b00001110);        // Sends 00001110
  Wire.endTransmission();

  Wire.beginTransmission(0x1D);  // address of MMA8452Q
  Wire.write(0x20);              // TRANSIENT_COUNT
  Wire.write(0b00001010);        // Sends 00001010
  Wire.endTransmission();


  //  Wire.beginTransmission((byte)0x1D);     // Begins transmission to 0x38 address of MMA8452Q
  //
  //  //  Wire.write((byte)14);  // Sends 00000000 (0x0E) to MMA8452Q 0x0D register (0x0D = TRANSIENT_CFG register)
  //  Wire.write((byte)0x0E);  // Sends 00000000 (14) to MMA8452Q 0x0D register (0x0D = TRANSIENT_CFG register)
  //  //  Wire.write(0);  // Sends 00000000 (0x00) to MMA8452Q 0x1E register (0x1E = TRANSIENT_SRC register)
  //  //  Wire.write((byte)0);  // Sends 00000000 (0x00) to MMA8452Q 0x1F register (0x1F = TRANSIENT_THS register)
  //  Wire.write((byte)0x00);  // Sends 00000000 (0x00) to MMA8452Q 0x1F register (0x1F = TRANSIENT_THS register)
  //  //  Wire.write((byte)10);  // Sends 00001010 (0x0A) to MMA8452Q 0x20 register (0x20 = TRANSIENT_COUNT register)
  //  Wire.write((byte)0x20);  // Sends 00001010 (0x0A) to MMA8452Q 0x20 register (0x20 = TRANSIENT_COUNT register)
  //
  //  Wire.endTransmission();

  Serial.println("Brake_021320");
  pinMode(LED, OUTPUT);
  pinMode(Brake, OUTPUT);

  // Choose your adventure! There are a few options when it comes
  // to initializing the MMA8452Q:
  //  1. Default init. This will set the accelerometer up
  //     with a full-scale range of +/-2g, and an output data rate
  //     of 800 Hz (fastest).
  accel.init();
  //  2. Initialize with FULL-SCALE setting. You can set the scale
  //     using either SCALE_2G, SCALE_4G, or SCALE_8G as the value.
  //     That'll set the scale to +/-2g, 4g, or 8g respectively.
  //accel.init(SCALE_4G); // Uncomment this out if you'd like
  //  3. Initialize with FULL-SCALE and DATA RATE setting. If you
  //     want control over how fast your accelerometer produces
  //     data use one of the following options in the second param:
  //     ODR_800, ODR_400, ODR_200, ODR_100, ODR_50, ODR_12,
  //     ODR_6, or ODR_1.
  //     Sets to 800, 400, 200, 100, 50, 12.5, 6.25, or 1.56 Hz.
  //accel.init(SCALE_8G, ODR_6);
  //      accel.init(SCALE_2G, ODR_12);

}

// The loop function will simply check for new data from the
//  accelerometer and print it out if it's available.

void loop()
{
  // Use the accel.available() function to wait for new data
  //  from the accelerometer.
  if (accel.available())  //  If new data is available...
  {
    // First, use accel.read() to read the (3) new variables:
    accel.read();

    // accel.read() will update two sets of variables.
    // * int's x, y, and z will store the signed 12-bit values
    //   read out of the accelerometer.
    // * floats cx, cy, and cz will store the calculated
    //   acceleration from those 12-bit values. These variables
    //   are in units of g's.
    // Check the two function declarations below for an example
    // of how to use these variables.
    printCalculatedAccels();
    //        printAccels(); // Uncomment to print digital readings

    // The library also supports the portrait/landscape detection
    //  of the MMA8452Q. Check out this function declaration for
    //  an example of how to use that.
    //  printOrientation();
    //    PrintData();  // Formatted to print data to Serial Monitor with data lables.
    //    PlotData();  // Formatted to print data to Serial Plotter withou data lables.

    int xAcceleration = accel.x; // Read in raw x-axis acceleration data


    toprint = map(xAcceleration, 0, 1000, 0, 90);
    //    sprintf(tmpstring, "%2d", toprint);
    //    Serial.print("Angle measurement is ");
    //    Serial.print(toprint); Serial.print("°");
    //    Serial.print("angle: ");
    //    Serial.println(tmpstring);
    Serial.println();  // Print new line every time. This is needed to plot to the Serial Plotter correctly.


    static float smoothedValue = 0.0;  // re-zeros smoothedValue??? Variable has to be declared "static float" here in code for calculations to work.
    //    smoothedValue = 0.0;
    //    float newReading = (accel.cx);
    //    smoothedValue = (alpha * smoothedValue) + ((1 - alpha) * newReading);
    smoothedValue = (alpha * smoothedValue) + ((1 - alpha) * accel.cx);
    //    Serial.print(newReading); Serial.print(", ");
    //    Serial.print(accel.cx); Serial.print(", ");
    //    Serial.print(smoothedValue); Serial.println("\t ");

    //    Current = accel.cx;

    //    if (smoothedValue > 0.015)
    //    if ((Past - Current) > 0.015)
    //    Serial.print(Past - smoothedValue);

    //    if (Past - smoothedValue < 0)
    //    TrueAccX = (smoothedValue - (cos((90 - toprint) * Deg2Rad)));


    //    if ((smoothedValue - (sin(toprint * Deg2Rad))) < 0)
    //    if ((TrueAccX > 0.25) || (TrueAccX < -0.25))
    //    if (TrueAccX < -0.1)
    if (smoothedValue < -0.1)
    {
      analogWrite(Brake, 255);
      digitalWrite(LED, LOW);
      //      Serial.println("---Decelerating---");
    }

    else
    {
      analogWrite(Brake, 0);
      digitalWrite(LED, HIGH);
      //      Serial.println("Acclerating");
    }
    //    Past = smoothedValue;

    // check to see if it's time to blink the LED; that is, if the
    // difference between the current time and last time you blinked
    // the LED is bigger than the interval at which you want to
    // blink the LED.
    unsigned long currentMillis = millis();

    if (currentMillis - previousMillis > interval)
    {
      // save the last time you blinked the LED
      previousMillis = currentMillis;

      // if the LED is off turn it on and vice-versa:
      if (ledState == LOW)
        ledState = HIGH;
      else
        ledState = LOW;

      // set the LED with the ledState of the variable:
      digitalWrite(LED, ledState);
    }
  }
}

}