/* MMA8452Q Example Code by: Jim Lindblom SparkFun Electronics date: October 13, 2011 license: Creative Commons Share-Alike v3.0 (CC BY-SA 3.0) This code should provide example usage for most features of the MMA8452Q 3-axis, I2C accelerometer. In the loop function the accelerometer interrupt outputs will be polled, and either the x/y/z accel data will be output, or single/double-taps, portrait/landscape changes will be announced. The skeleton is here, feel free to cut/paste what code you need. Play around with the settings in initMMA8452Q. Try running the code without printing the accel values, to really appreciate the single/double-tap and portrait landscape functions. The P/L stuff is really neat, something not many accelerometers have. Hardware setup: MMA8452 Breakout ------------ Arduino 3.3V --------------------- 3.3V SDA ----------------------- A4 SCL ----------------------- A5 INT2 ---------------------- D3 INT1 ---------------------- D2 GND ---------------------- GND SDA and SCL should have external pull-up resistors (to 3.3V). 10k resistors worked for me. They should be on the breakout board. Note: The MMA8452 is an I2C sensor, however this code does not make use of the Arduino Wire library. Because the sensor is not 5V tolerant, we can't use the internal pull-ups used by the Wire library. Instead use the included i2c.h file. */ //#include "i2c.h" // not the wire library, can't use pull-ups #include "SoftI2CMaster.h" //const int sdaPin = A4; //const int sclPin = A5; const int sdaPin = 4; const int sclPin = 5; SoftI2CMaster i2c = SoftI2CMaster( sclPin, sdaPin, 0 ); #define SA0 1 // Breakout board defaults to 1, set to 0 if SA0 jumper is set #if SA0 #define MMA8452_ADDRESS 0x1D // SA0 is high, 0x1C if low #else #define MMA8452_ADDRESS 0x1C #endif /* Set the scale below either 2, 4 or 8*/ const byte scale = 2; // Sets full-scale range to +/-2, 4, or 8g. Used to calc real g values. /* Set the output data rate below. Value should be between 0 and 7*/ const byte dataRate = 0; // 0=800Hz, 1=400, 2=200, 3=100, 4=50, 5=12.5, 6=6.25, 7=1.56 /* Pin definitions */ int int1Pin = 2; // These can be changed, 2 and 3 are the Arduinos ext int pins int int2Pin = 3; byte data[6]; // x/y/z accel register data store here int accelCount[3]; // Stores the 12-bit signed value float accel[3]; // Stores the real accel value in g's void setup() { byte c; Serial.begin(115200); /* Set up the interrupt pins, they're set as active high, push-pull */ pinMode(int1Pin, INPUT); digitalWrite(int1Pin, LOW); pinMode(int2Pin, INPUT); digitalWrite(int2Pin, LOW); /* Read the WHO_AM_I register, this is a good test of communication */ c = readRegister(0x0D); // Read WHO_AM_I register if (c == 0x2A) // WHO_AM_I should always be 0x2A { initMMA8452(scale, dataRate); // init the accelerometer if communication is good Serial.println(F("MMA8452Q is online...")); } else { Serial.print(F("Could not connect to MMA8452Q: 0x")); Serial.println(c, HEX); while (1) // Loop forever if communication doesn't happen ; } } void loop() { static byte source; /* If int1 goes high, all data registers have new data */ if (digitalRead(int1Pin)) // Interrupt pin, should probably attach to interrupt function //if (readRegister(0x00)&0x7) // Polling, you can use this instead of the interrupt pins { readRegisters(0x01, 6, &data[0]); // Read the six data registers into data array // For loop to calculate 12-bit ADC and g value for each axis for (int i=0; i<6; i+=2) { accelCount[i/2] = ((data[i] << 8) | data[i+1]) >> 4; // Turn the MSB and LSB into a 12-bit value if (data[i] > 0x7F) { // If the number is negative, we have to make it so manually (no 12-bit data type) accelCount[i/2] = ~accelCount[i/2] + 1; accelCount[i/2] *= -1; // Transform into negative 2's complement # } accel[i/2] = (float) accelCount[i/2]/((1<<12)/(2*scale)); // get actual g value, this depends on scale being set } // For loop to print out values for (int i=0; i<3; i++) { Serial.print(accel[i], 4); // Print g values //Serial.print(accelCount[i], DEC); // Print adc count values, feel free to uncomment this line Serial.print("\t\t"); } Serial.println(); } /* If int2 goes high, either p/l has changed or there's been a single/double tap */ if (digitalRead(int2Pin)) { source = readRegister(0x0C); // Read the interrupt source reg. if ((source & 0x10)==0x10) // If the p/l bit is set, go check those registers portraitLandscapeHandler(); else if ((source & 0x08)==0x08) // Otherwise, if tap register is set go check that tapHandler(); delay(100); // Delay here for a little printing visibility, make it longer, or delete it } delay(20); } /* This function will read the status of the tap source register. And print if there's been a single or double tap, and on what axis. */ void tapHandler() { byte source = readRegister(0x22); // Reads the PULSE_SRC register if ((source & 0x10)==0x10) // If AxX bit is set { if ((source & 0x08)==0x08) // If DPE (double puls) bit is set Serial.print(" 2 X"); else Serial.print("1 X"); if ((source & 0x01)==0x01) // If PoIX is set Serial.println(" +"); else Serial.println(" -"); } if ((source & 0x20)==0x20) // If AxY bit is set { if ((source & 0x08)==0x08) // If DPE (double puls) bit is set Serial.print(" 2 Y"); else Serial.print("1 Y"); if ((source & 0x02)==0x02) // If PoIY is set Serial.println(" +"); else Serial.println(" -"); } if ((source & 0x40)==0x40) // If AxZ bit is set { if ((source & 0x08)==0x08) // If DPE (double puls) bit is set Serial.print(" 2 Z"); else Serial.print("1 Z"); if ((source & 0x04)==0x04) // If PoIZ is set Serial.println(" +"); else Serial.println(" -"); } } /* This function will read the p/l source register and print what direction the sensor is now facing */ void portraitLandscapeHandler() { byte pl = readRegister(0x10); // Reads the PL_STATUS register switch((pl&0x06)>>1) // Check on the LAPO[1:0] bits { case 0: Serial.print(F("Portrait up, ")); break; case 1: Serial.print(F("Portrait Down, ")); break; case 2: Serial.print(F("Landscape Right, ")); break; case 3: Serial.print(F("Landscape Left, ")); break; } if (pl&0x01) // Check the BAFRO bit Serial.print(F("Back")); else Serial.print(F("Front")); if (pl&0x40) // Check the LO bit Serial.print(F(", Z-tilt!")); Serial.println(); } /* Initialize the MMA8452 registers See the many application notes for more info on setting all of these registers: http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MMA8452Q Feel free to modify any values, these are settings that work well for me. */ void initMMA8452(byte fsr, byte dataRate) { MMA8452Standby(); // Must be in standby to change registers /* Set up the full scale range to 2, 4, or 8g. */ if ((fsr==2)||(fsr==4)||(fsr==8)) writeRegister(0x0E, fsr >> 2); else writeRegister(0x0E, 0); /* Setup the 3 data rate bits, from 0 to 7 */ writeRegister(0x2A, readRegister(0x2A) & ~(0x38)); if (dataRate <= 7) writeRegister(0x2A, readRegister(0x2A) | (dataRate << 3)); /* Set up portrait/landscap registers */ writeRegister(0x11, 0x40); // Enable P/L writeRegister(0x13, 0x14); // 29deg z-lock, writeRegister(0x14, 0x84); // 45deg thresh, 14deg hyst writeRegister(0x12, 0x05); // debounce counter at 100ms /* Set up single and double tap */ writeRegister(0x21, 0x7F); // enable single/double taps on all axes writeRegister(0x23, 0x20); // x thresh at 2g writeRegister(0x24, 0x20); // y thresh at 2g writeRegister(0x25, 0x8); // z thresh at .5g writeRegister(0x26, 0x30); // 60ms time limit, the min/max here is very dependent on output data rate writeRegister(0x27, 0x28); // 200ms between taps min writeRegister(0x28, 0xFF); // 1.275s (max value) between taps max /* Set up interrupt 1 and 2 */ writeRegister(0x2C, 0x02); // Active high, push-pull writeRegister(0x2D, 0x19); // DRDY int enabled, P/L enabled writeRegister(0x2E, 0x01); // DRDY on INT1, P/L on INT2 MMA8452Active(); // Set to active to start reading } /* Sets the MMA8452 to standby mode. It must be in standby to change most register settings */ void MMA8452Standby() { byte c = readRegister(0x2A); writeRegister(0x2A, c & ~(0x01)); } /* Sets the MMA8452 to active mode. Needs to be in this mode to output data */ void MMA8452Active() { byte c = readRegister(0x2A); writeRegister(0x2A, c | 0x01); } /* Read i registers sequentially, starting at address into the dest byte arra */ void readRegisters(byte address, int i, byte * dest) { i2c.beginTransmission( MMA8452_ADDRESS ); i2c.write( address ); i2c.endTransmission(); i2c.requestFrom( MMA8452_ADDRESS ); int j; for( j=0; j