Works with 8g

Fork of MMA8452 by Ashley Mills

MMA8452.cpp

Committer:
ashleymills
Date:
2014-03-04
Revision:
11:dfd1e0afcb7b
Parent:
10:ca9ba7ad4e94
Child:
12:172540ff6b8b

File content as of revision 11:dfd1e0afcb7b:

// Author: Nicholas Herriot, Ashley Mills
/* Copyright (c) 2013 Vodafone, MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "MMA8452.h"
#include "mbed.h"

extern Serial pc;

// Connect module at I2C address using I2C port pins sda and scl
MMA8452::MMA8452(PinName sda, PinName scl, int frequency) : _i2c(sda, scl) , _frequency(frequency) {
   DBG("Creating MMA8452");
   _i2c.frequency(_frequency);
   
   // setup read and write addresses to avoid duplication
   _readAddress   = MMA8452_ADDRESS | 0x01;
   _writeAddress  = MMA8452_ADDRESS & 0xFE;
   DBG("Done");
}


// Destroys instance
MMA8452::~MMA8452() {}

// Setting the control register bit 1 to true to activate the MMA8452
int MMA8452::activate() {
    // perform write and return error code
    return logicalORRegister(CTRL_REG_1,MMA8452_ACTIVE_MASK);
}

// Setting the control register bit 1 to 0 to standby the MMA8452
int MMA8452::standby() {
    // perform write and return error code
    return logicalANDRegister(CTRL_REG_1,MMA8452_STANDBY_MASK);
}

// this reads a register, applies a bitmask with logical AND, sets a value with logical OR,
// and optionally goes into and out of standby at the beginning and end of the function respectively
int MMA8452::maskAndApplyRegister(char reg, char mask, char value, int toggleActivation) {
   if(toggleActivation) {
       if(standby()) {
          return 1;
       }
   }
   
   // read from register
   char oldValue = 0;
   if(readRegister(reg,&oldValue)) {
      return 1;
   }
   
   // apply bitmask
   oldValue &= mask;
   
   // set value
   oldValue |= value;
   
   // write back to register
   if(writeRegister(reg,oldValue)) {
      return 1;
   }
   
   if(toggleActivation) {
       if(activate()) {
          return 1;
       }
   }
   return 0;
}

int MMA8452::setDynamicRange(DynamicRange range, int toggleActivation) {
   return maskAndApplyRegister(
      MMA8452_XYZ_DATA_CFG,
      MMA8452_DYNAMIC_RANGE_MASK,
      range,
      toggleActivation
   );
}

int MMA8452::setDataRate(DataRateHz dataRate, int toggleActivation) {
   return maskAndApplyRegister(
       MMA8452_CTRL_REG_1,
       MMA8452_DATA_RATE_MASK,
       dataRate<<MMA8452_DATA_RATE_MASK_SHIFT,
       toggleActivation
   );
}

int MMA8452::setBitDepth(BitDepth depth,int toggleActivation) {
   return maskAndApplyRegister(
      MMA8452_CTRL_REG_1,
      MMA8452_BIT_DEPTH_MASK,
      depth<<MMA8452_BIT_DEPTH_MASK_SHIFT,
      toggleActivation
   );
}


// Get device ID 
int MMA8452::getDeviceID(char *dst)
{
    return readRegister(WHO_AM_I,dst);
}


int MMA8452::readRaw(char src, char *dst, int len) {
    // this is the register we want to get data from               
    char register_address[1];
    register_address[0] = src;
    
    if(_i2c.write(_writeAddress,register_address,1,true) == 0)
    {
        if(_i2c.read(_readAddress,dst,len)==0)
        {
           return 0;
        }
    }
    
    // failure case, zero array and return error
    for(int i=0; i<len; i++) {
       dst[i] = 0x00;
    }
    return 1;   
}

// Reads x data. This method reads two registers containing the x-axis values from the accelerometer. 
// It takes a 2 byte char array and copies the register values into the buffer. If it fails the char array
// is set to '0'. It returns '0' success '1' fail. This method does nothing to the registers - just returns
// the raw data.
//int MMA8452::read_x(int& xaxisLSB)
int MMA8452::readRawX(char *xaxis) {   
    return readRaw(OUT_X_MSB,xaxis,2);  
}


// Reads y data. This method reads two registers containing the x-axis values from the accelerometer. 
// It takes a 2 byte char array and copies the register values into the buffer. If it fails the char array
// is set to '0'. It returns '0' success '1' fail. This method does nothing to the registers - just returns
// the raw data.

int MMA8452::readRawY(char *yaxis) {
   return readRaw(OUT_Y_MSB,yaxis,2);
}



// Reads z data. This method reads two registers containing the x-axis values from the accelerometer. 
// It takes a 2 byte char array and copies the register values into the buffer. If it fails the char array
// is set to '0'. It returns '0' success '1' fail. This method does nothing to the registers - just returns
// the raw data.

int MMA8452::readRawZ(char *zaxis)
{
   return readRaw(OUT_Z_MSB,zaxis,2);
}



// Reads y data
int MMA8452::read_y()
{
    char mcu_address = (MMA8452_ADDRESS <<1);

    _i2c.start();                  // Start
    _i2c.write(mcu_address);              // A write to device 0x98
    _i2c.write(OUT_Y_MSB);             // Register to read
    _i2c.start();                  
    _i2c.write(mcu_address);              // Read from device 0x99
    int y = _i2c.read(0);         // Read the data
    _i2c.stop();
    
    return y; 

}

// Reads z data
int MMA8452::read_z()
{
    char mcu_address = (MMA8452_ADDRESS <<1);
    
    _i2c.start();                  // Start
    _i2c.write(mcu_address);              // A write to device 0x98
    _i2c.write(OUT_Z_MSB);             // Register to read
    _i2c.start();                  
    _i2c.write(mcu_address);              // Read from device 0x99
    int z = _i2c.read(0);         // Read the data
    _i2c.stop();
    
    return z;

}


MMA8452::DynamicRange MMA8452::getDynamicRange() {
   char rval = 0;
   if(readRegister(MMA8452_CTRL_REG_1,&rval)) {
      return MMA8452::DYNAMIC_RANGE_UNKNOWN;
   }
   rval &= (MMA8452_DYNAMIC_RANGE_MASK^0xFF);
   return (MMA8452::DynamicRange)rval;
}

MMA8452::DataRateHz MMA8452::getDataRate() {
   char rval = 0;
   if(readRegister(MMA8452_CTRL_REG_1,&rval)) {
      return MMA8452::RATE_UNKNOWN;
   }
   // logical AND with inverse of mask
   rval = rval&(MMA8452_DATA_RATE_MASK^0xFF);
   // shift back into position
   rval >>= MMA8452_DATA_RATE_MASK_SHIFT;
   return (MMA8452::DataRateHz)rval;
}

// Reads xyz
int MMA8452::readRawXYZ(char *x, char *y, char *z) 
{
    
    
    char mcu_address = (MMA8452_ADDRESS <<1);
    char register_buffer[6] ={0,0,0,0,0,0};
    const char Addr_X = OUT_X_MSB;
    _i2c.write(mcu_address);              // A write to device 0x98
    _i2c.write(MMA8452_ADDRESS, &Addr_X, 1);         // Pointer to the OUT_X_MSB register
    
    if(_i2c.write(mcu_address,&Addr_X,1) == 0)
    {
        if(_i2c.read(mcu_address,register_buffer,6) == 0)
        {
            *x = register_buffer[1];
            *y = register_buffer[3];
            *z = register_buffer[5];
            return 0;           // yahoooooo
        }
        else
        {
            return 1;           // failed oh nooo!
        }    
    }
    else
    {
        return 1;               // failed oh nooo!
    }

}

// apply an AND mask to a register. read register value, apply mask, write it back
int MMA8452::logicalANDRegister(char addr, char mask) {
   char value = 0;
   // read register value
   if(readRegister(addr,&value)) {
      return 0;
   }
   // apply mask
   value &= mask;
   return writeRegister(addr,value);
}


// apply an OR mask to a register. read register value, apply mask, write it back
int MMA8452::logicalORRegister(char addr, char mask) {
   char value = 0;
   // read register value
   if(readRegister(addr,&value)) {
      return 0;
   }
   // apply mask
   value |= mask;
   return writeRegister(addr,value);
}


// apply an OR mask to a register. read register value, apply mask, write it back
int MMA8452::logicalXORRegister(char addr, char mask) {
   char value = 0;
   // read register value
   if(readRegister(addr,&value)) {
      return 0;
   }
   // apply mask
   value ^= mask;
   return writeRegister(addr,value);
}

// Write register (The device must be placed in Standby Mode to change the value of the registers) 
int MMA8452::writeRegister(char addr, char data) {
    // what this actually does is the following
    // 1. tell I2C bus to start transaction
    // 2. tell slave we want to write (slave address & write flag)
    // 3. send the write address
    // 4. send the data to write
    // 5. tell I2C bus to end transaction

    // we can wrap this up in the I2C library write function
    char buf[2] = {0,0};
    buf[0] = addr;
    buf[1] = data;
    return _i2c.write(MMA8452_ADDRESS, buf,2);
    // note, could also do return writeRegister(addr,&data,1);
}


int MMA8452::writeRegister(char addr, char *data, int nbytes) {
    // writing multiple bytes is a little bit annoying because
    // the I2C library doesn't support sending the address separately
    // so we just do it manually
    
    // 1. tell I2C bus to start transaction
    _i2c.start();
    // 2. tell slave we want to write (slave address & write flag)
    if(_i2c.write(_writeAddress)!=1) {
       return 1;
    }
    // 3. send the write address
    if(_i2c.write(addr)!=1) {
       return 1;
    }
    // 4. send the data to write
    for(int i=0; i<nbytes; i++) {
       if(_i2c.write(data[i])!=1) {
          return 1;
       }
    }
    // 5. tell I2C bus to end transaction
    _i2c.stop();
    return 0;
}

int MMA8452::readRegister(char addr, char *dst, int nbytes) {
    // this is a bit odd, but basically proceeds like this
    // 1. Send a start command
    // 2. Tell the slave we want to write (slave address & write flag)
    // 3. Send the address of the register (addr)
    // 4. Send another start command to delineate read portion
    // 5. Tell the slave we want to read (slave address & read flag)
    // 6. Read the register value bytes
    // 7. Send a stop command
    
    // we can wrap this process in the I2C library read and write commands
    if(_i2c.write(MMA8452_ADDRESS,&addr,1,true)) {
       return 1;
    }
    return _i2c.read(MMA8452_ADDRESS,dst,nbytes);
}

// most registers are 1 byte, so here is a convenience function
int MMA8452::readRegister(char addr, char *dst) {
    return readRegister(addr,dst,1);
}

void MMA8452::debugRegister(char reg) {
   // get register value
   char v = 0;
   if(readRegister(reg,&v)) {
      DBG("Error reading control register");
      return;
   }
   // print out details
   switch(reg) {
      case MMA8452_CTRL_REG_1:
         DBG("CTRL_REG_1 has value: 0x%x",v);
         DBG(" 7  ALSP_RATE_1: %d",(v&0x80)>>7);
         DBG(" 6  ALSP_RATE_0: %d",(v&0x40)>>6);
         DBG(" 5  DR2: %d",        (v&0x20)>>5);
         DBG(" 4  DR1: %d",        (v&0x10)>>4);
         DBG(" 3  DR0: %d",        (v&0x08)>>3);
         DBG(" 2  LNOISE: %d",     (v&0x04)>>2);
         DBG(" 1  FREAD: %d",      (v&0x02)>>1);
         DBG(" 0  ACTIVE: %d",     (v&0x01));
      break;
        
      case MMA8452_XYZ_DATA_CFG:
         DBG("XYZ_DATA_CFG has value: 0x%x",v);
         DBG(" 7  Unused: %d", (v&0x80)>>7);
         DBG(" 6  0: %d",      (v&0x40)>>6);
         DBG(" 5  0: %d",      (v&0x20)>>5);
         DBG(" 4  HPF_Out: %d",(v&0x10)>>4);
         DBG(" 3  0: %d",      (v&0x08)>>3);
         DBG(" 2  0: %d",      (v&0x04)>>2);
         DBG(" 1  FS1: %d",    (v&0x02)>>1);
         DBG(" 0  FS0: %d",    (v&0x01));
         switch(v&0x03) {
            case 0:
               DBG("Dynamic range: 2G");
            break;
            case 1:
               DBG("Dynamic range: 4G");
            break;
            case 2:
               DBG("Dynamic range: 8G");
            break;
            default:
               DBG("Unknown dynamic range");
            break;
         }
      break;
      
      default:
         DBG("Unknown register address: 0x%x",reg);
      break;
   }
}