AnsweredAssumed Answered

MMA8452Q INT1 staying high

Question asked by John Munroe on Sep 30, 2015
Latest reply on Mar 2, 2018 by Jeffrey Haynes

Hi

 

I'm using the MMA8452Q in one of my Arduino projects. The issue I'm facing is that INT1 stays high when I don't think it's supposed to.

 

According to S5.9 of the datasheet:

 

"If the event flag is asserted because the event condition is detected, the corresponding interrupt pin, INT1 or INT2, will assert."

 

So, my understanding is that INT1 should be asserted only when an event condition is satisfied and it is cleared when the relevant register is read. However, even having read the registers and bits in INT_SRC are cleared, INT1 is still asserted.

 

I have attached my library and sketch code below.

 

Thanks for any help in advance.

 

/******************************************************************************
SFE_MMA8452Q.h
SFE_MMA8452Q Library Header File
Jim Lindblom @ SparkFun Electronics
Original Creation Date: June 3, 2014
https://github.com/sparkfun/MMA8452_Accelerometer


This file prototypes the MMA8452Q class, implemented in SFE_MMA8452Q.cpp. In
addition, it defines every register in the MMA8452Q.


Development environment specifics:
  IDE: Arduino 1.0.5
  Hardware Platform: Arduino Uno


This code is beerware; if you see me (or any other SparkFun employee) at the
local, and you've found our code helpful, please buy us a round!


Distributed as-is; no warranty is given.
******************************************************************************/


#ifndef SFE_MMA8452Q_h
#define SFE_MMA8452Q_h


#include <Arduino.h>


///////////////////////////////////
// MMA8452Q Register Definitions //
///////////////////////////////////
enum MMA8452Q_Register {
  STATUS = 0x00,
  OUT_X_MSB = 0x01,
  OUT_X_LSB = 0x02,
  OUT_Y_MSB = 0x03,
  OUT_Y_LSB = 0x04,
  OUT_Z_MSB = 0x05,
  OUT_Z_LSB = 0x06,
  SYSMOD = 0x0B,
  INT_SOURCE = 0x0C,
  WHO_AM_I = 0x0D,
  XYZ_DATA_CFG = 0x0E,
  HP_FILTER_CUTOFF = 0x0F,
  PL_STATUS = 0x10,
  PL_CFG = 0x11,
  PL_COUNT = 0x12,
  PL_BF_ZCOMP = 0x13,
  P_L_THS_REG = 0x14,
  FF_MT_CFG = 0x15,
  FF_MT_SRC = 0x16,
  FF_MT_THS = 0x17,
  FF_MT_COUNT = 0x18,
  TRANSIENT_CFG = 0x1D,
  TRANSIENT_SRC = 0x1E,
  TRANSIENT_THS = 0x1F,
  TRANSIENT_COUNT = 0x20,
  PULSE_CFG = 0x21,
  PULSE_SRC = 0x22,
  PULSE_THSX = 0x23,
  PULSE_THSY = 0x24,
  PULSE_THSZ = 0x25,
  PULSE_TMLT = 0x26,
  PULSE_LTCY = 0x27,
  PULSE_WIND = 0x28,
  ASLP_COUNT = 0x29,
  CTRL_REG1 = 0x2A,
  CTRL_REG2 = 0x2B,
  CTRL_REG3 = 0x2C,
  CTRL_REG4 = 0x2D,
  CTRL_REG5 = 0x2E,
  OFF_X = 0x2F,
  OFF_Y = 0x30,
  OFF_Z = 0x31
};


////////////////////////////////
// MMA8452Q Misc Declarations //
////////////////////////////////
enum MMA8452Q_Scale {SCALE_2G = 2, SCALE_4G = 4, SCALE_8G = 8}; // Possible full-scale settings
enum MMA8452Q_ODR {ODR_800, ODR_400, ODR_200, ODR_100, ODR_50, ODR_12, ODR_6, ODR_1}; // possible data rates
enum MMA8452Q_Power {NORMAL, LOW_NOISE, HIGH_RES, LOW_POWER};
// Possible portrait/landscape settings
#define PORTRAIT_U 0
#define PORTRAIT_D 1
#define LANDSCAPE_R 2
#define LANDSCAPE_L 3
#define LOCKOUT 0x40


// Possible tap pulses
#define SINGLE_TAP 0
#define DOUBLE_TAP 1


#define SRC_FF_MT 1
#define SRC_PULSE 2
#define SRC_TRANSIENT 8


////////////////////////////////
// MMA8452Q Class Declaration //
////////////////////////////////
class MMA8452Q
{
public:
    MMA8452Q(byte addr = 0x1D); // Constructor

  byte init(MMA8452Q_Scale fsr = SCALE_2G, MMA8452Q_ODR odr = ODR_800, MMA8452Q_Power pmode = NORMAL);
    void read();
  byte available();
  byte readTap();
  byte readPL();
  byte readISR();
  byte readFF_MT();
  byte readTransient();

    int x, y, z;
  float cx, cy, cz;
private:
  byte address;
  MMA8452Q_Scale scale;

  void standby();
  void active();
  void setupPL();
  void setupISR();
  void setupFF_MT();
  void setupTransient();
  void setupTap(byte xThs, byte yThs, byte zThs);
  void setScale(MMA8452Q_Scale fsr);
  void setODR(MMA8452Q_ODR odr);
  void setActivePowerMode(MMA8452Q_Power pmode);
  void writeRegister(MMA8452Q_Register reg, byte data);
    void writeRegisters(MMA8452Q_Register reg, byte *buffer, byte len);
  byte readRegister(MMA8452Q_Register reg);
    void readRegisters(MMA8452Q_Register reg, byte *buffer, byte len);
};


#endif


 

/******************************************************************************
SFE_MMA8452Q.cpp
SFE_MMA8452Q Library Source File
Jim Lindblom @ SparkFun Electronics
Original Creation Date: June 3, 2014
https://github.com/sparkfun/MMA8452_Accelerometer


This file implements all functions of the MMA8452Q class. Functions here range
from higher level stuff, like reading/writing MMA8452Q registers to low-level,
hardware I2C reads and writes.


Development environment specifics:
  IDE: Arduino 1.0.5
  Hardware Platform: Arduino Uno


This code is beerware; if you see me (or any other SparkFun employee) at the
local, and you've found our code helpful, please buy us a round!


Distributed as-is; no warranty is given.
******************************************************************************/


#include "SFE_MMA8452Q.h"
#include <Arduino.h>
#include <Wire.h>


// CONSTRUCTUR
//   This function, called when you initialize the class will simply write the
//   supplied address into a private variable for future use.
//   The variable addr should be either 0x1C or 0x1D, depending on which voltage
//   the SA0 pin is tied to (GND or 3.3V respectively).
MMA8452Q::MMA8452Q(byte addr)
{
  address = addr; // Store address into private variable
}


// INITIALIZATION
// This function initializes the MMA8452Q. It sets up the scale (either 2, 4,
// or 8g), output data rate, portrait/landscape detection and tap detection.
// It also checks the WHO_AM_I register to make sure we can communicate with
// the sensor. Returns a 0 if communication failed, 1 if successful.
byte MMA8452Q::init(MMA8452Q_Scale fsr, MMA8452Q_ODR odr, MMA8452Q_Power pmode)
{
  scale = fsr; // Haul fsr into our class variable, scale

  Wire.begin(); // Initialize I2C
  byte c = readRegister(WHO_AM_I);  // Read WHO_AM_I register


  if (c != 0x2A) // WHO_AM_I should always be 0x2A
  {
  return 0;
  }

  standby();  // Must be in standby to change registers

  setScale(scale);  // Set up accelerometer scale
  setODR(odr);  // Set up output data rate
  setActivePowerMode(pmode);


  setupPL();  // Set up portrait/landscape detection
  setupISR();
  setupFF_MT();
  setupTransient();


  // Multiply parameter by 0.0625g to calculate threshold.
  setupTap(0x80, 0x80, 0x08); // Disable x, y, set z to 0.5g

  active();  // Set to active to start reading

  return 1;
}


// READ ACCELERATION DATA
//  This function will read the acceleration values from the MMA8452Q. After
// reading, it will update two triplets of variables:
// * int's x, y, and z will store the signed 12-bit values read out
//  of the acceleromter.
// * floats cx, cy, and cz will store the calculated acceleration from
//  those 12-bit values. These variables are in units of g's.
void MMA8452Q::read()
{
  byte rawData[6];  // x/y/z accel register data stored here


  readRegisters(OUT_X_MSB, rawData, 6);  // Read the six raw data registers into data array

  x = (rawData[0]<<8 | rawData[1]) >> 4;
  y = (rawData[2]<<8 | rawData[3]) >> 4;
  z = (rawData[4]<<8 | rawData[5]) >> 4;
  cx = (float) x / (float)(1<<11) * (float)(scale);
  cy = (float) y / (float)(1<<11) * (float)(scale);
  cz = (float) z / (float)(1<<11) * (float)(scale);
}


// CHECK IF NEW DATA IS AVAILABLE
// This function checks the status of the MMA8452Q to see if new data is availble.
// returns 0 if no new data is present, or a 1 if new data is available.
byte MMA8452Q::available()
{
  return (readRegister(STATUS) & 0x08) >> 3;
}


// SET FULL-SCALE RANGE
// This function sets the full-scale range of the x, y, and z axis accelerometers.
// Possible values for the fsr variable are SCALE_2G, SCALE_4G, or SCALE_8G.
void MMA8452Q::setScale(MMA8452Q_Scale fsr)
{
  // Must be in standby mode to make changes!!!
  byte cfg = readRegister(XYZ_DATA_CFG);
  cfg &= 0xFC; // Mask out scale bits
  cfg |= (fsr >> 2);  // Neat trick, see page 22. 00 = 2G, 01 = 4A, 10 = 8G
  writeRegister(XYZ_DATA_CFG, cfg);
}


// SET THE OUTPUT DATA RATE
// This function sets the output data rate of the MMA8452Q.
// Possible values for the odr parameter are: ODR_800, ODR_400, ODR_200, 
// ODR_100, ODR_50, ODR_12, ODR_6, or ODR_1
void MMA8452Q::setODR(MMA8452Q_ODR odr)
{
  // Must be in standby mode to make changes!!!
  byte ctrl = readRegister(CTRL_REG1);
  ctrl &= 0xCF; // Mask out data rate bits
  ctrl |= (odr << 3);
  writeRegister(CTRL_REG1, ctrl);
}


void MMA8452Q::setActivePowerMode(MMA8452Q_Power pmode)
{
  byte ctrl = readRegister(CTRL_REG2);
  ctrl &= 0xDC;
  ctrl |= pmode;
  writeRegister(CTRL_REG2, ctrl);
}


// SET UP TAP DETECTION
// This function can set up tap detection on the x, y, and/or z axes.
// The xThs, yThs, and zThs parameters serve two functions:
// 1. Enable tap detection on an axis. If the 7th bit is SET (0x80)
// tap detection on that axis will be DISABLED.
// 2. Set tap g's threshold. The lower 7 bits will set the tap threshold
// on that axis.
void MMA8452Q::setupTap(byte xThs, byte yThs, byte zThs)
{
  // Set up single and double tap - 5 steps:
  // for more info check out this app note:
  // http://cache.freescale.com/files/sensors/doc/app_note/AN4072.pdf
  // Set the threshold - minimum required acceleration to cause a tap.
  byte temp = 0;
  if (!(xThs & 0x80)) // If top bit ISN'T set
  {
  temp |= 0x3; // Enable taps on x
  writeRegister(PULSE_THSX, xThs);  // x thresh
  }
  if (!(yThs & 0x80))
  {
  temp |= 0xC; // Enable taps on y
  writeRegister(PULSE_THSY, yThs);  // y thresh
  }
  if (!(zThs & 0x80))
  {
  temp |= 0x30; // Enable taps on z
  writeRegister(PULSE_THSZ, zThs);  // z thresh
  }
  // Set up single and/or double tap detection on each axis individually.
  writeRegister(PULSE_CFG, temp | 0x40);
  // Set the time limit - the maximum time that a tap can be above the thresh
  writeRegister(PULSE_TMLT, 0x30);  // 30ms time limit at 800Hz odr
  // Set the pulse latency - the minimum required time between pulses
  writeRegister(PULSE_LTCY, 0xA0);  // 200ms (at 800Hz odr) between taps min
  // Set the second pulse window - maximum allowed time between end of
  // latency and start of second pulse
  writeRegister(PULSE_WIND, 0xFF);  // 5. 318ms (max value) between taps max
}


// READ TAP STATUS
// This function returns any taps read by the MMA8452Q. If the function 
// returns no new taps were detected. Otherwise the function will return the
// lower 7 bits of the PULSE_SRC register.
byte MMA8452Q::readTap()
{
  byte tapStat = readRegister(PULSE_SRC);
  if (tapStat & 0x80) // Read EA bit to check if a interrupt was generated
  {
  return tapStat & 0x7F;
  }
  else
  return 0;
}


// SET UP PORTRAIT/LANDSCAPE DETECTION
// This function sets up portrait and landscape detection.
void MMA8452Q::setupPL()
{
  // Must be in standby mode to make changes!!!
  // For more info check out this app note:
  // http://cache.freescale.com/files/sensors/doc/app_note/AN4068.pdf
  // 1. Enable P/L
  writeRegister(PL_CFG, readRegister(PL_CFG) | 0x40); // Set PL_EN (enable)
  // 2. Set the debounce rate
  writeRegister(PL_COUNT, 0x50);  // Debounce counter at 100ms (at 800 hz)
}


// READ PORTRAIT/LANDSCAPE STATUS
// This function reads the portrait/landscape status register of the MMA8452Q.
// It will return either PORTRAIT_U, PORTRAIT_D, LANDSCAPE_R, LANDSCAPE_L,
// or LOCKOUT. LOCKOUT indicates that the sensor is in neither p or ls.
byte MMA8452Q::readPL()
{
  byte plStat = readRegister(PL_STATUS);

  if (plStat & 0x40) // Z-tilt lockout
  return LOCKOUT;
  else // Otherwise return LAPO status
  return (plStat & 0x6) >> 1;
}


void MMA8452Q::setupISR()
{
  writeRegister(CTRL_REG4, 0x2D);
}


byte MMA8452Q::readISR()
{
  byte intStat = readRegister(INT_SOURCE);


  if (intStat & 0x2C)
  {
  return intStat & 0x2C;
  }
  else
  return 0;
}


void MMA8452Q::setupFF_MT()
{
  writeRegister(FF_MT_CFG, 0xF8);
  writeRegister(FF_MT_THS, 0x13);
  writeRegister(FF_MT_COUNT, 0x00);
}


byte MMA8452Q::readFF_MT()
{
  byte ff_mt_src = readRegister(FF_MT_SRC);


  if (ff_mt_src & 0x80)
  {
  return ff_mt_src & 0x2F;
  }
  else
  return 0;
}


void MMA8452Q::setupTransient()
{
  writeRegister(TRANSIENT_CFG, 0x1E);
  writeRegister(TRANSIENT_THS, 0x01);
  writeRegister(TRANSIENT_COUNT, 0x01);
}


byte MMA8452Q::readTransient()
{
  byte transStat = readRegister(TRANSIENT_SRC);


  if (transStat & 0x40)
  {
  return transStat & 0x3F;
  }
  else
  return 0;
}


// SET STANDBY MODE
// Sets the MMA8452 to standby mode. It must be in standby to change most register settings
void MMA8452Q::standby()
{
  byte c = readRegister(CTRL_REG1);
  writeRegister(CTRL_REG1, c & ~(0x01)); //Clear the active bit to go into standby
}


// SET ACTIVE MODE
// Sets the MMA8452 to active mode. Needs to be in this mode to output data
void MMA8452Q::active()
{
  byte c = readRegister(CTRL_REG1);
  writeRegister(CTRL_REG1, c | 0x01); //Set the active bit to begin detection
}


// WRITE A SINGLE REGISTER
// Write a single byte of data to a register in the MMA8452Q.
void MMA8452Q::writeRegister(MMA8452Q_Register reg, byte data)
{
  writeRegisters(reg, &data, 1);
}


// WRITE MULTIPLE REGISTERS
// Write an array of "len" bytes ("buffer"), starting at register "reg", and
// auto-incrmenting to the next.
void MMA8452Q::writeRegisters(MMA8452Q_Register reg, byte *buffer, byte len)
{
  Wire.beginTransmission(address);
  Wire.write(reg);
  for (int x = 0; x < len; x++)
  Wire.write(buffer[x]);
  Wire.endTransmission(); //Stop transmitting
}


// READ A SINGLE REGISTER
// Read a byte from the MMA8452Q register "reg".
byte MMA8452Q::readRegister(MMA8452Q_Register reg)
{
  Wire.beginTransmission(address);
  Wire.write(reg);
  Wire.endTransmission(false); //endTransmission but keep the connection active


  Wire.requestFrom(address, (byte) 1); //Ask for 1 byte, once done, bus is released by default
  while(!Wire.available()) ; //Wait for the data to come back


  return Wire.read(); //Return this one byte
}


// READ MULTIPLE REGISTERS
// Read "len" bytes from the MMA8452Q, starting at register "reg". Bytes are stored
// in "buffer" on exit.
void MMA8452Q::readRegisters(MMA8452Q_Register reg, byte *buffer, byte len)
{
  Wire.beginTransmission(address);
  Wire.write(reg);
  Wire.endTransmission(false); //endTransmission but keep the connection active


  Wire.requestFrom(address, len); //Ask for bytes, once done, bus is released by default


  while(Wire.available() < len); //Hang out until we get the # of bytes we expect


  for(int x = 0 ; x < len ; x++)
  buffer[x] = Wire.read();    
}



 

 

/******************************************************************************
MMA8452Q_Basic.ino
SFE_MMA8452Q Library Basic Example Sketch
Jim Lindblom @ SparkFun Electronics
Original Creation Date: June 3, 2014
https://github.com/sparkfun/MMA8452_Accelerometer


This sketch uses the SFE_MMA8452Q library to initialize the
accelerometer, and stream values from it.


Hardware hookup:
  Arduino --------------- MMA8452Q Breakout
    3.3V  ---------------     3.3V
    GND   ---------------     GND
  SDA (A4) --\/330 Ohm\/--    SDA
  SCL (A5) --\/330 Ohm\/--    SCL


The MMA8452Q is a 3.3V max sensor, so you'll need to do some 
level-shifting between the Arduino and the breakout. Series
resistors on the SDA and SCL lines should do the trick.


Development environment specifics:
  IDE: Arduino 1.0.5
  Hardware Platform: Arduino Uno


This code is beerware; if you see me (or any other SparkFun employee) at the
local, and you've found our code helpful, please buy us a round!


Distributed as-is; no warranty is given.
******************************************************************************/
#include <Wire.h> // Must include Wire library for I2C
#include <SFE_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.
void setup()
{
  Serial.begin(9600);
  Serial.println("MMA8452Q Test Code!");
  // 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_8G, ODR_200, LOW_POWER);
}


// 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.println
  if (accel.available())
  {
    // First, use accel.read() to read the 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();


    
    printISR();


    printTransient();


    // read FF_MT_SRC to clear register
    accel.readFF_MT();


    printTap();
    
    Serial.println(); // Print new line every time.
  }


}


// The function demonstrates how to use the accel.x, accel.y and
//  accel.z variables.
// Before using these variables you must call the accel.read()
//  function!
void printAccels()
{
  Serial.print(accel.x, 3);
  Serial.print("\t");
  Serial.print(accel.y, 3);
  Serial.print("\t");
  Serial.print(accel.z, 3);
  Serial.print("\t");
}


// This function demonstrates how to use the accel.cx, accel.cy,
//  and accel.cz variables.
// Before using these variables you must call the accel.read()
//  function!
void printCalculatedAccels()
{ 
  Serial.print(accel.cx, 3);
  Serial.print("\t");
  Serial.print(accel.cy, 3);
  Serial.print("\t");
  Serial.print(accel.cz, 3);
  Serial.print("\t");
}


// This function demonstrates how to use the accel.readPL()
// function, which reads the portrait/landscape status of the
// sensor.
void printOrientation()
{
  // accel.readPL() will return a byte containing information
  // about the orientation of the sensor. It will be either
  // PORTRAIT_U, PORTRAIT_D, LANDSCAPE_R, LANDSCAPE_L, or
  // LOCKOUT.
  byte pl = accel.readPL();
  switch (pl)
  {
  case PORTRAIT_U:
    Serial.print("Portrait Up");
    break;
  case PORTRAIT_D:
    Serial.print("Portrait Down");
    break;
  case LANDSCAPE_R:
    Serial.print("Landscape Right");
    break;
  case LANDSCAPE_L:
    Serial.print("Landscape Left");
    break;
  case LOCKOUT:
    Serial.print("Flat");
    break;
  }
  Serial.print("\t");
}


void printTap()
{
  byte tap = accel.readTap();
  if (tap != 0)
  {
    switch ((tap & 0x08) >> 3)
    {
      case SINGLE_TAP:
        Serial.print("Single Tap");
        break;
      case DOUBLE_TAP:
        Serial.print("Double Tap");
        break;
    }
  }
  else
    Serial.print("No Tap");


  Serial.print("\t");    
}


void printTransient()
{
  byte trans = accel.readTransient();
  if (trans)
    Serial.print("Transient");
  else
    Serial.print("No Transient");


  Serial.print("\t");
}


void printISR()
{
  byte isr = (accel.readISR() >> 2);
  if (isr & SRC_PULSE)
    Serial.print("(ISR) Tap");
  else
    Serial.print("(ISR) No Tap");


  Serial.print("\t");


  if (isr & SRC_FF_MT)
    Serial.print("(ISR) Motion");
  else
    Serial.print("(ISR) No Motion");


  Serial.print("\t");    


  if (isr & SRC_TRANSIENT)
    Serial.print("(ISR) Transient");
  else
    Serial.print("(ISR) No Transient");


  Serial.print("\t");


}

Outcomes