Added code to manage Orientation, FreeFall and Motion Detection. Data is also available via IRQ.

Dependents:   Test_FRDM_MMA8451Q AccelTest FRDM-KL46-Template KL25Z_Demo ... more

Fork of MMA8451Q by Emilio Monti

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MMA8451Q.cpp Source File

MMA8451Q.cpp

00001 /* Copyright (c) 2010-2011 mbed.org, MIT License
00002 *
00003 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004 * and associated documentation files (the "Software"), to deal in the Software without
00005 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
00006 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
00007 * Software is furnished to do so, subject to the following conditions:
00008 *
00009 * The above copyright notice and this permission notice shall be included in all copies or
00010 * substantial portions of the Software.
00011 *
00012 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017 */
00018 
00019 #include "MMA8451Q.h"
00020 
00021 #define REG_STATUS        0x00
00022 #define REG_WHO_AM_I      0x0D
00023 #define REG_CTRL_REG_1    0x2A
00024 #define REG_CTRL_REG_2    0x2B
00025 #define REG_CTRL_REG_4    0x2D
00026 #define REG_CTRL_REG_5    0x2E
00027 #define REG_INT_SRC       0x0C
00028 #define REG_FF_MT_CFG     0x15
00029 #define REG_FF_MT_SRC     0x16
00030 #define REG_FF_MT_THS     0x17
00031 #define REG_FF_MT_CNT     0x18
00032 #define REG_DBCNTM        0x11
00033 #define REG_DBNCE         0x12
00034 #define REG_BKFR          0x13
00035 #define REG_P_L_THS       0x14
00036 #define REG_PL_STATUS     0x10
00037 
00038 //
00039 #define REG_OUT_X_MSB     0x01
00040 #define REG_OUT_Y_MSB     0x03
00041 #define REG_OUT_Z_MSB     0x05
00042 
00043 #define UINT14_MAX        16383
00044 
00045 //
00046 #define ZYXDR           0x08
00047 #define ZDR             0x04
00048 #define YDR             0x02
00049 #define XDR             0x01
00050 
00051 /** Interrupt schema
00052 *
00053 * :: The FreeFall and Motion detection share the same IRQ2. 
00054 * 
00055 *   FreeFall --+                             +-- Fall_IRQ -----+
00056 *               \                           /                   \
00057 *                +-- MMA8451Q_Int2.fall ---+                     +--- MMA8451Q_usr2_fptr
00058 *               /                           \                   /
00059 *   Motion ----+                             +-- Motion_IRQ ---+
00060 *   
00061 * :: The Orientation Detect use the IRQ1
00062 * 
00063 *   Orientation Detect -- MMA8451Q_Int1.fall --- Orientation_IRQ --- MMA8451Q_usr1_fptr
00064 *
00065 *
00066 * :: The data ready use the IRQ2
00067 *
00068 *   Data Ready -- MMA8451Q_Int2.fall --- DataReady_IRQ --- usr2_fptr
00069 *
00070 */
00071 void (*MMA8451Q_usr2_fptr)(void);               // Pointers to user function called after
00072 void (*MMA8451Q_usr1_fptr)(void);               // IRQ assertion.
00073 
00074 //
00075 InterruptIn MMA8451Q_Int1( PTA14);      // INT1
00076 InterruptIn MMA8451Q_Int2( PTA15);      // INT2
00077 
00078 MMA8451Q::MMA8451Q(PinName sda, PinName scl, int addr) : m_i2c(sda, scl), m_addr(addr) {
00079     
00080     MMA8451Q_Int1.fall( NULL);
00081     MMA8451Q_Int2.fall( NULL);
00082     MMA8451Q_usr2_fptr = NULL;
00083     MMA8451Q_usr1_fptr = NULL;    
00084 
00085     Reset();    
00086     Active();    
00087 }
00088 
00089 MMA8451Q::~MMA8451Q() 
00090 {
00091      MMA8451Q_Int1.fall( NULL);
00092      MMA8451Q_Int2.fall( NULL);
00093      MMA8451Q_usr2_fptr = NULL;
00094      MMA8451Q_usr1_fptr = NULL;
00095 }
00096 
00097 void MMA8451Q::Reset( void)
00098 {
00099     // Soft reset
00100     uint8_t data[2] = {REG_CTRL_REG_2, 0x40};
00101     writeRegs(data, 2);
00102     wait( 0.1);
00103 }
00104 
00105 void MMA8451Q::FreeFallDetection( void(*fptr)(void))
00106 {
00107     // Soft Reset
00108     Reset();
00109     
00110     // Example Steps for Configuring Linear Freefall Detection
00111     // X AND Y AND Z < 0.2g using MFF Function, 50 Hz ODR
00112     // Step 1: Put the device in Standby Mode: Register 0x2A CTRL_REG1
00113     unsigned char data[2] = {REG_CTRL_REG_1, 0x20};
00114     writeRegs(data, 2);
00115     
00116     // Step 2: Configuration Register set for Freefall Detection enabling “AND” condition, OAE = 0, Enabling X,
00117     // Y, Z and the Latch
00118     data[0] = REG_FF_MT_CFG;
00119     data[1] = 0x01;
00120     writeRegs(data, 2);
00121 
00122     // Step 3: Threshold Setting Value for the resulting acceleration < 0.2g
00123     // Note: The step count is 0.063g/count
00124     // • 0.2g/0.063g = 3.17 counts //Round to 3 counts
00125     data[0] = REG_FF_MT_THS;
00126     data[1] = 0x03;
00127     writeRegs(data, 2);
00128 
00129     // Step 4: Set the debounce counter to eliminate false positive readings for 50Hz sample rate with a
00130     // requirement of 120 ms timer, assuming Normal Mode.
00131     // Note: 120 ms/20 ms (steps) = 6 counts
00132     data[0] = REG_FF_MT_CNT;
00133     data[1] = 0x06;
00134     writeRegs(data, 2);
00135 
00136     // Step 5: Enable Motion/Freefall Interrupt Function in the System (CTRL_REG4)
00137     data[0] = REG_CTRL_REG_4;
00138     data[1] = 0x04;
00139     writeRegs(data, 2);
00140 
00141     // Step 6: Route the Motion/Freefall Interrupt Function to INT2 hardware pin (CTRL_REG5)
00142     data[0] = REG_CTRL_REG_5;
00143     data[1] = 0x00;
00144     writeRegs(data, 2);
00145     
00146     // Step 7: Put the device in Active Mode, 50 Hz
00147     data[0] = REG_CTRL_REG_1;
00148     data[1] = 0x21;
00149     writeRegs(data, 2);
00150     
00151     MMA8451Q_usr2_fptr = fptr;
00152     MMA8451Q_Int2.fall( this, &MMA8451Q::Fall_IRQ);
00153 }
00154 
00155 void MMA8451Q::Fall_IRQ( void)
00156 {
00157     unsigned char t;
00158     
00159     // Determine source of the interrupt by first reading the system interrupt
00160     readRegs( REG_INT_SRC, &t, 1);
00161     //
00162     if ( (t & 0x04) == 0x04) {
00163         // Read the Motion/Freefall Function to clear the interrupt
00164         readRegs( REG_FF_MT_SRC, &t, 1);
00165         // Run the user supplied function
00166         MMA8451Q_usr2_fptr();
00167     }
00168 }
00169 
00170 void MMA8451Q::MotionDetection( void(*fptr)(void))
00171 {
00172     // Soft Reset
00173     Reset();
00174     
00175     // 6.1 Example Steps for Configuring Motion Detection
00176     // X or Y > 3g using MFF Function 4g, 100 Hz ODR, Normal Mode
00177     // Step 1: Put the device into Standby Mode: Register 0x2A CTRL_REG1
00178     unsigned char data[2] = {REG_CTRL_REG_1, 0x18}; // Set the device in 100 Hz ODR, Standby
00179     writeRegs(data, 2);
00180 
00181     
00182     // Step 2: Set Configuration Register for Motion Detection by setting the “OR” condition OAE = 1, enabling
00183     // X, Y, and the latch
00184     data[0] = REG_FF_MT_CFG;
00185     data[1] = 0xD8;
00186     writeRegs(data, 2);
00187 
00188     // Step 3: Threshold Setting Value for the Motion detection of > 2g
00189     // Note: The step count is 0.063g/ count
00190     // • 1g/0.063g = 15.8; //Round up to 16
00191     data[0] = REG_FF_MT_THS;
00192     data[1] = 0x10;
00193     writeRegs(data, 2);
00194     
00195     // Step 4: Set the debounce counter to eliminate false readings for 100 Hz sample rate with a requirement
00196     // of 100 ms timer.
00197     // Note: 100 ms/10 ms (steps) = 10 counts
00198     data[0] = REG_FF_MT_CNT;
00199     data[1] = 0x0A;
00200     writeRegs(data, 2);
00201     
00202     // Step 5: Enable Motion/Freefall Interrupt Function in the System (CTRL_REG4)
00203     data[0] = REG_CTRL_REG_4;
00204     data[1] = 0x04;
00205     writeRegs(data, 2);
00206     
00207     // Step 6: Route the Motion/Freefall Interrupt Function to INT2 hardware pin (CTRL_REG5)
00208     data[0] = REG_CTRL_REG_5;
00209     data[1] = 0x00;
00210     writeRegs(data, 2);
00211     
00212     // Step 7: Put the device in Active Mode
00213     data[0] = REG_CTRL_REG_1;
00214     data[1] = 0x19;
00215     writeRegs(data, 2);
00216 
00217     MMA8451Q_usr2_fptr = fptr;
00218     MMA8451Q_Int2.fall( this, &MMA8451Q::Motion_IRQ);
00219 
00220 }
00221 
00222 void MMA8451Q::Motion_IRQ( void)
00223 {
00224     unsigned char t;
00225     
00226     // Determine source of the interrupt by first reading the system interrupt
00227     readRegs( REG_INT_SRC, &t, 1);
00228     //
00229     if ( (t & 0x04) == 0x04) {
00230         // Read the Motion/Freefall Function to clear the interrupt
00231         readRegs( REG_FF_MT_SRC, &t, 1);
00232         // Run the user supplied function
00233         MMA8451Q_usr2_fptr();
00234     }
00235 }
00236 
00237 void MMA8451Q::OrientationDetect( void(*fptr)(void))
00238 {
00239     OrientationDetect( fptr, Z_LOCKOUT_14, Z_BKFR_80, PL_THS_15, PL_HYS_0);
00240 }
00241 
00242 void MMA8451Q::OrientationDetect( void(*fptr)(void), unsigned int Z_LockOut, unsigned int Z_BkFr, unsigned int PL_Thsld, unsigned int PL_Hyst)
00243 {
00244     unsigned char t;
00245 
00246     // Soft Reset
00247     Reset();
00248         
00249     // Reset orientation value.
00250     OrientationState = 0;
00251     OrientationStateUpdated = 0;
00252     
00253     // Step 1: Put the part into Standby Mode
00254     Standby();
00255     
00256     // Step 2: Set the data rate to 50 Hz (for example, but can choose any sample rate).
00257     readRegs( REG_CTRL_REG_1, &t, 1);       // Note: Can combine this step with above
00258     t &= 0xC7;                             // Clear the sample rate bits
00259     t |= 0x20;                             // Set the sample rate bits to 50 Hz
00260     unsigned char data[2] = {REG_CTRL_REG_1, t};
00261     writeRegs(data, 2);                     // Write updated value into the register.   
00262     
00263     
00264     // Step 3: Set the PL_EN bit in Register 0x11 PL_CFG. This will enable the orientation detection.
00265     readRegs( REG_DBCNTM, &t, 1);
00266     data[0] = REG_DBCNTM;
00267     data[1] = t | 0x40;
00268     writeRegs(data, 2);
00269     
00270     // Step 4: Set the Back/Front Angle trip points in register 0x13 following the table in the data sheet.
00271     // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
00272     // MMA8451Q.
00273     readRegs( REG_BKFR, &t, 1);
00274     t &= 0x3F;                      // Clear bit 7 and 6    
00275     data[0] = REG_BKFR;
00276     data[1] = t | Z_BkFr;
00277     writeRegs(data, 2);             // Write in the updated Back/Front Angle
00278 
00279     // Step 5: Set the Z-Lockout angle trip point in register 0x13 following the table in the data sheet.
00280     // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
00281     // MMA8451Q.
00282     readRegs( REG_BKFR, &t, 1);
00283     t &= 0xF8;                      // Clear the last three bits of the register
00284     data[0] = REG_BKFR;
00285     data[1] = t | Z_LockOut;
00286     writeRegs(data, 2);             // Write in the updated Z-lockout angle
00287     
00288     // Step 6: Set the Trip Threshold Angle
00289     // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
00290     // MMA8451Q.
00291     // Select the angle desired in the table, and,
00292     // Enter in the values given in the table for the corresponding angle.
00293     // Refer to Figure 7 for the reference frame of the trip angles.
00294     readRegs( REG_P_L_THS, &t, 1);
00295     t &= 0x07;                      // Clear the Threshold values
00296     data[0] = REG_P_L_THS;
00297     data[1] = t | (PL_Thsld<<3);
00298     writeRegs(data, 2);             
00299     
00300     // Step 7: Set the Hysteresis Angle
00301     // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
00302     // MMA8451Q.
00303     // Select the hysteresis value based on the desired final trip points (threshold + hysteresis)
00304     // Enter in the values given in the table for that corresponding angle.
00305     // Note: Care must be taken. Review the final resulting angles. Make sure there isn’t a resulting trip value
00306     // greater than 90 or less than 0.
00307     // The following are the options for setting the hysteresis.
00308     readRegs( REG_P_L_THS, &t, 1);
00309     t &= 0xF8;                      // Clear the Hysteresis values
00310     data[0] = REG_P_L_THS;
00311     data[1] = t | PL_Hyst;
00312     writeRegs(data, 2);             
00313     
00314     // Step 8: Register 0x2D, Control Register 4 configures all embedded features for interrupt
00315     // detection.
00316     // To set this device up to run an interrupt service routine:
00317     // Program the Orientation Detection bit in Control Register 4.
00318     // Set bit 4 to enable the orientation detection “INT_EN_LNDPRT”.
00319     readRegs( REG_CTRL_REG_4, &t, 1);
00320     data[0] = REG_CTRL_REG_4;
00321     data[1] = t | 0x10;                 // Set bit 4
00322     writeRegs(data, 2);             
00323     
00324     // Step 9: Register 0x2E is Control Register 5 which gives the option of routing the interrupt to
00325     // either INT1 or INT2
00326     // Depending on which interrupt pin is enabled and configured to the processor:
00327     // Set bit 4 “INT_CFG_LNDPRT” to configure INT1, or,
00328     // Leave the bit clear to configure INT2.
00329     readRegs( REG_CTRL_REG_5, &t, 1);
00330     data[0] = REG_CTRL_REG_5;
00331     data[1] = t | 0x10;                 // Set bit 4 to choose the interrupt to route to INT1
00332     writeRegs(data, 2);             
00333     
00334     // Step 10: Set the debounce counter in register 0x12
00335     // This value will scale depending on the application-specific required ODR.
00336     // If the device is set to go to sleep, reset the debounce counter before the device goes to sleep. This setting
00337     // helps avoid long delays since the debounce will always scale with the current sample rate. The debounce
00338     // can be set between 50 ms - 100 ms to avoid long delays.
00339     data[0] = REG_DBNCE;
00340     data[1] = 0x05;                     // This sets the debounce counter to 100 ms at 50 Hz
00341     writeRegs(data, 2);             
00342     
00343     // Step 11: Put the device in Active Mode
00344     Active();
00345  
00346     MMA8451Q_usr1_fptr = fptr;
00347     MMA8451Q_Int1.fall( this, &MMA8451Q::Orientation_IRQ);
00348 
00349 }
00350 
00351 void MMA8451Q::Orientation_IRQ( void)
00352 {
00353     unsigned char t;
00354     
00355     // Determine source of the interrupt by first reading the system interrupt
00356     readRegs( REG_INT_SRC, &t, 1);
00357     //
00358     if ( (t & 0x10) == 0x10) {
00359         // Read the PL State from the Status Register, clear the interrupt
00360         readRegs( REG_PL_STATUS, &t, 1);
00361         // Set the orientation state variable
00362         OrientationState = t;
00363         OrientationStateUpdated = 1;
00364         // Run the user supplied function
00365         MMA8451Q_usr1_fptr();
00366     }
00367 }
00368 
00369 unsigned char MMA8451Q::GetOrientationState( void)
00370 {
00371     if ( OrientationStateUpdated) {
00372         OrientationStateUpdated = 0;
00373         return OrientationState;
00374     }
00375     //
00376     return 0;
00377 }
00378 
00379 void MMA8451Q::DataReady( void(*fptr)(void), unsigned char ODR)
00380 {
00381     // Soft Reset
00382     Reset();
00383     
00384     // Step 1: Put the device into Standby Mode: Register 0x2A CTRL_REG1
00385     // Set the device ODR value and Standby
00386     unsigned char data[2] = {REG_CTRL_REG_1, ((ODR<<3) & 0xFE)};
00387     writeRegs(data, 2);
00388 
00389     // Step 2: Enable Data Ready Interrupt Function in the System (CTRL_REG4)
00390     data[0] = REG_CTRL_REG_4;
00391     data[1] = 0x01;
00392     writeRegs(data, 2);
00393     
00394     // Step 6: Route the Data Ready Interrupt Function to INT2 hardware pin (CTRL_REG5)
00395     data[0] = REG_CTRL_REG_5;
00396     data[1] = 0x00;
00397     writeRegs(data, 2);
00398     
00399     // Step 7: Put the device in Active Mode
00400     data[0] = REG_CTRL_REG_1;
00401     data[1] = ((ODR<<3) | 0x01);
00402     writeRegs(data, 2);
00403 
00404     MMA8451Q_usr2_fptr = fptr;
00405     MMA8451Q_Int2.fall( this, &MMA8451Q::DataReady_IRQ);
00406 
00407 }
00408 
00409 void MMA8451Q::DataReady_IRQ( void)
00410 {
00411     unsigned char t;
00412     
00413     // Determine source of the interrupt by first reading the system interrupt
00414     readRegs( REG_INT_SRC, &t, 1);
00415     //
00416     if ( (t & 0x01) == 0x01) {
00417         // Read the DataReady_IRQ Function to clear the interrupt
00418         readRegs( REG_FF_MT_SRC, &t, 1);
00419         // Run the user supplied function
00420         MMA8451Q_usr2_fptr();
00421     }
00422 }
00423 
00424 
00425 void MMA8451Q::Active( void)
00426 {
00427     unsigned char t;
00428     
00429     // Activate the peripheral
00430     readRegs(REG_CTRL_REG_1, &t, 1);
00431     unsigned char data[2] = {REG_CTRL_REG_1, t|0x01};
00432     writeRegs(data, 2);
00433 }
00434 
00435 void MMA8451Q::Standby( void)
00436 {
00437     unsigned char t;
00438     
00439     // Standby
00440     readRegs(REG_CTRL_REG_1, &t, 1);
00441     unsigned char data[2] = {REG_CTRL_REG_1, t&0xFE};
00442     writeRegs(data, 2);
00443 }
00444 
00445 uint8_t MMA8451Q::getWhoAmI() {
00446     uint8_t who_am_i = 0;
00447     readRegs(REG_WHO_AM_I, &who_am_i, 1);
00448     return who_am_i;
00449 }
00450 
00451 float MMA8451Q::getAccX() {
00452     return (float(getAccAxis(REG_OUT_X_MSB))/4096.0);
00453 }
00454 
00455 float MMA8451Q::getAccY() {
00456     return (float(getAccAxis(REG_OUT_Y_MSB))/4096.0);
00457 }
00458 
00459 float MMA8451Q::getAccZ() {
00460     return (float(getAccAxis(REG_OUT_Z_MSB))/4096.0);
00461 }
00462 
00463 void MMA8451Q::getAccAllAxis(float * res) {
00464     res[0] = getAccX();
00465     res[1] = getAccY();
00466     res[2] = getAccZ();
00467 }
00468 
00469 int16_t MMA8451Q::getAccAxis(uint8_t addr) {
00470     int16_t acc;
00471     uint8_t res[2];
00472     readRegs(addr, res, 2);
00473 
00474     acc = (res[0] << 6) | (res[1] >> 2);
00475     if (acc > UINT14_MAX/2)
00476         acc -= UINT14_MAX;
00477 
00478     return acc;
00479 }
00480 
00481 unsigned int MMA8451Q::getAccRawAllAxis( int16_t * res) 
00482 {
00483     if ( isDataAvailable() & ZYXDR) 
00484     {
00485         getAccRawX( &res[0]);
00486         getAccRawY( &res[1]);
00487         getAccRawZ( &res[2]);
00488         return 1;
00489     } else
00490         return 0;
00491 }
00492 
00493 int16_t MMA8451Q::getAccRawX( int16_t * res) 
00494 {
00495     if ( isDataAvailable() & XDR) 
00496     {
00497         *res = getAccAxis(REG_OUT_X_MSB);
00498         return 1;
00499     } else
00500         return 0;        
00501 }
00502 
00503 int16_t MMA8451Q::getAccRawY( int16_t * res) 
00504 {
00505     if ( isDataAvailable() & YDR) 
00506     {
00507         *res = getAccAxis(REG_OUT_Y_MSB);
00508         return 1;
00509     } else
00510         return 0;        
00511 }
00512 
00513 int16_t MMA8451Q::getAccRawZ( int16_t * res) 
00514 {
00515     if ( isDataAvailable() & ZDR) 
00516     {
00517         *res = getAccAxis(REG_OUT_Z_MSB);
00518         return 1;
00519     } else
00520         return 0;        
00521 }
00522 
00523 unsigned int MMA8451Q::isDataAvailable( void)
00524 {
00525     unsigned char status;
00526     
00527     readRegs( REG_STATUS, &status, 1);
00528 
00529     return (status);
00530     
00531 }
00532 
00533 void MMA8451Q::readRegs(int addr, uint8_t * data, int len) {
00534     char t[1] = {addr};
00535     m_i2c.write(m_addr, t, 1, true);
00536     m_i2c.read(m_addr, (char *)data, len);
00537 }
00538 
00539 void MMA8451Q::writeRegs(uint8_t * data, int len) {
00540     m_i2c.write(m_addr, (char *)data, len);
00541 }