Works with 8g
Fork of MMA8452 by
Diff: MMA8452.cpp
- Revision:
- 11:dfd1e0afcb7b
- Parent:
- 10:ca9ba7ad4e94
- Child:
- 12:172540ff6b8b
--- a/MMA8452.cpp Tue Mar 04 11:14:34 2014 +0000 +++ b/MMA8452.cpp Tue Mar 04 16:23:40 2014 +0000 @@ -1,4 +1,4 @@ -// Author: Nicholas Herriot +// Author: Nicholas Herriot, Ashley Mills /* Copyright (c) 2013 Vodafone, MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software @@ -17,15 +17,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -# include "MMA8452.h" +#include "MMA8452.h" +#include "mbed.h" + +extern Serial pc; // Connect module at I2C address using I2C port pins sda and scl -MMA8452::MMA8452(PinName sda, PinName scl,int frequency) : m_i2c(sda, scl) , m_frequency(frequency) { - //m_i2c.frequency(m_frequency); - - // setup read and write addresses to avoid duplication - _readAddress = (MMA8452_ADDRESS<<1) | 0x01; - _writeAddress = (MMA8452_ADDRESS<<1) & 0xFE; +MMA8452::MMA8452(PinName sda, PinName scl, int frequency) : _i2c(sda, scl) , _frequency(frequency) { + DBG("Creating MMA8452"); + _i2c.frequency(_frequency); + + // setup read and write addresses to avoid duplication + _readAddress = MMA8452_ADDRESS | 0x01; + _writeAddress = MMA8452_ADDRESS & 0xFE; + DBG("Done"); } @@ -34,99 +39,93 @@ // Setting the control register bit 1 to true to activate the MMA8452 int MMA8452::activate() { - // set control register 1 to active - char init[2] = {CTRL_REG_1,ACTIVE}; - // perform write and return error code - return m_i2c.write(_writeAddress,init,2); + return logicalORRegister(CTRL_REG_1,MMA8452_ACTIVE_MASK); } - -// Get 'Fast Read Mode' called F_READ. If bit 1 is set '1' then F_READ is active. Fast read will skip LSB when reading xyz -// resisters from 0x01 to 0x06. When F_READ is '0' then all 6 registers will be read. - -int MMA8452::get_CTRL_Reg1(int* dst) -{ - return readRegister(CTRL_REG_1,dst); +// Setting the control register bit 1 to 0 to standby the MMA8452 +int MMA8452::standby() { + // perform write and return error code + return logicalANDRegister(CTRL_REG_1,MMA8452_STANDBY_MASK); } -// Setting the control register bit 1 to true to activate the MMA8452 -int MMA8452::standby() -{ - // set control register 1 to standby - char init[2] = {CTRL_REG_1,STANDBY}; - - // write to the register and return the error code - return m_i2c.write(_writeAddress,init,2); +// this reads a register, applies a bitmask with logical AND, sets a value with logical OR, +// and optionally goes into and out of standby at the beginning and end of the function respectively +int MMA8452::maskAndApplyRegister(char reg, char mask, char value, int toggleActivation) { + if(toggleActivation) { + if(standby()) { + return 1; + } + } + + // read from register + char oldValue = 0; + if(readRegister(reg,&oldValue)) { + return 1; + } + + // apply bitmask + oldValue &= mask; + + // set value + oldValue |= value; + + // write back to register + if(writeRegister(reg,oldValue)) { + return 1; + } + + if(toggleActivation) { + if(activate()) { + return 1; + } + } + return 0; } - - -// Device initialization -void MMA8452::init() -{ - - writeRegister(INTSU_STATUS, 0x10); // automatic interrupt after every measurement - writeRegister(SR_STATUS, 0x00); // 120 Samples/Second - writeRegister(MODE_STATUS, 0x01); // Active Mode - +int MMA8452::setDynamicRange(DynamicRange range, int toggleActivation) { + return maskAndApplyRegister( + MMA8452_XYZ_DATA_CFG, + MMA8452_DYNAMIC_RANGE_MASK, + range, + toggleActivation + ); } -// Get real time status of device - it can be STANDBY, WAKE or SLEEP -int MMA8452::getSystemMode(int *dst) -{ - return readRegister(SYSMOD,dst); +int MMA8452::setDataRate(DataRateHz dataRate, int toggleActivation) { + return maskAndApplyRegister( + MMA8452_CTRL_REG_1, + MMA8452_DATA_RATE_MASK, + dataRate<<MMA8452_DATA_RATE_MASK_SHIFT, + toggleActivation + ); } - - -// Get real time status of device - it can be STANDBY, WAKE or SLEEP -int MMA8452::getStatus(int* dst) -{ - return readRegister(STATUS,dst); +int MMA8452::setBitDepth(BitDepth depth,int toggleActivation) { + return maskAndApplyRegister( + MMA8452_CTRL_REG_1, + MMA8452_BIT_DEPTH_MASK, + depth<<MMA8452_BIT_DEPTH_MASK_SHIFT, + toggleActivation + ); } // Get device ID -int MMA8452::getDeviceID(int *dst) +int MMA8452::getDeviceID(char *dst) { return readRegister(WHO_AM_I,dst); } -/* -// Reads x data -int MMA8452::read_x(int& xaxisLSB) -{ - char mcu_address = (MMA8452_ADDRESS<<1); - m_i2c.start(); - if( m_i2c.write( mcu_address & 0xFE) == 0) // just good practice to force bit 1 to a '0' by ANDing with 0xFE - { - return 1; // we failed to write the mcu address on the bus to initiate dialogue - } - if( m_i2c.write( OUT_X_MSB) == 0) - { - return 1; // we failed to write 'X axis LSB' to the chip - } - m_i2c.start(); - if( m_i2c.write( mcu_address | 0x01) == 0) // this is asking to read the slave mcu address - even though it's a 'write' method!!! Crap API... - { - return 1; // we failed to request a read from that mcu - this really is just writing the mcu vaule on the bus - } - xaxisLSB = m_i2c.read(0); - m_i2c.stop(); - return 0; -} -*/ - int MMA8452::readRaw(char src, char *dst, int len) { // this is the register we want to get data from char register_address[1]; register_address[0] = src; - if(m_i2c.write(_writeAddress,register_address,1,true) == 0) + if(_i2c.write(_writeAddress,register_address,1,true) == 0) { - if(m_i2c.read(_readAddress,dst,len)==0) + if(_i2c.read(_readAddress,dst,len)==0) { return 0; } @@ -144,8 +143,7 @@ // is set to '0'. It returns '0' success '1' fail. This method does nothing to the registers - just returns // the raw data. //int MMA8452::read_x(int& xaxisLSB) -int MMA8452::readRawX(char *xaxis) -{ +int MMA8452::readRawX(char *xaxis) { return readRaw(OUT_X_MSB,xaxis,2); } @@ -178,37 +176,57 @@ { char mcu_address = (MMA8452_ADDRESS <<1); - m_i2c.start(); // Start - m_i2c.write(mcu_address); // A write to device 0x98 - m_i2c.write(OUT_Y_MSB); // Register to read - m_i2c.start(); - m_i2c.write(mcu_address); // Read from device 0x99 - int y = m_i2c.read(0); // Read the data - m_i2c.stop(); + _i2c.start(); // Start + _i2c.write(mcu_address); // A write to device 0x98 + _i2c.write(OUT_Y_MSB); // Register to read + _i2c.start(); + _i2c.write(mcu_address); // Read from device 0x99 + int y = _i2c.read(0); // Read the data + _i2c.stop(); return y; } - // Reads z data int MMA8452::read_z() { char mcu_address = (MMA8452_ADDRESS <<1); - m_i2c.start(); // Start - m_i2c.write(mcu_address); // A write to device 0x98 - m_i2c.write(OUT_Z_MSB); // Register to read - m_i2c.start(); - m_i2c.write(mcu_address); // Read from device 0x99 - int z = m_i2c.read(0); // Read the data - m_i2c.stop(); + _i2c.start(); // Start + _i2c.write(mcu_address); // A write to device 0x98 + _i2c.write(OUT_Z_MSB); // Register to read + _i2c.start(); + _i2c.write(mcu_address); // Read from device 0x99 + int z = _i2c.read(0); // Read the data + _i2c.stop(); return z; } +MMA8452::DynamicRange MMA8452::getDynamicRange() { + char rval = 0; + if(readRegister(MMA8452_CTRL_REG_1,&rval)) { + return MMA8452::DYNAMIC_RANGE_UNKNOWN; + } + rval &= (MMA8452_DYNAMIC_RANGE_MASK^0xFF); + return (MMA8452::DynamicRange)rval; +} + +MMA8452::DataRateHz MMA8452::getDataRate() { + char rval = 0; + if(readRegister(MMA8452_CTRL_REG_1,&rval)) { + return MMA8452::RATE_UNKNOWN; + } + // logical AND with inverse of mask + rval = rval&(MMA8452_DATA_RATE_MASK^0xFF); + // shift back into position + rval >>= MMA8452_DATA_RATE_MASK_SHIFT; + return (MMA8452::DataRateHz)rval; +} + // Reads xyz int MMA8452::readRawXYZ(char *x, char *y, char *z) { @@ -217,12 +235,12 @@ char mcu_address = (MMA8452_ADDRESS <<1); char register_buffer[6] ={0,0,0,0,0,0}; const char Addr_X = OUT_X_MSB; - m_i2c.write(mcu_address); // A write to device 0x98 - m_i2c.write(MMA8452_ADDRESS, &Addr_X, 1); // Pointer to the OUT_X_MSB register + _i2c.write(mcu_address); // A write to device 0x98 + _i2c.write(MMA8452_ADDRESS, &Addr_X, 1); // Pointer to the OUT_X_MSB register - if(m_i2c.write(mcu_address,&Addr_X,1) == 0) + if(_i2c.write(mcu_address,&Addr_X,1) == 0) { - if(m_i2c.read(mcu_address,register_buffer,6) == 0) + if(_i2c.read(mcu_address,register_buffer,6) == 0) { *x = register_buffer[1]; *y = register_buffer[3]; @@ -241,47 +259,159 @@ } - // Write register (The device must be placed in Standby Mode to change the value of the registers) -void MMA8452::writeRegister(char addr, char data) -{ +// apply an AND mask to a register. read register value, apply mask, write it back +int MMA8452::logicalANDRegister(char addr, char mask) { + char value = 0; + // read register value + if(readRegister(addr,&value)) { + return 0; + } + // apply mask + value &= mask; + return writeRegister(addr,value); +} + + +// apply an OR mask to a register. read register value, apply mask, write it back +int MMA8452::logicalORRegister(char addr, char mask) { + char value = 0; + // read register value + if(readRegister(addr,&value)) { + return 0; + } + // apply mask + value |= mask; + return writeRegister(addr,value); +} + - char cmd[2] = {0, 0}; - - cmd[0] = MODE_STATUS; - cmd[1] = 0x00; // Standby Mode on - m_i2c.write(MMA8452_ADDRESS, cmd, 2); - - cmd[0] = addr; - cmd[1] = data; // New value of the register - m_i2c.write(MMA8452_ADDRESS, cmd, 2); - - cmd[0] = MODE_STATUS; - cmd[1] = 0x01; // Active Mode on - m_i2c.write(MMA8452_ADDRESS, cmd, 2); - +// apply an OR mask to a register. read register value, apply mask, write it back +int MMA8452::logicalXORRegister(char addr, char mask) { + char value = 0; + // read register value + if(readRegister(addr,&value)) { + return 0; + } + // apply mask + value ^= mask; + return writeRegister(addr,value); +} + +// Write register (The device must be placed in Standby Mode to change the value of the registers) +int MMA8452::writeRegister(char addr, char data) { + // what this actually does is the following + // 1. tell I2C bus to start transaction + // 2. tell slave we want to write (slave address & write flag) + // 3. send the write address + // 4. send the data to write + // 5. tell I2C bus to end transaction + + // we can wrap this up in the I2C library write function + char buf[2] = {0,0}; + buf[0] = addr; + buf[1] = data; + return _i2c.write(MMA8452_ADDRESS, buf,2); + // note, could also do return writeRegister(addr,&data,1); } - // Read from specified MMA7660FC register -int MMA8452::readRegister(char addr, int *dst) -{ + +int MMA8452::writeRegister(char addr, char *data, int nbytes) { + // writing multiple bytes is a little bit annoying because + // the I2C library doesn't support sending the address separately + // so we just do it manually - m_i2c.start(); - if( m_i2c.write(_writeAddress) == 0) - { - return 1; // we failed to write the mcu address on the bus to initiate dialogue + // 1. tell I2C bus to start transaction + _i2c.start(); + // 2. tell slave we want to write (slave address & write flag) + if(_i2c.write(_writeAddress)!=1) { + return 1; + } + // 3. send the write address + if(_i2c.write(addr)!=1) { + return 1; + } + // 4. send the data to write + for(int i=0; i<nbytes; i++) { + if(_i2c.write(data[i])!=1) { + return 1; + } + } + // 5. tell I2C bus to end transaction + _i2c.stop(); + return 0; +} + +int MMA8452::readRegister(char addr, char *dst, int nbytes) { + // this is a bit odd, but basically proceeds like this + // 1. Send a start command + // 2. Tell the slave we want to write (slave address & write flag) + // 3. Send the address of the register (addr) + // 4. Send another start command to delineate read portion + // 5. Tell the slave we want to read (slave address & read flag) + // 6. Read the register value bytes + // 7. Send a stop command + + // we can wrap this process in the I2C library read and write commands + if(_i2c.write(MMA8452_ADDRESS,&addr,1,true)) { + return 1; } - if( m_i2c.write(addr) == 0) - { - return 1; // we failed to write 'status' to the chip - } - m_i2c.start(); - if( m_i2c.write(_readAddress) == 0) // this is asking to read the slave mcu address - even though it's a 'write' method!!! Crap API... - { - return 1; // we failed to request a read from that mcu - this really is just writing the mcu vaule on the bus - } - *dst = m_i2c.read(0); - m_i2c.stop(); - - return 0; - + return _i2c.read(MMA8452_ADDRESS,dst,nbytes); +} + +// most registers are 1 byte, so here is a convenience function +int MMA8452::readRegister(char addr, char *dst) { + return readRegister(addr,dst,1); } + +void MMA8452::debugRegister(char reg) { + // get register value + char v = 0; + if(readRegister(reg,&v)) { + DBG("Error reading control register"); + return; + } + // print out details + switch(reg) { + case MMA8452_CTRL_REG_1: + DBG("CTRL_REG_1 has value: 0x%x",v); + DBG(" 7 ALSP_RATE_1: %d",(v&0x80)>>7); + DBG(" 6 ALSP_RATE_0: %d",(v&0x40)>>6); + DBG(" 5 DR2: %d", (v&0x20)>>5); + DBG(" 4 DR1: %d", (v&0x10)>>4); + DBG(" 3 DR0: %d", (v&0x08)>>3); + DBG(" 2 LNOISE: %d", (v&0x04)>>2); + DBG(" 1 FREAD: %d", (v&0x02)>>1); + DBG(" 0 ACTIVE: %d", (v&0x01)); + break; + + case MMA8452_XYZ_DATA_CFG: + DBG("XYZ_DATA_CFG has value: 0x%x",v); + DBG(" 7 Unused: %d", (v&0x80)>>7); + DBG(" 6 0: %d", (v&0x40)>>6); + DBG(" 5 0: %d", (v&0x20)>>5); + DBG(" 4 HPF_Out: %d",(v&0x10)>>4); + DBG(" 3 0: %d", (v&0x08)>>3); + DBG(" 2 0: %d", (v&0x04)>>2); + DBG(" 1 FS1: %d", (v&0x02)>>1); + DBG(" 0 FS0: %d", (v&0x01)); + switch(v&0x03) { + case 0: + DBG("Dynamic range: 2G"); + break; + case 1: + DBG("Dynamic range: 4G"); + break; + case 2: + DBG("Dynamic range: 8G"); + break; + default: + DBG("Unknown dynamic range"); + break; + } + break; + + default: + DBG("Unknown register address: 0x%x",reg); + break; + } +} \ No newline at end of file