318 lines
9.8 KiB
Arduino
318 lines
9.8 KiB
Arduino
|
/* 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<i-1; j++) {
|
||
|
dest[j] = i2c.read();
|
||
|
}
|
||
|
dest[j+1] = i2c.readLast();
|
||
|
i2c.endTransmission();
|
||
|
|
||
|
}
|
||
|
|
||
|
// read a single byte from address and return it as a byte
|
||
|
byte readRegister(uint8_t address)
|
||
|
{
|
||
|
byte data;
|
||
|
|
||
|
i2c.beginTransmission( MMA8452_ADDRESS );
|
||
|
i2c.write( address );
|
||
|
i2c.endTransmission();
|
||
|
|
||
|
i2c.requestFrom( MMA8452_ADDRESS );
|
||
|
data = i2c.readLast();
|
||
|
i2c.endTransmission();
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Writes a single byte (data) into address
|
||
|
void writeRegister(unsigned char address, unsigned char data)
|
||
|
{
|
||
|
i2c.beginTransmission( MMA8452_ADDRESS );
|
||
|
i2c.write( address );
|
||
|
i2c.write( data );
|
||
|
i2c.endTransmission();
|
||
|
}
|
||
|
|