196 lines
4.9 KiB
C++
196 lines
4.9 KiB
C++
|
/**
|
||
|
* Class definition for MicroBit Accelerometer.
|
||
|
*
|
||
|
* Represents an implementation of the Freescale MMA8653 3 axis accelerometer
|
||
|
* Also includes basic data caching and on demand activation.
|
||
|
*/
|
||
|
|
||
|
#include "MicroBit.h"
|
||
|
|
||
|
/**
|
||
|
* Issues a standard, 2 byte I2C command write to the accelerometer.
|
||
|
* Blocks the calling thread until complete.
|
||
|
*
|
||
|
* @param reg The address of the register to write to.
|
||
|
* @param value The value to write.
|
||
|
*/
|
||
|
void MicroBitAccelerometer::writeCommand(uint8_t reg, uint8_t value)
|
||
|
{
|
||
|
uint8_t command[2];
|
||
|
command[0] = reg;
|
||
|
command[1] = value;
|
||
|
|
||
|
uBit.i2c.write(address, (const char *)command, 2);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Issues a read command into the specified buffer.
|
||
|
* Blocks the calling thread until complete.
|
||
|
*
|
||
|
* @param reg The address of the register to access.
|
||
|
* @param buffer Memory area to read the data into.
|
||
|
* @param length The number of bytes to read.
|
||
|
*/
|
||
|
void MicroBitAccelerometer::readCommand(uint8_t reg, uint8_t* buffer, int length)
|
||
|
{
|
||
|
uBit.i2c.write(address, (const char *)®, 1, true);
|
||
|
uBit.i2c.read(address, (char *)buffer, length);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructor.
|
||
|
* Create an accelerometer representation with the given ID.
|
||
|
* @param id the ID of the new object.
|
||
|
* @param address the default base address of the accelerometer.
|
||
|
*
|
||
|
* Example:
|
||
|
* @code
|
||
|
* accelerometer(MICROBIT_ID_ACCELEROMETER, MMA8653_DEFAULT_ADDR)
|
||
|
* @endcode
|
||
|
*/
|
||
|
MicroBitAccelerometer::MicroBitAccelerometer(uint16_t id, uint16_t address) : sample(), int1(MICROBIT_PIN_ACCEL_DATA_READY)
|
||
|
{
|
||
|
// Store our identifiers.
|
||
|
this->id = id;
|
||
|
this->address = address;
|
||
|
|
||
|
// Enable the accelerometer.
|
||
|
// First place the device into standby mode, so it can be configured.
|
||
|
writeCommand(MMA8653_CTRL_REG1, 0x00);
|
||
|
|
||
|
// Enable the INT1 interrupt pin.
|
||
|
writeCommand(MMA8653_CTRL_REG4, 0x01);
|
||
|
|
||
|
// Select the DATA_READY event source to be routed to INT1
|
||
|
writeCommand(MMA8653_CTRL_REG5, 0x01);
|
||
|
|
||
|
// Configure for a +/- 2g range.
|
||
|
writeCommand(MMA8653_XYZ_DATA_CFG, 0x00);
|
||
|
|
||
|
// Bring the device back online, with 10bit wide samples at a 50Hz frequency.
|
||
|
writeCommand(MMA8653_CTRL_REG1, 0x21);
|
||
|
|
||
|
// indicate that we're ready to receive tick callbacks.
|
||
|
uBit.flags |= MICROBIT_FLAG_ACCELEROMETER_RUNNING;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attempts to determine the 8 bit ID from the accelerometer.
|
||
|
* @return the 8 bit ID returned by the accelerometer
|
||
|
*
|
||
|
* Example:
|
||
|
* @code
|
||
|
* uBit.accelerometer.whoAmI();
|
||
|
* @endcode
|
||
|
*/
|
||
|
int MicroBitAccelerometer::whoAmI()
|
||
|
{
|
||
|
uint8_t data;
|
||
|
|
||
|
readCommand(MMA8653_WHOAMI, &data, 1);
|
||
|
return (int)data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads the acceleration data from the accelerometer, and stores it in our buffer.
|
||
|
* This is called by the tick() member function, if the interrupt is set!
|
||
|
*/
|
||
|
void MicroBitAccelerometer::update()
|
||
|
{
|
||
|
int8_t data[6];
|
||
|
|
||
|
readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6);
|
||
|
|
||
|
// read MSB values...
|
||
|
sample.x = data[0];
|
||
|
sample.y = data[2];
|
||
|
sample.z = data[4];
|
||
|
|
||
|
// Scale into millig (approx!)
|
||
|
sample.x *= 16;
|
||
|
sample.y *= 16;
|
||
|
sample.z *= 16;
|
||
|
|
||
|
// Invert the x and y axes, so that the reference frame aligns with micro:bit expectations
|
||
|
sample.x = -sample.x;
|
||
|
sample.y = -sample.y;
|
||
|
|
||
|
// We ignore the LSB bits for now, as they're just noise...
|
||
|
// TODO: Revist this when we have working samples to see if additional resolution is needed.
|
||
|
|
||
|
#ifdef USE_ACCEL_LSB
|
||
|
// Add in LSB values.
|
||
|
sample.x += (data[1] / 64);
|
||
|
sample.y += (data[3] / 64);
|
||
|
sample.z += (data[5] / 64);
|
||
|
#endif
|
||
|
|
||
|
//TODO: Issue an event.
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Reads the X axis value of the latest update from the accelerometer.
|
||
|
* Currently limited to +/- 2g
|
||
|
* @return The force measured in the X axis, in milli-g.
|
||
|
*
|
||
|
* Example:
|
||
|
* @code
|
||
|
* uBit.accelerometer.getX();
|
||
|
* @endcode
|
||
|
*/
|
||
|
int MicroBitAccelerometer::getX()
|
||
|
{
|
||
|
return sample.x;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads the Y axis value of the latest update from the accelerometer.
|
||
|
* Currently limited to +/- 2g
|
||
|
* @return The force measured in the Y axis, in milli-g.
|
||
|
*
|
||
|
* Example:
|
||
|
* @code
|
||
|
* uBit.accelerometer.getY();
|
||
|
* @endcode
|
||
|
*/
|
||
|
int MicroBitAccelerometer::getY()
|
||
|
{
|
||
|
return sample.y;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads the Z axis value of the latest update from the accelerometer.
|
||
|
* Currently limited to +/- 2g
|
||
|
* @return The force measured in the Z axis, in milli-g.
|
||
|
*
|
||
|
* Example:
|
||
|
* @code
|
||
|
* uBit.accelerometer.getZ();
|
||
|
* @endcode
|
||
|
*/
|
||
|
int MicroBitAccelerometer::getZ()
|
||
|
{
|
||
|
return sample.z;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* periodic callback from MicroBit clock.
|
||
|
* Check if any data is ready for reading by checking the interrupt flag on the accelerometer
|
||
|
*/
|
||
|
void MicroBitAccelerometer::idleTick()
|
||
|
{
|
||
|
// Poll interrupt line from accelerometer.
|
||
|
// n.b. Default is Active LO. Interrupt is cleared in data read.
|
||
|
if(!int1)
|
||
|
update();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
|
||
|
*/
|
||
|
int MicroBitAccelerometer::isIdleCallbackNeeded()
|
||
|
{
|
||
|
return !int1;
|
||
|
}
|