Library for driving the MMA8452 accelerometer over I2C

Dependents:   MMA8452_Test MMA8452_Demo Dualing_Tanks IMU-Controlled_MP3_Player ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MMA8452.cpp Source File

MMA8452.cpp

00001 // Authors: Ashley Mills, Nicholas Herriot
00002 /* Copyright (c) 2013 Vodafone, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 #include "MMA8452.h"
00021 #include "mbed.h"
00022 
00023 #ifdef MMA8452_DEBUG
00024 // you need to define Serial pc(USBTX,USBRX) somewhere for the below line to make sense
00025 extern Serial pc;
00026 #define MMA8452_DBG(...) pc.printf(__VA_ARGS__); pc.printf("\r\n");
00027 #else
00028 #define MMA8452_DBG(...)
00029 #endif
00030 
00031 // Connect module at I2C address using I2C port pins sda and scl
00032 MMA8452::MMA8452(PinName sda, PinName scl, int frequency) : _i2c(sda, scl) , _frequency(frequency) {
00033    MMA8452_DBG("Creating MMA8452");
00034    
00035    // set I2C frequency
00036    _i2c.frequency(_frequency);
00037    
00038    // setup read and write addresses for convenience
00039    _readAddress   = MMA8452_ADDRESS | 0x01;
00040    _writeAddress  = MMA8452_ADDRESS & 0xFE;
00041    
00042    // set some defaults
00043    _bitDepth = BIT_DEPTH_UNKNOWN;
00044    setBitDepth(BIT_DEPTH_12);
00045    _dynamicRange = DYNAMIC_RANGE_UNKNOWN;
00046    setDynamicRange(DYNAMIC_RANGE_2G);
00047    
00048    MMA8452_DBG("Done");
00049 }
00050 
00051 
00052 // Destroys instance
00053 MMA8452::~MMA8452() {}
00054 
00055 // Setting the control register bit 1 to true to activate the MMA8452
00056 int MMA8452::activate() {
00057     // perform write and return error code
00058     return logicalORRegister(MMA8452_CTRL_REG_1,MMA8452_ACTIVE_MASK);
00059 }
00060 
00061 // Setting the control register bit 1 to 0 to standby the MMA8452
00062 int MMA8452::standby() {
00063     // perform write and return error code
00064     return logicalANDRegister(MMA8452_CTRL_REG_1,MMA8452_STANDBY_MASK);
00065 }
00066 
00067 // this reads a register, applies a bitmask with logical AND, sets a value with logical OR,
00068 // and optionally goes into and out of standby at the beginning and end of the function respectively
00069 int MMA8452::maskAndApplyRegister(char reg, char mask, char value, int toggleActivation) {
00070    if(toggleActivation) {
00071        if(standby()) {
00072           return 1;
00073        }
00074    }
00075    
00076    // read from register
00077    char oldValue = 0;
00078    if(readRegister(reg,&oldValue)) {
00079       return 1;
00080    }
00081    
00082    // apply bitmask
00083    oldValue &= mask;
00084    
00085    // set value
00086    oldValue |= value;
00087    
00088    // write back to register
00089    if(writeRegister(reg,oldValue)) {
00090       return 1;
00091    }
00092    
00093    if(toggleActivation) {
00094        if(activate()) {
00095           return 1;
00096        }
00097    }
00098    return 0;
00099 }
00100 
00101 int MMA8452::setDynamicRange(DynamicRange range, int toggleActivation) {
00102    _dynamicRange = range;
00103    return maskAndApplyRegister(
00104       MMA8452_XYZ_DATA_CFG,
00105       MMA8452_DYNAMIC_RANGE_MASK,
00106       range,
00107       toggleActivation
00108    );
00109 }
00110 
00111 int MMA8452::setDataRate(DataRateHz dataRate, int toggleActivation) {
00112    return maskAndApplyRegister(
00113        MMA8452_CTRL_REG_1,
00114        MMA8452_DATA_RATE_MASK,
00115        dataRate<<MMA8452_DATA_RATE_MASK_SHIFT,
00116        toggleActivation
00117    );
00118 }
00119 
00120 int MMA8452::setBitDepth(BitDepth depth,int toggleActivation) {
00121    _bitDepth = depth;
00122    return maskAndApplyRegister(
00123       MMA8452_CTRL_REG_1,
00124       MMA8452_BIT_DEPTH_MASK,
00125       depth<<MMA8452_BIT_DEPTH_MASK_SHIFT,
00126       toggleActivation
00127    );
00128 }
00129 
00130 char MMA8452::getMaskedRegister(int addr, char mask) {
00131    char rval = 0;
00132    if(readRegister(addr,&rval)) {
00133       return 0;
00134    }
00135    return (rval&mask);
00136 }
00137 
00138 int MMA8452::isXYZReady() {
00139    return getMaskedRegister(MMA8452_STATUS,MMA8452_STATUS_ZYXDR_MASK)>0;
00140 }
00141 
00142 int MMA8452::isXReady() {
00143    return getMaskedRegister(MMA8452_STATUS,MMA8452_STATUS_XDR_MASK)>0;
00144 }
00145 
00146 int MMA8452::isYReady() {
00147    return getMaskedRegister(MMA8452_STATUS,MMA8452_STATUS_YDR_MASK)>0;
00148 }
00149 
00150 int MMA8452::isZReady() {
00151    return getMaskedRegister(MMA8452_STATUS,MMA8452_STATUS_ZDR_MASK)>0;
00152 }
00153 
00154 
00155 int MMA8452::getDeviceID(char *dst) {
00156    return readRegister(MMA8452_WHO_AM_I,dst);
00157 }
00158 
00159 int MMA8452::getStatus(char* dst) {
00160    return readRegister(MMA8452_STATUS,dst);
00161 }
00162 
00163 MMA8452::DynamicRange MMA8452::getDynamicRange() {
00164    char rval = 0;
00165    if(readRegister(MMA8452_XYZ_DATA_CFG,&rval)) {
00166       return MMA8452::DYNAMIC_RANGE_UNKNOWN;
00167    }
00168    rval &= (MMA8452_DYNAMIC_RANGE_MASK^0xFF);
00169    return (MMA8452::DynamicRange)rval;
00170 }
00171 
00172 MMA8452::DataRateHz MMA8452::getDataRate() {
00173    char rval = 0;
00174    if(readRegister(MMA8452_CTRL_REG_1,&rval)) {
00175       return MMA8452::RATE_UNKNOWN;
00176    }
00177    // logical AND with inverse of mask
00178    rval = rval&(MMA8452_DATA_RATE_MASK^0xFF);
00179    // shift back into position
00180    rval >>= MMA8452_DATA_RATE_MASK_SHIFT;
00181    return (MMA8452::DataRateHz)rval;
00182 }
00183 
00184 // Reads xyz
00185 int MMA8452::readXYZRaw(char *dst) {
00186    if(_bitDepth==BIT_DEPTH_UNKNOWN) {
00187       return 1;
00188    }
00189    int readLen = 3;
00190    if(_bitDepth==BIT_DEPTH_12) {
00191       readLen = 6;
00192    }
00193    return readRegister(MMA8452_OUT_X_MSB,dst,readLen);
00194 }
00195 
00196 int MMA8452::readXRaw(char *dst) {
00197    if(_bitDepth==BIT_DEPTH_UNKNOWN) {
00198       return 1;
00199    }
00200    int readLen = 1;
00201    if(_bitDepth==BIT_DEPTH_12) {
00202       readLen = 2;
00203    }
00204    return readRegister(MMA8452_OUT_X_MSB,dst,readLen);
00205 }
00206 
00207 int MMA8452::readYRaw(char *dst) {
00208    if(_bitDepth==BIT_DEPTH_UNKNOWN) {
00209       return 1;
00210    }
00211    int readLen = 1;
00212    if(_bitDepth==BIT_DEPTH_12) {
00213       readLen = 2;
00214    }
00215    return readRegister(MMA8452_OUT_Y_MSB,dst,readLen);
00216 }
00217 
00218 int MMA8452::readZRaw(char *dst) {
00219    if(_bitDepth==BIT_DEPTH_UNKNOWN) {
00220       return 1;
00221    }
00222    int readLen = 1;
00223    if(_bitDepth==BIT_DEPTH_12) {
00224       readLen = 2;
00225    }
00226    return readRegister(MMA8452_OUT_Z_MSB,dst,readLen);
00227 }
00228 
00229 int MMA8452::readXYZCounts(int *x, int *y, int *z) {
00230    char buf[6];
00231    if(readXYZRaw((char*)&buf)) {
00232        return 1;
00233    }
00234    if(_bitDepth==BIT_DEPTH_12) {
00235      *x = twelveBitToSigned(&buf[0]);
00236      *y = twelveBitToSigned(&buf[2]);
00237      *z = twelveBitToSigned(&buf[4]);
00238    } else {
00239      *x = eightBitToSigned(&buf[0]);
00240      *y = eightBitToSigned(&buf[1]);
00241      *z = eightBitToSigned(&buf[2]);
00242    }
00243    
00244    return 0;
00245 }
00246 
00247 int MMA8452::readXCount(int *x) {
00248    char buf[2];
00249    if(readXRaw((char*)&buf)) {
00250        return 1;
00251    }
00252    if(_bitDepth==BIT_DEPTH_12) {
00253      *x = twelveBitToSigned(&buf[0]);
00254    } else {
00255      *x = eightBitToSigned(&buf[0]);
00256    }
00257    return 0;
00258 }
00259 
00260 int MMA8452::readYCount(int *y) {
00261    char buf[2];
00262    if(readYRaw((char*)&buf)) {
00263        return 1;
00264    }
00265    if(_bitDepth==BIT_DEPTH_12) {
00266      *y = twelveBitToSigned(&buf[0]);
00267    } else {
00268      *y = eightBitToSigned(&buf[0]);
00269    }
00270    return 0;
00271 }
00272 
00273 int MMA8452::readZCount(int *z) {
00274    char buf[2];
00275    if(readZRaw((char*)&buf)) {
00276        return 1;
00277    }
00278    if(_bitDepth==BIT_DEPTH_12) {
00279      *z = twelveBitToSigned(&buf[0]);
00280    } else {
00281      *z = eightBitToSigned(&buf[0]);
00282    }
00283    return 0;
00284 }
00285 
00286 double MMA8452::convertCountToGravity(int count, int countsPerG) {
00287    return (double)count/(double)countsPerG;
00288 }
00289 
00290 int MMA8452::getCountsPerG() {
00291  // assume starting with DYNAMIC_RANGE_2G and BIT_DEPTH_12
00292    int countsPerG = 1024;
00293    if(_bitDepth==BIT_DEPTH_8) {
00294       countsPerG = 64;
00295    }
00296    switch(_dynamicRange) {
00297       case DYNAMIC_RANGE_4G:
00298          countsPerG /= 2;
00299       break;
00300       case DYNAMIC_RANGE_8G:
00301          countsPerG /= 4;
00302       break;
00303    }
00304    return countsPerG;
00305 }
00306 
00307 int MMA8452::readXYZGravity(double *x, double *y, double *z) {
00308    int xCount = 0, yCount = 0, zCount = 0;
00309    if(readXYZCounts(&xCount,&yCount,&zCount)) {
00310       return 1;
00311    }
00312    int countsPerG = getCountsPerG();
00313    
00314    *x = convertCountToGravity(xCount,countsPerG);
00315    *y = convertCountToGravity(yCount,countsPerG);
00316    *z = convertCountToGravity(zCount,countsPerG);
00317    return 0;
00318 }
00319 
00320 int MMA8452::readXGravity(double *x) {
00321    int xCount = 0;
00322    if(readXCount(&xCount)) {
00323       return 1;
00324    }
00325    int countsPerG = getCountsPerG();
00326    
00327    *x = convertCountToGravity(xCount,countsPerG);
00328    return 0;
00329 }
00330 
00331 int MMA8452::readYGravity(double *y) {
00332    int yCount = 0;
00333    if(readYCount(&yCount)) {
00334       return 1;
00335    }
00336    int countsPerG = getCountsPerG();
00337    
00338    *y = convertCountToGravity(yCount,countsPerG);
00339    return 0;
00340 }
00341 
00342 int MMA8452::readZGravity(double *z) {
00343    int zCount = 0;
00344    if(readZCount(&zCount)) {
00345       return 1;
00346    }
00347    int countsPerG = getCountsPerG();
00348    
00349    *z = convertCountToGravity(zCount,countsPerG);
00350    return 0;
00351 }
00352 
00353 // apply an AND mask to a register. read register value, apply mask, write it back
00354 int MMA8452::logicalANDRegister(char addr, char mask) {
00355    char value = 0;
00356    // read register value
00357    if(readRegister(addr,&value)) {
00358       return 0;
00359    }
00360    // apply mask
00361    value &= mask;
00362    return writeRegister(addr,value);
00363 }
00364 
00365 
00366 // apply an OR mask to a register. read register value, apply mask, write it back
00367 int MMA8452::logicalORRegister(char addr, char mask) {
00368    char value = 0;
00369    // read register value
00370    if(readRegister(addr,&value)) {
00371       return 0;
00372    }
00373    // apply mask
00374    value |= mask;
00375    return writeRegister(addr,value);
00376 }
00377 
00378 // apply an OR mask to a register. read register value, apply mask, write it back
00379 int MMA8452::logicalXORRegister(char addr, char mask) {
00380    char value = 0;
00381    // read register value
00382    if(readRegister(addr,&value)) {
00383       return 0;
00384    }
00385    // apply mask
00386    value ^= mask;
00387    return writeRegister(addr,value);
00388 }
00389 
00390 // Write register (The device must be placed in Standby Mode to change the value of the registers) 
00391 int MMA8452::writeRegister(char addr, char data) {
00392     // what this actually does is the following
00393     // 1. tell I2C bus to start transaction
00394     // 2. tell slave we want to write (slave address & write flag)
00395     // 3. send the write address
00396     // 4. send the data to write
00397     // 5. tell I2C bus to end transaction
00398 
00399     // we can wrap this up in the I2C library write function
00400     char buf[2] = {0,0};
00401     buf[0] = addr;
00402     buf[1] = data;
00403     return _i2c.write(MMA8452_ADDRESS, buf,2);
00404     // note, could also do return writeRegister(addr,&data,1);
00405 }
00406 
00407 int MMA8452::eightBitToSigned(char *buf) {
00408    return (int8_t)*buf;
00409 }
00410 
00411 int MMA8452::twelveBitToSigned(char *buf) {
00412    // cheat by using the int16_t internal type
00413    // all we need to do is convert to little-endian format and shift right
00414    int16_t x = 0;
00415    ((char*)&x)[1] = buf[0];
00416    ((char*)&x)[0] = buf[1];
00417    // note this only works because the below is an arithmetic right shift
00418    return x>>4; 
00419 }
00420 
00421 int MMA8452::writeRegister(char addr, char *data, int nbytes) {
00422     // writing multiple bytes is a little bit annoying because
00423     // the I2C library doesn't support sending the address separately
00424     // so we just do it manually
00425     
00426     // 1. tell I2C bus to start transaction
00427     _i2c.start();
00428     // 2. tell slave we want to write (slave address & write flag)
00429     if(_i2c.write(_writeAddress)!=1) {
00430        return 1;
00431     }
00432     // 3. send the write address
00433     if(_i2c.write(addr)!=1) {
00434        return 1;
00435     }
00436     // 4. send the data to write
00437     for(int i=0; i<nbytes; i++) {
00438        if(_i2c.write(data[i])!=1) {
00439           return 1;
00440        }
00441     }
00442     // 5. tell I2C bus to end transaction
00443     _i2c.stop();
00444     return 0;
00445 }
00446 
00447 int MMA8452::readRegister(char addr, char *dst, int nbytes) {
00448     // this is a bit odd, but basically proceeds like this
00449     // 1. Send a start command
00450     // 2. Tell the slave we want to write (slave address & write flag)
00451     // 3. Send the address of the register (addr)
00452     // 4. Send another start command to delineate read portion
00453     // 5. Tell the slave we want to read (slave address & read flag)
00454     // 6. Read the register value bytes
00455     // 7. Send a stop command
00456     
00457     // we can wrap this process in the I2C library read and write commands
00458     if(_i2c.write(MMA8452_ADDRESS,&addr,1,true)) {
00459        return 1;
00460     }
00461     return _i2c.read(MMA8452_ADDRESS,dst,nbytes);
00462 }
00463 
00464 // most registers are 1 byte, so here is a convenience function
00465 int MMA8452::readRegister(char addr, char *dst) {
00466     return readRegister(addr,dst,1);
00467 }
00468 
00469 MMA8452::BitDepth MMA8452::getBitDepth() {
00470    return _bitDepth;
00471 }
00472 
00473 #ifdef MMA8452_DEBUG
00474 void MMA8452::debugRegister(char reg) {
00475    // get register value
00476    char v = 0;
00477    if(readRegister(reg,&v)) {
00478       MMA8452_DBG("Error reading specified register");
00479       return;
00480    }
00481    // print out details
00482    switch(reg) {
00483       case MMA8452_CTRL_REG_1:
00484          MMA8452_DBG("CTRL_REG_1 has value: 0x%x",v);
00485          MMA8452_DBG(" 7  ALSP_RATE_1: %d",(v&0x80)>>7);
00486          MMA8452_DBG(" 6  ALSP_RATE_0: %d",(v&0x40)>>6);
00487          MMA8452_DBG(" 5  DR2: %d",        (v&0x20)>>5);
00488          MMA8452_DBG(" 4  DR1: %d",        (v&0x10)>>4);
00489          MMA8452_DBG(" 3  DR0: %d",        (v&0x08)>>3);
00490          MMA8452_DBG(" 2  LNOISE: %d",     (v&0x04)>>2);
00491          MMA8452_DBG(" 1  FREAD: %d",      (v&0x02)>>1);
00492          MMA8452_DBG(" 0  ACTIVE: %d",     (v&0x01));
00493       break;
00494         
00495       case MMA8452_XYZ_DATA_CFG:
00496          MMA8452_DBG("XYZ_DATA_CFG has value: 0x%x",v);
00497          MMA8452_DBG(" 7  Unused: %d", (v&0x80)>>7);
00498          MMA8452_DBG(" 6  0: %d",      (v&0x40)>>6);
00499          MMA8452_DBG(" 5  0: %d",      (v&0x20)>>5);
00500          MMA8452_DBG(" 4  HPF_Out: %d",(v&0x10)>>4);
00501          MMA8452_DBG(" 3  0: %d",      (v&0x08)>>3);
00502          MMA8452_DBG(" 2  0: %d",      (v&0x04)>>2);
00503          MMA8452_DBG(" 1  FS1: %d",    (v&0x02)>>1);
00504          MMA8452_DBG(" 0  FS0: %d",    (v&0x01));
00505          switch(v&0x03) {
00506             case 0:
00507                MMA8452_DBG("Dynamic range: 2G");
00508             break;
00509             case 1:
00510                MMA8452_DBG("Dynamic range: 4G");
00511             break;
00512             case 2:
00513                MMA8452_DBG("Dynamic range: 8G");
00514             break;
00515             default:
00516                MMA8452_DBG("Unknown dynamic range");
00517             break;
00518          }
00519       break;
00520       
00521       case MMA8452_STATUS:
00522          MMA8452_DBG("STATUS has value: 0x%x",v);
00523          MMA8452_DBG(" 7  ZYXOW: %d",(v&0x80)>>7);
00524          MMA8452_DBG(" 6  ZOW: %d",  (v&0x40)>>6);
00525          MMA8452_DBG(" 5  YOW: %d",  (v&0x20)>>5);
00526          MMA8452_DBG(" 4  XOW: %d",  (v&0x10)>>4);
00527          MMA8452_DBG(" 3  ZYXDR: %d",(v&0x08)>>3);
00528          MMA8452_DBG(" 2  ZDR: %d",  (v&0x04)>>2);
00529          MMA8452_DBG(" 1  YDR: %d",  (v&0x02)>>1);
00530          MMA8452_DBG(" 0  XDR: %d",  (v&0x01));
00531       break;
00532       
00533       default:
00534          MMA8452_DBG("Unknown register address: 0x%x",reg);
00535       break;
00536    }
00537 }
00538 #endif