MMA8451Q driver

Fork of lib_mma8451q by canuck lehead

mma8451q.cpp

Committer:
shaunkrnelson
Date:
2016-08-12
Revision:
6:828b08201d8b
Parent:
5:7bcda574c0fa
Parent:
4:b06535efa239
Child:
8:442c7e6e35ce

File content as of revision 6:828b08201d8b:

#include "mma8451q.h"
/* turn on: CTRL_REG1 active_bit = 1
 * back to standby: CTRL_REG1 active_bit = 0
 */

/* STANDBY: SYSMOD = 00 */

/*
 * MMA8451 I2C address
 */ 
#define MMA8451_I2C_ADDRESS                          0x38 //0x1C

MMA8451Q::MMA8451Q(I2C& r, DigitalIn& int_pin) : m_i2c(r), m_int_pin(int_pin)
{
    /* INT pins on this chip default to push-pull output */
    write(MMA8451_CTRL_REG3, 0x01);    // set PP_OD
    /* INT1 and INT2 are tied together */
    
}

MMA8451Q::~MMA8451Q()
{
}

void MMA8451Q::read(uint8_t addr, uint8_t *dst_buf, int length)
{
    char cmd[2];

    cmd[0] = addr;
    if (m_i2c.write(MMA8451_I2C_ADDRESS, cmd, 1, true))
        printf("MMA write-fail %02x\n", addr);
    if (m_i2c.read(MMA8451_I2C_ADDRESS, (char *)dst_buf, length))
        printf("MMA read-fail\n");
}

uint8_t MMA8451Q::read_single(uint8_t addr)
{
    char cmd[2];

    cmd[0] = addr;
    if (m_i2c.write(MMA8451_I2C_ADDRESS, cmd, 1, true))
        printf("MMA write-fail %02x\n", addr);
    if (m_i2c.read(MMA8451_I2C_ADDRESS, cmd, 1))
        printf("MMA read-fail\n");

    return cmd[0];
}

void MMA8451Q::print_regs()
{
    printf("ID: %02x\r\n", read_single(MMA8451_ID));
    printf("sysmod:%02x\r\n", read_single(MMA8451_SYSMOD));
    ctrl_reg1.octet = read_single(MMA8451_CTRL_REG1);
    printf("ctrl_reg1:%02x\r\n", ctrl_reg1.octet);
    printf("ctrl_reg2:%02x\r\n", read_single(MMA8451_CTRL_REG2));
    printf("ctrl_reg3:%02x\r\n", read_single(MMA8451_CTRL_REG3)); /* TODO: PP_OD is bit 0 (1=open drain) */
    printf("(int en) ctrl_reg4:%02x\r\n", read_single(MMA8451_CTRL_REG4));
    printf("(int cfg) ctrl_reg5:%02x\r\n", read_single(MMA8451_CTRL_REG5));
    printf("status:%02x\r\n", read_single(MMA8451_STATUS)); 
    /* (interrupt status) int src at 0x0c (MMA8451_INT_SOURCE): data ready, motion/freefall, pulse, orientation, transient, auto sleep */
    printf("INT_SOURCE:%02x\r\n", read_single(MMA8451_INT_SOURCE)); 
}

void MMA8451Q::write(uint8_t addr, uint8_t data)
{
    uint8_t cmd[2];
    
    cmd[0] = addr;
    cmd[1] = data;

    if (m_i2c.write(MMA8451_I2C_ADDRESS, (char *)cmd, 2))
        printf("MMA write-fail %02x\n", addr);
}

void MMA8451Q::set_active(char arg)
{
    char cmd[2];
    
    cmd[0] = MMA8451_CTRL_REG1;
    cmd[1] = arg;

    if (m_i2c.write(MMA8451_I2C_ADDRESS, cmd, 2))
        printf("MMA write-fail %02x\n", cmd[0]);
}

bool MMA8451Q::get_active(void)
{
    uint8_t ret = read_single(MMA8451_CTRL_REG1);
    //printf("CTRL_REG1: %x\n", ret);
    if (ret & 1)
        return true;
    else
        return false;
}

void MMA8451Q::orient_detect()
{
    uint8_t v;
    
    ctrl_reg1.octet = read_single(MMA8451_CTRL_REG1);
    /* AN4068 Sensors Freescale Semiconductor, Inc.    
     * 4.1 Example Steps for Implementing the Embedded Orientation Detection */
     
     /* Step 1: Put the part into Standby Mode */
    ctrl_reg1.bits.ACTIVE = 0;
    write(MMA8451_CTRL_REG1, ctrl_reg1.octet);     
     
    /* Step 2: Set the data rate to 50 Hz (for example, but can choose any sample rate). */
    ctrl_reg1.bits.DR = 4;
    write(MMA8451_CTRL_REG1, ctrl_reg1.octet);
    
    /* Step 3: Set the PL_EN bit in Register 0x11 PL_CFG. This will enable the orientation detection. */
    v = read_single(MMA8451_PL_CFG);
    v |= 0x40;
    write(MMA8451_PL_CFG, v);
    
    /* Step 4: Set the Back/Front Angle trip points in register 0x13 following the table in the data sheet. */
    v = read_single(MMA8451_PL_BF_ZCOMP);
    /*v &= 0x3f;
    v |= 0xX0;
    write(MMA8451_PL_BF_ZCOMP, v);*/
    
    /* Step 5: Set the Z-Lockout angle trip point in register 0x13 following the table in the data sheet. */
    /* v &= 0xf8;
    v |= 0x0X;
    */
    
    /* Step 6: Set the Trip Threshold Angle */
    v = read_single(MMA8451_PL_THS_REG);
    /*v &= 0x07;
    v |= 0x0X << 3;
    write(MMA8451_PL_THS_REG. v);*/
    
    /* Step 7: Set the Hysteresis Angle */
    v = read_single(MMA8451_PL_THS_REG);
    /*v &= 0xf8;
    v |= 0x0X;
    write(MMA8451_PL_THS_REG. v);*/    
    
    /* Step 8: Register 0x2D, Control Register 4 configures all embedded features for interrupt */
    ctrl_reg4.octet = 0;
    ctrl_reg4.bits.INT_EN_LNDPRT = 1;
    write(MMA8451_CTRL_REG4, ctrl_reg4.octet);
    
    /* Step 9: Register 0x2E is Control Register 5 which gives the option of routing the interrupt to either INT1 or INT2 */
    ctrl_reg5.octet = 0;
    ctrl_reg5.bits.INT_CFG_LNDPRT = 1;
    write(MMA8451_CTRL_REG5, ctrl_reg5.octet);
        
    /* Step 10: Set the debounce counter in register 0x12 */
    write(MMA8451_PL_COUNT, 5); // 5: debounce to 100ms at 50hz
    
    /* Step 11: Put the device in Active Mode */
    ctrl_reg1.octet = read_single(MMA8451_CTRL_REG1);
    ctrl_reg1.bits.ACTIVE = 1;
    write(MMA8451_CTRL_REG1, ctrl_reg1.octet);
        
    /* Step 12: in service() function */
}


void MMA8451Q::transient_detect()
{
    ctrl_reg1.octet = read_single(MMA8451_CTRL_REG1);
    /* AN4071 Sensors Freescale Semiconductor, Inc.
     * 7.1 Example Steps for Configuring Transient Detection
     * Change in X or Y > 0.5g for 50 ms at 100 Hz ODR, Normal mode */

    /* Step 1: Put the device in Standby Mode: Register 0x2A CTRL_REG1 */
    ctrl_reg1.bits.ACTIVE = 0;
    write(MMA8451_CTRL_REG1, ctrl_reg1.octet);
    ctrl_reg1.bits.DR = 3; //Set device in 100 Hz ODR, Standby
    write(MMA8451_CTRL_REG1, ctrl_reg1.octet);

    /* Step 2: Enable X and Y Axes and enable the latch: Register 0x1D Configuration Register */
    transient_cfg.octet = 0;
    transient_cfg.bits.ELE = 1; // enable latch
    transient_cfg.bits.YTEFE = 1;   // enable Y
    transient_cfg.bits.XTEFE = 1;   // enable X
    transient_cfg.bits.ZTEFE = 1;   // enable Z
    write(MMA8451_TRANSIENT_CFG, transient_cfg.octet);

    /* Step 3: Set the Threshold: Register 0x1F 
     * Note: Step count is 0.063g per count, 0.5g / 0.063g = 7.93.
     * Therefore set the threshold to 8 counts */
    write(MMA8451_TRANSIENT_THS, 8);

    /* Step 4: Set the Debounce Counter for 50 ms: Register 0x20
     * Note: 100 Hz ODR, therefore 10 ms step sizes */
    write(MMA8451_TRANSIENT_COUNT, 5);

    /* Step 5: Enable Transient Detection Interrupt in the System (CTRL_REG4) */
    ctrl_reg4.octet = 0;
    ctrl_reg4.bits.INT_EN_TRANS = 1;
    write(MMA8451_CTRL_REG4, ctrl_reg4.octet);

    /* Step 6: Route the Transient Interrupt to INT 1 hardware pin (CTRL_REG5) */
    ctrl_reg5.octet = 0;
    ctrl_reg5.bits.INT_CFG_TRANS = 1;
    write(MMA8451_CTRL_REG5, ctrl_reg5.octet);

    /* Step 7: Put the device in Active Mode: Register 0x2A CTRL_REG1 */
    ctrl_reg1.octet = read_single(MMA8451_CTRL_REG1);
    ctrl_reg1.bits.ACTIVE = 1;
    write(MMA8451_CTRL_REG1, ctrl_reg1.octet);

    /* Step 8: Write Interrupt Service Routine Reading the
     * System Interrupt Status and the Transient Status */
}

uint8_t MMA8451Q::service()
{
    mma_int_source_t int_src;
    
    if (m_int_pin)
        return 0; // no interrupt
        
    int_src.octet = read_single(MMA8451_INT_SOURCE);
    
    if (int_src.bits.SRC_DRDY) {
        read(MMA8451_OUT_X_MSB, out.octets, 6);
    }
    if (int_src.bits.SRC_FF_MT) {
        read_single(MMA8451_FF_MT_SRC);
    }
    if (int_src.bits.SRC_PULSE) {
        read_single(MMA8451_PULSE_SRC);
    }
    if (int_src.bits.SRC_LNDPRT) {
        /*AN4068 Step 12: Write a Service Routine to Service the Interrupt */
        mma_pl_status_t pl_status;
        pl_status.octet = read_single(MMA8451_PL_STATUS);

        if (pl_status.bits.NEWLP) 
        { 
            orientation.reset();
            orientation.pl_status.octet = pl_status.octet;

            if (orientation.pl_status.bits.LO)
            {
                orientation.low = true;
                if(verbose)
                    printf("Z-tilt-LO ");
            }
                
            if (orientation.pl_status.bits.LAPO == 0)
            {
                orientation.up = true;
                if(verbose)
                    printf("up ");
            }
            else if (orientation.pl_status.bits.LAPO == 1)
            {
                orientation.down = true;
                if(verbose)
                    printf("down ");
            }
            else if (orientation.pl_status.bits.LAPO == 2)
            {
                orientation.right = true;
                if(verbose)
                    printf("right ");
            }
            else if (pl_status.bits.LAPO == 3)
            {
                orientation.left = true;
                if(verbose)
                    printf("left ");
            }

            
            if (orientation.pl_status.bits.BAFRO)
            {
                orientation.back = true;
                if(verbose)
                    printf("back ");
            }
            else
            {
                orientation.front = true;
                if(verbose)
                    printf("front ");
            }
        }

        if(verbose)
            printf("\r\n");
    } // ...int_src.bits.SRC_LNDPRT
                         
    if (int_src.bits.SRC_TRANS) {
        transient_src_t t_src;
        t_src.octet = read_single(MMA8451_TRANSIENT_SRC);
        if (verbose) {
            printf("transient src:%x ", t_src.octet);
            if (t_src.bits.XTRANSE)
                printf("X_Pol:%d ", t_src.bits.X_Trans_Pol);
            if (t_src.bits.YTRANSE)
                printf("Y_Pol:%d ", t_src.bits.Y_Trans_Pol);
            if (t_src.bits.ZTRANSE)
                printf("Z_Pol:%d ", t_src.bits.Z_Trans_Pol);
            printf("\r\n");        
        } 
    } // ...int_src.bits.SRC_TRANS
    
    if (int_src.bits.SRC_ASLP) {
        read_single(MMA8451_SYSMOD);
    }
    
    return int_src.octet;
}