mbed Starter Kit Experiment Guide

This Tutorial is Retired!

This tutorial covers concepts or technologies that are no longer current. It's still here for you to read and enjoy, but may not be as useful as our newest tutorials.

Pages
Contributors: Shawn Hymel
Favorited Favorite 8

Experiment 4: Accelerometer

Using the graphic LCD from the previous tutorial, we will connect a sensor to add an interactive component to our project. We can use an MMA8452Q 3-axis accelerometer to move a ball around the screen for a cool demo.

mbed LCD accelerometer circuit

Suggested Reading

The Circuit

This circuit can be made with parts in the SparkFun mbed Starter Kit. Also, keep in mind that the LPC1768 box contains a USB mini-B cable for programming and power.

Parts List

To follow this experiment, you would will need the following materials if you did not order the SparkFun mbed starter kit. You may not need everything though depending on what you have. Add it to your cart, read through the guide, and adjust the cart as necessary.

Schematic

mbed 4D graphic LCD and accelerometer schematic

Click on schematic to view larger image.

Connections

Connect the LPC1768 to the LCD and accelerometer in the following fashion. Note that the LCD uses the same connections as in Part 3.

Fritzing Diagram

mbed 4d lcd accelerometer fritzing

Hookup Table

Place the LPC1768 in a breadboard with pin VOUT in position i1 and pin 20 in position b20.

Connect the rest of the components as follows:

Component Breadboard
uLCD-144-G2* h26 (RES) h27 (GND) h28 (RX) h29 (TX) h30 (+5V)
MMA8452* b25 (GND) b28 (SCL) b29 (SDA) b30 (3.3V)
Jumper Wire j2 f30
Jumper Wire a1 ( - )
Jumper Wire a9 f28
Jumper Wire a10 f29
Jumper Wire a11 f26
Jumper Wire ( - ) f27
Jumper Wire ( - ) d25
Jumper Wire j14 d28
Jumper Wire j13 d29
Jumper Wire j1 d30

* Pins not listed are not used.

The Code

We will be building on the previous tutorial. In addition to importing an mbed library from the Cookbook for the LCD, we will be building our own library for the MMA8452Q accelerometer.

Libraries

Navigate to the developer.mbed.org, login, and navigate to your Compiler.

Right-click on "My Programs" and create a new program.

mbed new program

Give your program an appropriate name (such as "ulcd_accel"), keep the Template as "Blinky LED Hello World," and click OK.

mbed new program template

Navigate to the 4DGL-uLCD-SE library page.

mbed 4DGL-uLCD-SE library page

Click “Import this library” on the right side of the page. You will be brought to the mbed Compiler and asked to import the library. For "Target Path," select our "ulcd_accel" project and click "Import."

importing a library into our project

The library should now appear under our project in the Program Workspace.

library in our project

Now, we get to create our own library! Right-click on the program in the left pane and select "New Library..."

create a new library

Name your library "MMA8452Q" and click OK.

name our new library

Right-click on the newly created library and select "New File..."

creating a new file

Name your new file "MMA8452Q.h" and click OK.

naming a new file

Repeat the same file creation process to make another file: "MMA8452Q.cpp". Your project folder should contain a main.cpp, the mbed library, the 4DGL-uLCD-SE library, and our newly created MMA8452Q library (with our blank .h and .cpp files).

mbed ulcd_accel project directory

Click on the "MMA8452Q.h" file to open up the blank header (.h) file. Copy the following code into the file.

language:c
// Library for our MMA8452Q 3-axis accelerometer
// Based on the MMA8452Q Arduino Library by Jim Lindblom (SparkFun Electronics)

#ifndef MMA8452Q_H
#define MMA8452Q_H

#include "mbed.h"

// Register definitions
#define REG_STATUS          0x00
#define OUT_X_MSB           0x01
#define OUT_X_LSB           0x02
#define OUT_Y_MSB           0x03
#define OUT_Y_LSB           0x04
#define OUT_Z_MSB           0x05
#define OUT_Z_LSB           0x06
#define REG_WHO_AM_I        0x0D
#define REG_XYZ_DATA_CFG    0x0E
#define REG_CTRL_REG1       0x2A

// WHO_AM_I check
#define FACTORY_ID          0x2A

// Scale definitions
#define SCALE_2G            2
#define SCALE_4G            4
#define SCALE_8G            8

// Data rates
#define ODR_800HZ           0
#define ODR_400HZ           1
#define ODR_200HZ           2
#define ODR_100HZ           3
#define ODR_50HZ            4
#define ODR_12_5HZ          5
#define ODR_6_25HZ          6
#define ODR_1_56HZ          7

// Init values
#define DEFAULT_FSR         SCALE_2G
#define DEFAULT_ODR         ODR_800HZ


// Class declaration
class MMA8452Q
{
    public:
        MMA8452Q(PinName sda, PinName scl, int addr);
        ~MMA8452Q();
        bool init();
        uint8_t available();
        void setScale(uint8_t fsr);
        void setODR(uint8_t odr);
        void standby();
        void active();
        float readX();
        float readY();
        float readZ();
        uint8_t readRegister(uint8_t reg);
        void writeRegister(uint8_t reg, uint8_t data);

    private:
        I2C m_i2c;
        int m_addr;
        int scale;
};

#endif

Click "Save". Note that since we are not compiling our library files right away, we want to save them so we can work on other files. That way, if we lose power or accidentally close our browser, we won't lose our work (don't worry, our library files will get compiled later). Save often!

Click on "MMA8452Q.cpp" to open the blank program (.cpp) file. Copy the following code into the file.

language:c
// Library for our MMA8452Q 3-axis accelerometer
// Based on the MMA8452Q Arduino Library by Jim Lindblom (SparkFun Electronics)

#include "mbed.h"
#include "MMA8452Q.h"

// Constructor
MMA8452Q::MMA8452Q(PinName sda, PinName scl, int addr) : m_i2c(sda, scl), m_addr(addr)
{
    // Initialize members
    scale = DEFAULT_FSR;    
}

// Destructor
MMA8452Q::~MMA8452Q()
{

}

// Initialization
bool MMA8452Q::init()
{
    // Check to make sure the chip's ID matches the factory ID
    uint8_t c = readRegister(REG_WHO_AM_I);
    if( c != FACTORY_ID ) {
        return false;
    }

    // Set default scale and data rate
    standby();
    setScale(DEFAULT_FSR);
    setODR(DEFAULT_ODR);
    active();

    return true;
}

// Set the full-scale range for x, y, and z data
void MMA8452Q::setScale(uint8_t fsr)
{
    uint8_t config = readRegister(REG_XYZ_DATA_CFG);
    scale = fsr;
    config &= 0xFC;                     // Mask out FSR bits
    fsr = fsr >> 2;                     // Trick to translate scale to FSR bits
    fsr &= 0x03;                        // Mask out acceptable FSRs
    config |= fsr;                      // Write FSR bits to config byte
    writeRegister(REG_XYZ_DATA_CFG, config);              // Write config back to register
}

// Set the Output Data Rate
void MMA8452Q::setODR(uint8_t odr)
{
    uint8_t ctrl = readRegister(REG_CTRL_REG1);
    ctrl &= 0xCF;                       // Mask out data rate bits
    odr &= 0x07;                        // Mask out acceptable ODRs
    ctrl |= (odr << 3);                 // Write ODR bits to control byte
    writeRegister(REG_CTRL_REG1, ctrl); // Write control back to register
}

// Set accelerometer into standby mode
void MMA8452Q::standby()
{
    uint8_t c = readRegister(REG_CTRL_REG1);
    c &= ~(0x01);                       // Clear bit 0 to go into standby
    writeRegister(REG_CTRL_REG1, c);    // Write back to CONTROL register
}

// Set accelerometer into active mode
void MMA8452Q::active()
{
    uint8_t c = readRegister(REG_CTRL_REG1);
    c |= 0x01;                          // Set bit 0 to go into active mode
    writeRegister(REG_CTRL_REG1, c);    // Write back to CONTROL register
}

// Read X registers
float MMA8452Q::readX()
{
    int16_t x = 0;
    float cx = 0;

    // Read MSB and LSB from X registers
    x = readRegister(OUT_X_MSB);
    x = x << 8;
    x |= readRegister(OUT_X_LSB);
    x = x >> 4;

    // Calculate human readable X
    cx = (float)x / (float)2048 * (float)(scale);

    return cx;
}

// Read Y registers
float MMA8452Q::readY()
{
    int16_t y = 0;
    float cy = 0;

    // Read MSB and LSB from Y registers
    y = readRegister(OUT_Y_MSB);
    y = y << 8;
    y |= readRegister(OUT_Y_LSB);
    y = y >> 4;

    // Calculate human readable Y
    cy = (float)y / (float)2048 * (float)(scale);

    return cy;
}

// Read Z registers
float MMA8452Q::readZ()
{
    int16_t z = 0;
    float cz = 0;

    // Read MSB and LSB from Z registers
    z = readRegister(OUT_Z_MSB);
    z = z << 8;
    z |= readRegister(OUT_Z_LSB);
    z = z >> 4;

    // Calculate human readable Z
    cz = (float)z / (float)2048 * (float)(scale);

    return cz;
}

// Raw read register over I2C
uint8_t MMA8452Q::readRegister(uint8_t reg)
{
    uint8_t dev_addr;
    uint8_t data;

    // I2C address are bits [6..1] in the transmitted byte, so we shift by 1
    dev_addr = m_addr << 1;

    // Write device address with a trailing 'write' bit
    m_i2c.start();
    m_i2c.write(dev_addr & 0xFE);

    // Write register address
    m_i2c.write(reg);

    // Write a start bit and device address with a trailing 'read' bit
    m_i2c.start();
    m_i2c.write(dev_addr | 0x01);

    // Read single byte from I2C device
    data = m_i2c.read(0);
    m_i2c.stop();

    return data;
}

// Raw write data to a register over I2C    
void MMA8452Q::writeRegister(uint8_t reg, uint8_t data)
{
    uint8_t dev_addr;

    // I2C address are bits [6..1] in the transmitted byte, so we shift by 1
    dev_addr = m_addr << 1;

    // Write device address with a trailing 'write' bit
    m_i2c.start();
    m_i2c.write(dev_addr & 0xFE);

    // Write register address
    m_i2c.write(reg);

    // Write the data to the register
    m_i2c.write(data);
    m_i2c.stop();
}

Click "Save".

And that's it! We just created our very first library in mbed. Because the library is contained within our project, everything is automatically linked at compile time. We just need to write #include "MMA8452Q.h" in our main program to use the MMA8452Q accerlerometer functions.

Program

Click on "main.cpp" under our "ulcd-accel" project to open up our main program file. Because we selected the "Blinky" template, there will be some code in the file already. Go ahead and delete everything in "main.cpp". Copy and paste in the following code.

language:c
// Demo for the uLCD-144-G2 and MMA8452Q 3-axis accelerometer

#include "mbed.h"
#include "MMA8452Q.h"
#include "uLCD_4DGL.h"

// Graphic LCD - TX, RX, and RES pins
uLCD_4DGL uLCD(p9,p10,p11);

// Accelerometer - SDA, SCL, and I2C address
MMA8452Q accel(p28, p27, 0x1D);

int main() {

    // Initialize uLCD
    uLCD.baudrate(115200);
    uLCD.background_color(BLACK);
    uLCD.cls();

    // Initialize accelerometer
    accel.init();

    // Initial parameters for the circle
    float x = 64;
    float y = 64;
    int radius = 4;
    int speed = 4;

    // Make a ball "fall" in direction of accelerometer
    while (1) {

        // Draw a red circle
        uLCD.filled_circle((int)x, (int)y, radius, RED);

        // Wait before erasing old circle
        wait(0.02);         // In seconds

        // Erase old circle
        uLCD.filled_circle((int)x, (int)y, radius, BLACK);

        // Move circle. IMPORTANT! Notice how we adjust for sensor orientation!
        x -= (speed * accel.readY());
        y -= (speed * accel.readX());

        // Make circle sit on edges
        if ( x <= radius + 1 ) {
            x = radius + 1;
        } else if ( x >= 126 - radius ) {
            x = 126 - radius;
        }
        if ( y <= radius + 1 ) {
            y = radius + 1;
        } else if ( y >= 126 - radius ) {
            y = 126 - radius;
        }
    }
}

Run

Compile the program and copy the downloaded file to the mbed. Press the mbed's restart button to see the LCD display a little red ball. Pick up the breadboard and tilt it in different directions. You should see the ball start to move around!

mbed running LCD accelerometer demo

Concepts

We touched on a few important concepts in this tutorial that you may want to understand.

I2C

I2C (or "Inter-Integrated Circuit") is a communications protocol built by Philips in the 1980s. As I2C is a bus protocol, it allows for multiple masters and multiple devices to reside on the same bus and relies on addresses to communicate to specific devices. In our example, we used mbed's I2C library to talk to the accelerometer. To read more about the history of I2C, see this Wikipedia article.

Libraries

In the last tutorial, we imported an existing library. In this tutorial, we created a new library to make accessing the accelerometer easier. If you feel that you have a solid, well documented library that you want to share with others, read through mbed's Collaboration guide and specifically, how to write and publish a library.

Header Files

When we made our library, we created two files: a .h file and a .cpp file. The .h file is known as a header file. The header file contains declarations (variables, functions, classes, etc.) for other files in the program to use.

In our main file (main.cpp), we include all of the declarations from the header file (MMA8452Q.h) with the statement

language:c
#include "MMA8452Q.h"

This, in effect, copies everything from the header file to the #include line.

You will also notice that we included the same header file in the MMA8452Q.cpp file. We declare all of our classes, functions, and variables in the header file and define them in the .cpp file (read about the difference between declare and define).

When we compile our program, the compiler sees that we have declared the MMA8452Q class in the included header file, so we can use it in our main program. It will also compile the MMA8452Q.cpp file into an object file.

During the linking phase, the object files are combined into a single executable that is downloaded to your computer as a .bin file.

Floating Point

If you are carefully reviewing the example code, you might have noticed the keyword "float." If you have never dealt with floating point numbers, you might want to read up on how they work. Kip Irvine provides a great floating point tutorial. If you are interested in the history of floating point, see this Wikipedia article.

Going Further

We made an accelerometer do some cool stuff on a graphical display. If you are following the tutorials in order, you will need the LCD for one more!

Beyond the Tutorial

  • Can you make a digital bubble level? (Hint: think about how a bubble works and adjust how we move the circle)
  • Can you make the ball bounce off the sides? (Hint: look at how we make the ball "sit on edges" and make it bounce instead)
  • Can you make a basic ball-in-a-maze game? (Hint: look at how we draw shapes with the LCD library and how to make the ball sit on edges)

Digging Deeper