Home | History | Annotate | Download | only in mpu9150
      1 /*
      2  * Author: Jon Trulson <jtrulson (at) ics.com>
      3  * Copyright (c) 2015 Intel Corporation.
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining
      6  * a copy of this software and associated documentation files (the
      7  * "Software"), to deal in the Software without restriction, including
      8  * without limitation the rights to use, copy, modify, merge, publish,
      9  * distribute, sublicense, and/or sell copies of the Software, and to
     10  * permit persons to whom the Software is furnished to do so, subject to
     11  * the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be
     14  * included in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 #include <unistd.h>
     26 #include <iostream>
     27 #include <string.h>
     28 
     29 #include "mpu60x0.h"
     30 
     31 using namespace upm;
     32 using namespace std;
     33 
     34 
     35 MPU60X0::MPU60X0(int bus, uint8_t address) :
     36   m_i2c(bus), m_gpioIRQ(0)
     37 {
     38   m_addr = address;
     39 
     40   m_accelX = 0.0;
     41   m_accelY = 0.0;
     42   m_accelZ = 0.0;
     43 
     44   m_gyroX = 0.0;
     45   m_gyroY = 0.0;
     46   m_gyroZ = 0.0;
     47 
     48   m_temp = 0.0;
     49 
     50   m_accelScale = 1.0;
     51   m_gyroScale = 1.0;
     52 
     53   mraa::Result rv;
     54   if ( (rv = m_i2c.address(m_addr)) != mraa::SUCCESS)
     55     {
     56       throw std::runtime_error(std::string(__FUNCTION__) +
     57                                ": I2c.address() failed");
     58       return;
     59     }
     60 }
     61 
     62 MPU60X0::~MPU60X0()
     63 {
     64   uninstallISR();
     65 }
     66 
     67 bool MPU60X0::init()
     68 {
     69   // first, take the device out of sleep mode
     70   if (!setSleep(false))
     71     {
     72       throw std::runtime_error(std::string(__FUNCTION__) +
     73                                ": Unable to wake up device");
     74       return false;
     75     }
     76 
     77   // set the clock source to use the gyroscope PLL rather than the
     78   // internal clock for stability
     79   if (!setClockSource(PLL_XG))
     80     {
     81       throw std::runtime_error(std::string(__FUNCTION__) +
     82                                ": Unable to set clock source");
     83       return false;
     84     }
     85 
     86   usleep(5000);
     87 
     88   // enable temperature measurement (default on power up, but let's be sure)
     89   enableTemperatureSensor(true);
     90 
     91   // set the gyro and accel scale bits to reasonable values
     92   setGyroscopeScale(FS_500);
     93   setAccelerometerScale(AFS_2);
     94 
     95   // enable the DLPF
     96   setDigitalLowPassFilter(DLPF_94_98);
     97 
     98   // let things stabilize...
     99   usleep(100000);
    100 
    101   return true;
    102 }
    103 
    104 
    105 void MPU60X0::update()
    106 {
    107   // read all of the sensor registers - accel, gyro, and temp
    108   uint8_t buffer[14];
    109 
    110   memset(buffer, 0, 14);
    111   readRegs(REG_ACCEL_XOUT_H, buffer, 14);
    112 
    113   int16_t ax, ay, az;
    114   int16_t temp;
    115   int16_t gx, gy, gz;
    116 
    117   ax =  ( (buffer[0] << 8) | buffer[1] );
    118   ay =  ( (buffer[2] << 8) | buffer[3] );
    119   az =  ( (buffer[4] << 8) | buffer[5] );
    120 
    121   temp = ( (buffer[6] << 8) | buffer[7] );
    122 
    123   gx =  ( (buffer[8] << 8) | buffer[9] );
    124   gy =  ( (buffer[10] << 8) | buffer[11] );
    125   gz =  ( (buffer[12] << 8) | buffer[13] );
    126 
    127   // now stash them
    128   m_accelX = float(ax);
    129   m_accelY = float(ay);
    130   m_accelZ = float(az);
    131 
    132   m_temp = float(temp);
    133 
    134   m_gyroX = float(gx);
    135   m_gyroY = float(gy);
    136   m_gyroZ = float(gz);
    137 }
    138 
    139 uint8_t MPU60X0::readReg(uint8_t reg)
    140 {
    141   return m_i2c.readReg(reg);
    142 }
    143 
    144 void MPU60X0::readRegs(uint8_t reg, uint8_t *buffer, int len)
    145 {
    146   m_i2c.readBytesReg(reg, buffer, len);
    147 }
    148 
    149 bool MPU60X0::writeReg(uint8_t reg, uint8_t val)
    150 {
    151   mraa::Result rv;
    152   if ((rv = m_i2c.writeReg(reg, val)) != mraa::SUCCESS)
    153     {
    154       throw std::runtime_error(std::string(__FUNCTION__) +
    155                                ": I2c.writeReg() failed");
    156       return false;
    157     }
    158 
    159   return true;
    160 }
    161 
    162 bool MPU60X0::setSleep(bool enable)
    163 {
    164   uint8_t reg = readReg(REG_PWR_MGMT_1);
    165 
    166   if (enable)
    167     reg |= PWR_SLEEP;
    168   else
    169     reg &= ~PWR_SLEEP;
    170 
    171   return writeReg(REG_PWR_MGMT_1, reg);
    172 }
    173 
    174 bool MPU60X0::setClockSource(CLKSEL_T clk)
    175 {
    176   uint8_t reg = readReg(REG_PWR_MGMT_1);
    177 
    178   reg &= ~(_CLKSEL_MASK << _CLKSEL_SHIFT);
    179 
    180   reg |= (clk << _CLKSEL_SHIFT);
    181 
    182   return writeReg(REG_PWR_MGMT_1, reg);
    183 }
    184 
    185 bool MPU60X0::setGyroscopeScale(FS_SEL_T scale)
    186 {
    187   uint8_t reg = readReg(REG_GYRO_CONFIG);
    188 
    189   reg &= ~(_FS_SEL_MASK << _FS_SEL_SHIFT);
    190 
    191   reg |= (scale << _FS_SEL_SHIFT);
    192 
    193   if (!writeReg(REG_GYRO_CONFIG, reg))
    194     {
    195       return false;
    196     }
    197 
    198   // store scaling factor
    199 
    200   switch (scale)
    201     {
    202     case FS_250:
    203       m_gyroScale = 131.0;
    204       break;
    205 
    206     case FS_500:
    207       m_gyroScale = 65.5;
    208       break;
    209 
    210     case FS_1000:
    211       m_gyroScale = 32.8;
    212       break;
    213 
    214     case FS_2000:
    215       m_gyroScale = 16.4;
    216       break;
    217 
    218     default: // should never occur, but...
    219       m_gyroScale = 1.0;        // set a safe, though incorrect value
    220       throw std::logic_error(string(__FUNCTION__) +
    221                              ": internal error, unsupported scale");
    222       break;
    223     }
    224 
    225   return true;
    226 }
    227 
    228 bool MPU60X0::setAccelerometerScale(AFS_SEL_T scale)
    229 {
    230   uint8_t reg = readReg(REG_ACCEL_CONFIG);
    231 
    232   reg &= ~(_AFS_SEL_MASK << _AFS_SEL_SHIFT);
    233 
    234   reg |= (scale << _AFS_SEL_SHIFT);
    235 
    236   if (!writeReg(REG_ACCEL_CONFIG, reg))
    237     {
    238       return false;
    239     }
    240 
    241   // store scaling factor
    242 
    243   switch (scale)
    244     {
    245     case AFS_2:
    246       m_accelScale = 16384.0;
    247       break;
    248 
    249     case AFS_4:
    250       m_accelScale = 8192.0;
    251       break;
    252 
    253     case AFS_8:
    254       m_accelScale = 4096.0;
    255       break;
    256 
    257     case AFS_16:
    258       m_accelScale = 2048.0;
    259       break;
    260 
    261     default: // should never occur, but...
    262       m_accelScale = 1.0;        // set a safe, though incorrect value
    263       throw std::logic_error(string(__FUNCTION__) +
    264                              ": internal error, unsupported scale");
    265       break;
    266     }
    267 
    268   return true;
    269 }
    270 
    271 bool MPU60X0::setDigitalLowPassFilter(DLPF_CFG_T dlp)
    272 {
    273   uint8_t reg = readReg(REG_CONFIG);
    274 
    275   reg &= ~(_CONFIG_DLPF_MASK << _CONFIG_DLPF_SHIFT);
    276 
    277   reg |= (dlp << _CONFIG_DLPF_SHIFT);
    278 
    279   return writeReg(REG_CONFIG, reg);
    280 }
    281 
    282 bool MPU60X0::setSampleRateDivider(uint8_t div)
    283 {
    284   return writeReg(REG_SMPLRT_DIV, div);
    285 }
    286 
    287 uint8_t MPU60X0::getSampleRateDivider()
    288 {
    289   return readReg(REG_SMPLRT_DIV);
    290 }
    291 
    292 void MPU60X0::getAccelerometer(float *x, float *y, float *z)
    293 {
    294   if (x)
    295     *x = m_accelX / m_accelScale;
    296 
    297   if (y)
    298     *y = m_accelY / m_accelScale;
    299 
    300   if (z)
    301     *z = m_accelZ / m_accelScale;
    302 }
    303 
    304 void MPU60X0::getGyroscope(float *x, float *y, float *z)
    305 {
    306   if (x)
    307     *x = m_gyroX / m_gyroScale;
    308 
    309   if (y)
    310     *y = m_gyroY / m_gyroScale;
    311 
    312   if (z)
    313     *z = m_gyroZ / m_gyroScale;
    314 }
    315 
    316 float MPU60X0::getTemperature()
    317 {
    318   // this equation is taken from the datasheet
    319   return (m_temp / 340.0) + 36.53;
    320 }
    321 
    322 bool MPU60X0::enableTemperatureSensor(bool enable)
    323 {
    324   uint8_t reg = readReg(REG_PWR_MGMT_1);
    325 
    326   if (enable)
    327     reg &= ~TEMP_DIS;
    328   else
    329     reg |= TEMP_DIS;
    330 
    331   return writeReg(REG_PWR_MGMT_1, reg);
    332 }
    333 
    334 bool MPU60X0::setExternalSync(EXT_SYNC_SET_T val)
    335 {
    336   uint8_t reg = readReg(REG_CONFIG);
    337 
    338   reg &= ~(_CONFIG_EXT_SYNC_SET_MASK << _CONFIG_EXT_SYNC_SET_SHIFT);
    339 
    340   reg |= (val << _CONFIG_EXT_SYNC_SET_SHIFT);
    341 
    342   return writeReg(REG_CONFIG, reg);
    343 }
    344 
    345 bool MPU60X0::enableI2CBypass(bool enable)
    346 {
    347   uint8_t reg = readReg(REG_INT_PIN_CFG);
    348 
    349   if (enable)
    350     reg |= I2C_BYPASS_ENABLE;
    351   else
    352     reg &= ~I2C_BYPASS_ENABLE;
    353 
    354   return writeReg(REG_INT_PIN_CFG, reg);
    355 }
    356 
    357 bool MPU60X0::setMotionDetectionThreshold(uint8_t thr)
    358 {
    359   return writeReg(REG_MOT_THR, thr);
    360 }
    361 
    362 uint8_t MPU60X0::getInterruptStatus()
    363 {
    364   return readReg(REG_INT_STATUS);
    365 }
    366 
    367 bool MPU60X0::setInterruptEnables(uint8_t enables)
    368 {
    369   return writeReg(REG_INT_ENABLE, enables);
    370 }
    371 
    372 uint8_t MPU60X0::getInterruptEnables()
    373 {
    374   return readReg(REG_INT_ENABLE);
    375 }
    376 
    377 bool MPU60X0::setInterruptPinConfig(uint8_t cfg)
    378 {
    379   return writeReg(REG_INT_PIN_CFG, cfg);
    380 }
    381 
    382 uint8_t MPU60X0::getInterruptPinConfig()
    383 {
    384   return readReg(REG_INT_PIN_CFG);
    385 }
    386 
    387 #ifdef JAVACALLBACK
    388 void MPU60X0::installISR(int gpio, mraa::Edge level,
    389                          IsrCallback *cb)
    390 {
    391         installISR(gpio, level, generic_callback_isr, cb);
    392 }
    393 #endif
    394 
    395 void MPU60X0::installISR(int gpio, mraa::Edge level,
    396                          void (*isr)(void *), void *arg)
    397 {
    398   // delete any existing ISR and GPIO context
    399   uninstallISR();
    400 
    401   // greate gpio context
    402   m_gpioIRQ = new mraa::Gpio(gpio);
    403 
    404   m_gpioIRQ->dir(mraa::DIR_IN);
    405   m_gpioIRQ->isr(level, isr, arg);
    406 }
    407 
    408 void MPU60X0::uninstallISR()
    409 {
    410   if (m_gpioIRQ)
    411     {
    412       m_gpioIRQ->isrExit();
    413       delete m_gpioIRQ;
    414 
    415       m_gpioIRQ = 0;
    416     }
    417 }
    418