Library for driving the MMA8452 accelerometer over I2C
Dependents: MMA8452_Test MMA8452_Demo Dualing_Tanks IMU-Controlled_MP3_Player ... more
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
Generated on Tue Jul 12 2022 16:12:55 by 1.7.2