# MMA8451Q Accelerometer, calculating device orientation and G forces over the 3 axis Nowadays any “mobile” device like tablet smartphone etc, has internal accelerometers that can provide information about the orientation of the device or the G force to which the device is subjected. From that capabilities derive functions as “pedometer”, commands activated with the “shake” movement, the screen orientation and so on. But before you use it you need to understand exactly how it works. Imagine the device placed on a perfectly flat table. If the table is not moving and is perfectly flat, it is subject only to the force of gravity, that goes from the top down. This force is a constant acceleration, of 9,81m / s down, which is equivalent to 1G: the device will indicate +1G along the axis pointing toward the center of the Earth. If that indicates +1G on the Z axis, it means that it is laying on the table, while if it indicates + 1G on the X or Y axis, it means that it is placed on the table on one side or the other. Clearly are also computed intermediate points: for example placing it on a corner but perfectly vertical, this would indicate 0,70G on an axis and on the other 0,70G! (It will not be halved but calculated using the sine or cosine of the vector). Leaving aside the inclination, moving the device quickly towards the floor, favoring the force of gravity, for a short period probably it will indicate 0G. The device no longer detects the gravity acceleration because you are pandering it, then it actually is in weightlessness status, a bit like the zero-gravity flights organized by NASA, where a plane is being flown in nosedive to get the same effect.

But, how can a small device detect accelerations on different axes? An excerpt from a technical paper can clarify some things: Simply, the microchip inside has microscopic plates able to flex when the device is subjected to an acceleration, the same occurs with gravity: try imagine them “hanging down.” Moving, they move away or closer to their reference surface, thus varying the electric capacity between the two surfaces, exactly as it happens in a normal mechanical variable capacitor. Replicating the same mechanism for all 3 axes that describe three-dimensional space, we can get all the information we need to derive the direction and intensity of the vector of the force that is acting on the device. Take for example this “breakout board” with very small SMD accelerometer mounted on board. The output pins, conveniently spaced with 2.54mm, are quite clear: GND, + 3.3V, SCL and SDA for I2C interface. After connecting the corresponding pins to your control board, and specifically setting up the I2C interface depending on your editor, you can begin to communicate with the device!

First of all, in my case using CodeWarrior and Processor Expert, I set the I2C communication: Note that I set the SCL frequency below 100kHz, although it is possible to set up to 400kHz, just to avoid any problems.

Next, I created two simple functions to send and receive bytes with the device (the standard address is 0x77) using the I2C driver interrupt function, which resets a flag ended the respective process of sending or receiving data:

```// two interrupt used to know
// when is possible to close the comm.
{
DataReceivedFlg = TRUE; /* Set DataReceivedFlg flag */
}

void I2C2_OnTransmitData(void)
{
DataTransmittedFlg = TRUE; /* Set DataTransmittedFlg flag */
}

// read one or more regs from the device
void I2C_ReadReg(char regaddr, char *data, short dataSize) {
char res;
word ret;

// set the pointer to a register
res = I2C2_SendBlock(&regaddr, 1U, &ret);

// wait till the end of the trasmission
while (!DataTransmittedFlg) {}
DataTransmittedFlg = FALSE;

// read the requested number of bytes
res = I2C2_RecvBlock(data, dataSize, &ret);

// wait till the end of the communication

// end the communication
I2C2_SendStop();
}

// write a byte to the register
void I2C_WriteReg(char regaddr, char val) {
char buf, res;
word ret;

buf = regaddr; // address of the register to write
buf = val;     // value to put in the register
res = I2C2_SendBlock(&buf, 2U, &ret); // transmit the two bytes

// wait till the end of the communication
while (!DataTransmittedFlg) {}
DataTransmittedFlg = FALSE;

// end the communication
I2C2_SendStop();
}```

Now that we have simplified the whole thing by creating two functions to communicate with the device, we can bootstrap it and for that I have analyzed the datasheet obtaining the following table: ```// setting at 1 the reset bit
I2C_WriteReg(0x2B, 0x40);

// waiting till the end of the reset function
while (reg_data & 0x40)
{
}

// disabling fast-mode to obtain 14bit resolution
I2C_WriteReg(0x2A, 0x02);
// range to +-4g
I2C_WriteReg(0x0E, 0x02;
// high resolution
I2C_WriteReg(0x2B, 0x02);
// set accelerometer as active (yellow box in the table)
I2C_WriteReg(0x2A, 0x01);```

The device was initialized with the range + -4G, therefore checking the details on the datasheet, I have found that is needed to split the raw value by 2048: ```// defining some variables
#include "math.h" // necessary for the abs() function
float g_x, g_y, g_z, g_dir;

//reading 6 bytes related to the 3 axes

// joining the 8bit of the first byte
// with the 6bits of the second byte
g_x= (((xyz<<8) | xyz)>>2)/2048;
g_y= (((xyz<<8) | xyz)>>2)/2048;
g_z= (((xyz<<8) | xyz)>>2)/2048;

// with the next series of conditions is
// possible to select four orientation settings
if ((abs(g_z) < 5) && (g_x > 5) && (abs(g_y) < 4)) { g_dir = 1; }
if ((abs(g_z) < 5) && (g_x < -5) && (abs(g_y) < 4)) { g_dir = 2; }
if ((abs(g_z) < 5) && (g_y > 5) && (abs(g_x) < 4)) { g_dir = 3; }
if ((abs(g_z) < 5) && (g_y < -5) && (abs(g_x) < 4)) { g_dir = 4; }```

At this point in g_x, g_y, g_z I find the numerical value of the acceleration per axis expressed in G, example “1.0”. While in g_dir I find the rotation of orientation recommended for a possible screen, whose four values correspond to 0, 90, 180, 270 degrees. I extracted this last set of conditions from a technical document.