2015-08-12 10:53:41 +00:00
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* Class definition for MicroBit Accelerometer.
|
|
|
|
*
|
|
|
|
* Represents an implementation of the Freescale MMA8653 3 axis accelerometer
|
|
|
|
* Also includes basic data caching and on demand activation.
|
|
|
|
*/
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
#include "MicroBit.h"
|
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* Configures the accelerometer for G range and sample rate defined
|
|
|
|
* in this object. The nearest values are chosen to those defined
|
|
|
|
* that are supported by the hardware. The instance variables are then
|
|
|
|
* updated to reflect reality.
|
|
|
|
*
|
|
|
|
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the accelerometer could not be configured.
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitAccelerometer::configure()
|
2015-09-08 22:16:15 +00:00
|
|
|
{
|
|
|
|
const MMA8653SampleRangeConfig *actualSampleRange;
|
|
|
|
const MMA8653SampleRateConfig *actualSampleRate;
|
2015-10-25 21:51:33 +00:00
|
|
|
int result;
|
2015-09-08 22:16:15 +00:00
|
|
|
|
|
|
|
// First find the nearest sample rate to that specified.
|
|
|
|
actualSampleRate = &MMA8653SampleRate[MMA8653_SAMPLE_RATES-1];
|
|
|
|
for (int i=MMA8653_SAMPLE_RATES-1; i>=0; i--)
|
|
|
|
{
|
|
|
|
if(MMA8653SampleRate[i].sample_period < this->samplePeriod * 1000)
|
|
|
|
break;
|
|
|
|
|
|
|
|
actualSampleRate = &MMA8653SampleRate[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now find the nearest sample range to that specified.
|
|
|
|
actualSampleRange = &MMA8653SampleRange[MMA8653_SAMPLE_RANGES-1];
|
|
|
|
for (int i=MMA8653_SAMPLE_RANGES-1; i>=0; i--)
|
|
|
|
{
|
|
|
|
if(MMA8653SampleRange[i].sample_range < this->sampleRange)
|
|
|
|
break;
|
|
|
|
|
|
|
|
actualSampleRange = &MMA8653SampleRange[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, we have the correct data. Update our local state.
|
|
|
|
this->samplePeriod = actualSampleRate->sample_period / 1000;
|
|
|
|
this->sampleRange = actualSampleRange->sample_range;
|
|
|
|
|
|
|
|
// Now configure the accelerometer accordingly.
|
|
|
|
// First place the device into standby mode, so it can be configured.
|
2015-10-25 21:51:33 +00:00
|
|
|
result = writeCommand(MMA8653_CTRL_REG1, 0x00);
|
|
|
|
if (result != 0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
2016-01-05 15:44:57 +00:00
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
// Enable high precisiosn mode. This consumes a bit more power, but still only 184 uA!
|
2015-10-25 21:51:33 +00:00
|
|
|
result = writeCommand(MMA8653_CTRL_REG2, 0x10);
|
|
|
|
if (result != 0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
2015-09-08 22:16:15 +00:00
|
|
|
|
|
|
|
// Enable the INT1 interrupt pin.
|
2015-10-25 21:51:33 +00:00
|
|
|
result = writeCommand(MMA8653_CTRL_REG4, 0x01);
|
|
|
|
if (result != 0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
2015-09-08 22:16:15 +00:00
|
|
|
|
|
|
|
// Select the DATA_READY event source to be routed to INT1
|
2015-10-25 21:51:33 +00:00
|
|
|
result = writeCommand(MMA8653_CTRL_REG5, 0x01);
|
|
|
|
if (result != 0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
2015-09-08 22:16:15 +00:00
|
|
|
|
|
|
|
// Configure for the selected g range.
|
2015-10-25 21:51:33 +00:00
|
|
|
result = writeCommand(MMA8653_XYZ_DATA_CFG, actualSampleRange->xyz_data_cfg);
|
|
|
|
if (result != 0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
2016-01-05 15:44:57 +00:00
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
// Bring the device back online, with 10bit wide samples at the requested frequency.
|
2015-10-25 21:51:33 +00:00
|
|
|
result = writeCommand(MMA8653_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01);
|
|
|
|
if (result != 0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-09-08 22:16:15 +00:00
|
|
|
}
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* 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.
|
|
|
|
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitAccelerometer::writeCommand(uint8_t reg, uint8_t value)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
uint8_t command[2];
|
|
|
|
command[0] = reg;
|
|
|
|
command[1] = value;
|
2016-01-05 15:44:57 +00:00
|
|
|
|
2015-10-25 21:51:33 +00:00
|
|
|
return uBit.i2c.write(address, (const char *)command, 2);
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* 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.
|
|
|
|
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitAccelerometer::readCommand(uint8_t reg, uint8_t* buffer, int length)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-10-25 21:51:33 +00:00
|
|
|
int result;
|
|
|
|
|
|
|
|
if (buffer == NULL || length <= 0 )
|
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
result = uBit.i2c.write(address, (const char *)®, 1, true);
|
|
|
|
if (result !=0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
|
|
|
|
|
|
|
result = uBit.i2c.read(address, (char *)buffer, length);
|
|
|
|
if (result !=0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* 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
|
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
MicroBitAccelerometer::MicroBitAccelerometer(uint16_t id, uint16_t address) : sample(), int1(MICROBIT_PIN_ACCEL_DATA_READY)
|
|
|
|
{
|
|
|
|
// Store our identifiers.
|
|
|
|
this->id = id;
|
2015-12-17 14:08:30 +00:00
|
|
|
this->status = 0;
|
2015-08-12 10:53:41 +00:00
|
|
|
this->address = address;
|
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
// Update our internal state for 50Hz at +/- 2g (50Hz has a period af 20ms).
|
|
|
|
this->samplePeriod = 20;
|
|
|
|
this->sampleRange = 2;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2016-01-05 15:44:57 +00:00
|
|
|
// Initialise gesture history
|
|
|
|
this->sigma = 0;
|
|
|
|
this->lastGesture = GESTURE_NONE;
|
|
|
|
this->currentGesture = GESTURE_NONE;
|
|
|
|
this->shake.x = 0;
|
|
|
|
this->shake.y = 0;
|
|
|
|
this->shake.z = 0;
|
|
|
|
this->shake.count = 0;
|
|
|
|
this->shake.timer = 0;
|
2015-12-03 15:11:31 +00:00
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
// Configure and enable the accelerometer.
|
2015-10-25 21:51:33 +00:00
|
|
|
if (this->configure() == MICROBIT_OK)
|
|
|
|
uBit.flags |= MICROBIT_FLAG_ACCELEROMETER_RUNNING;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* Attempts to determine the 8 bit ID from the accelerometer.
|
|
|
|
* @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.accelerometer.whoAmI();
|
|
|
|
* @endcode
|
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
int MicroBitAccelerometer::whoAmI()
|
|
|
|
{
|
|
|
|
uint8_t data;
|
2015-10-25 21:51:33 +00:00
|
|
|
int result;
|
|
|
|
|
|
|
|
result = readCommand(MMA8653_WHOAMI, &data, 1);
|
|
|
|
if (result !=0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
return (int)data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* 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!
|
|
|
|
*
|
|
|
|
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the read request fails.
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitAccelerometer::update()
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
int8_t data[6];
|
2015-10-25 21:51:33 +00:00
|
|
|
int result;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-10-25 21:51:33 +00:00
|
|
|
result = readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6);
|
|
|
|
if (result !=0)
|
|
|
|
return MICROBIT_I2C_ERROR;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
// read MSB values...
|
|
|
|
sample.x = data[0];
|
|
|
|
sample.y = data[2];
|
|
|
|
sample.z = data[4];
|
2016-01-05 15:44:57 +00:00
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
// Normalize the data in the 0..1024 range.
|
|
|
|
sample.x *= 8;
|
|
|
|
sample.y *= 8;
|
|
|
|
sample.z *= 8;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-09-02 11:42:24 +00:00
|
|
|
#if CONFIG_ENABLED(USE_ACCEL_LSB)
|
2015-08-12 10:53:41 +00:00
|
|
|
// Add in LSB values.
|
|
|
|
sample.x += (data[1] / 64);
|
|
|
|
sample.y += (data[3] / 64);
|
|
|
|
sample.z += (data[5] / 64);
|
|
|
|
#endif
|
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
// Scale into millig (approx!)
|
|
|
|
sample.x *= this->sampleRange;
|
|
|
|
sample.y *= this->sampleRange;
|
|
|
|
sample.z *= this->sampleRange;
|
|
|
|
|
2016-01-09 19:10:04 +00:00
|
|
|
// Indicate that pitch and roll data is now stale, and needs to be recalculated if needed.
|
2015-12-17 14:08:30 +00:00
|
|
|
status &= ~MICROBIT_ACCEL_PITCH_ROLL_VALID;
|
|
|
|
|
2016-01-05 15:44:57 +00:00
|
|
|
// Update gesture tracking
|
|
|
|
updateGesture();
|
2015-12-03 15:11:31 +00:00
|
|
|
|
2015-09-19 20:00:38 +00:00
|
|
|
// Indicate that a new sample is available
|
|
|
|
MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE);
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
};
|
|
|
|
|
2015-12-03 15:11:31 +00:00
|
|
|
/**
|
|
|
|
* Service function. Calculates the current scalar acceleration of the device (x^2 + y^2 + z^2).
|
|
|
|
* It does not, however, square root the result, as this is a relatively high cost operation.
|
|
|
|
* This is left to application code should it be needed.
|
|
|
|
*
|
|
|
|
* @return the sum of the square of the acceleration of the device across all axes.
|
|
|
|
*/
|
2016-01-05 16:23:47 +00:00
|
|
|
int MicroBitAccelerometer::instantaneousAccelerationSquared()
|
2015-12-03 15:11:31 +00:00
|
|
|
{
|
2016-01-05 15:44:57 +00:00
|
|
|
// Use pythagoras theorem to determine the combined force acting on the device.
|
|
|
|
return (int)sample.x*(int)sample.x + (int)sample.y*(int)sample.y + (int)sample.z*(int)sample.z;
|
2015-12-03 15:11:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Service function. Determines the best guess posture of the device based on instantaneous data.
|
|
|
|
* This makes no use of historic data (except for shake), and forms this input to the filter implemented in updateGesture().
|
|
|
|
*
|
|
|
|
* @return A best guess of the current posture of the device, based on instantaneous data.
|
|
|
|
*/
|
2016-01-05 16:23:47 +00:00
|
|
|
BasicGesture MicroBitAccelerometer::instantaneousPosture()
|
2015-12-03 15:11:31 +00:00
|
|
|
{
|
2016-01-05 15:44:57 +00:00
|
|
|
int force = instantaneousAccelerationSquared();
|
|
|
|
bool shakeDetected = false;
|
|
|
|
|
|
|
|
// Test for shake events.
|
|
|
|
// We detect a shake by measuring zero crossings in each axis. In other words, if we see a strong acceleration to the left followed by
|
|
|
|
// a string acceleration to the right, then we can infer a shake. Similarly, we can do this for each acxis (left/right, up/down, in/out).
|
|
|
|
//
|
|
|
|
// If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device
|
|
|
|
// has been shaken.
|
|
|
|
if ((sample.x < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (sample.x > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x))
|
|
|
|
{
|
|
|
|
shakeDetected = true;
|
|
|
|
shake.x = !shake.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sample.y < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (sample.y > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y))
|
|
|
|
{
|
|
|
|
shakeDetected = true;
|
|
|
|
shake.y = !shake.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sample.z < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (sample.z > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z))
|
|
|
|
{
|
|
|
|
shakeDetected = true;
|
|
|
|
shake.z = !shake.z;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shakeDetected && shake.count < MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD && ++shake.count == MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD)
|
|
|
|
shake.shaken = 1;
|
|
|
|
|
|
|
|
if (++shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_DAMPING)
|
|
|
|
{
|
|
|
|
shake.timer = 0;
|
|
|
|
if (shake.count > 0)
|
|
|
|
{
|
|
|
|
if(--shake.count == 0)
|
|
|
|
shake.shaken = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shake.shaken)
|
|
|
|
return GESTURE_SHAKE;
|
|
|
|
|
|
|
|
if (force < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD)
|
2016-01-05 16:23:47 +00:00
|
|
|
return GESTURE_FREEFALL;
|
2016-01-05 15:44:57 +00:00
|
|
|
|
|
|
|
if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD)
|
|
|
|
return GESTURE_3G;
|
|
|
|
|
|
|
|
if (force > MICROBIT_ACCELEROMETER_6G_THRESHOLD)
|
|
|
|
return GESTURE_6G;
|
|
|
|
|
|
|
|
if (force > MICROBIT_ACCELEROMETER_8G_THRESHOLD)
|
|
|
|
return GESTURE_8G;
|
|
|
|
|
|
|
|
// Determine our posture.
|
|
|
|
if (sample.x < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
|
|
|
return GESTURE_LEFT;
|
|
|
|
|
|
|
|
if (sample.x > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
|
|
|
return GESTURE_RIGHT;
|
|
|
|
|
|
|
|
if (sample.y < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
|
|
|
return GESTURE_DOWN;
|
|
|
|
|
|
|
|
if (sample.y > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
|
|
|
return GESTURE_UP;
|
|
|
|
|
|
|
|
if (sample.z < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
|
|
|
return GESTURE_FACE_UP;
|
|
|
|
|
|
|
|
if (sample.z > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
|
|
|
|
return GESTURE_FACE_DOWN;
|
|
|
|
|
|
|
|
return GESTURE_NONE;
|
2015-12-03 15:11:31 +00:00
|
|
|
}
|
|
|
|
|
2016-01-05 16:23:47 +00:00
|
|
|
void MicroBitAccelerometer::updateGesture()
|
2015-12-03 15:11:31 +00:00
|
|
|
{
|
2016-01-05 15:44:57 +00:00
|
|
|
// Determine what it looks like we're doing based on the latest sample...
|
|
|
|
BasicGesture g = instantaneousPosture();
|
|
|
|
|
|
|
|
// Perform some low pass filtering to reduce jitter from any detected effects
|
|
|
|
if (g == currentGesture)
|
|
|
|
{
|
|
|
|
if (sigma < MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
|
|
|
|
sigma++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
currentGesture = g;
|
|
|
|
sigma = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we've reached threshold, update our record and raise the relevant event...
|
|
|
|
if (currentGesture != lastGesture && sigma >= MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
|
|
|
|
{
|
|
|
|
lastGesture = currentGesture;
|
|
|
|
MicroBitEvent e(MICROBIT_ID_GESTURE, lastGesture);
|
|
|
|
}
|
2015-12-03 15:11:31 +00:00
|
|
|
}
|
|
|
|
|
2015-09-08 22:16:15 +00:00
|
|
|
/**
|
|
|
|
* Attempts to set the sample rate of the accelerometer to the specified value (in ms).
|
|
|
|
* n.b. the requested rate may not be possible on the hardware. In this case, the
|
|
|
|
* nearest lower rate is chosen.
|
|
|
|
* @param period the requested time between samples, in milliseconds.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
|
2015-09-08 22:16:15 +00:00
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitAccelerometer::setPeriod(int period)
|
2015-09-08 22:16:15 +00:00
|
|
|
{
|
|
|
|
this->samplePeriod = period;
|
2015-10-25 21:51:33 +00:00
|
|
|
return this->configure();
|
2015-09-08 22:16:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the currently configured sample rate of the accelerometer.
|
|
|
|
* @return The time between samples, in milliseconds.
|
|
|
|
*/
|
|
|
|
int MicroBitAccelerometer::getPeriod()
|
|
|
|
{
|
|
|
|
return (int)samplePeriod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to set the sample range of the accelerometer to the specified value (in g).
|
|
|
|
* n.b. the requested range may not be possible on the hardware. In this case, the
|
|
|
|
* nearest lower rate is chosen.
|
|
|
|
* @param range The requested sample range of samples, in g.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
|
2015-09-08 22:16:15 +00:00
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitAccelerometer::setRange(int range)
|
2015-09-08 22:16:15 +00:00
|
|
|
{
|
|
|
|
this->sampleRange = range;
|
2015-10-25 21:51:33 +00:00
|
|
|
return this->configure();
|
2015-09-08 22:16:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the currently configured sample range of the accelerometer.
|
|
|
|
* @return The sample range, in g.
|
|
|
|
*/
|
|
|
|
int MicroBitAccelerometer::getRange()
|
|
|
|
{
|
|
|
|
return (int)sampleRange;
|
|
|
|
}
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
/**
|
|
|
|
* Reads the X axis value of the latest update from the accelerometer.
|
2015-12-17 14:08:30 +00:00
|
|
|
* @param system The coordinate system to use. By default, a simple cartesian system is provided.
|
2015-08-12 10:53:41 +00:00
|
|
|
* @return The force measured in the X axis, in milli-g.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.accelerometer.getX();
|
2015-12-17 14:08:30 +00:00
|
|
|
* uBit.accelerometer.getX(RAW);
|
2015-08-12 10:53:41 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-12-17 14:08:30 +00:00
|
|
|
int MicroBitAccelerometer::getX(MicroBitCoordinateSystem system)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-12-17 14:08:30 +00:00
|
|
|
switch (system)
|
|
|
|
{
|
|
|
|
case SIMPLE_CARTESIAN:
|
|
|
|
return -sample.x;
|
|
|
|
|
|
|
|
case NORTH_EAST_DOWN:
|
|
|
|
return sample.y;
|
|
|
|
|
|
|
|
case RAW:
|
|
|
|
default:
|
|
|
|
return sample.x;
|
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the Y axis value of the latest update from the accelerometer.
|
2015-12-17 14:08:30 +00:00
|
|
|
* @param system The coordinate system to use. By default, a simple cartesian system is provided.
|
2015-08-12 10:53:41 +00:00
|
|
|
* @return The force measured in the Y axis, in milli-g.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.accelerometer.getY();
|
2015-12-17 14:08:30 +00:00
|
|
|
* uBit.accelerometer.getY(RAW);
|
2015-08-12 10:53:41 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-12-17 14:08:30 +00:00
|
|
|
int MicroBitAccelerometer::getY(MicroBitCoordinateSystem system)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-12-17 14:08:30 +00:00
|
|
|
switch (system)
|
|
|
|
{
|
|
|
|
case SIMPLE_CARTESIAN:
|
|
|
|
return -sample.y;
|
|
|
|
|
|
|
|
case NORTH_EAST_DOWN:
|
|
|
|
return -sample.x;
|
|
|
|
|
|
|
|
case RAW:
|
|
|
|
default:
|
|
|
|
return sample.y;
|
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the Z axis value of the latest update from the accelerometer.
|
2015-12-17 14:08:30 +00:00
|
|
|
* @param system The coordinate system to use. By default, a simple cartesian system is provided.
|
2015-08-12 10:53:41 +00:00
|
|
|
* @return The force measured in the Z axis, in milli-g.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.accelerometer.getZ();
|
2015-12-17 14:08:30 +00:00
|
|
|
* uBit.accelerometer.getZ(RAW);
|
2015-08-12 10:53:41 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-12-17 14:08:30 +00:00
|
|
|
int MicroBitAccelerometer::getZ(MicroBitCoordinateSystem system)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-12-17 14:08:30 +00:00
|
|
|
switch (system)
|
|
|
|
{
|
|
|
|
case NORTH_EAST_DOWN:
|
|
|
|
return -sample.z;
|
|
|
|
|
|
|
|
case SIMPLE_CARTESIAN:
|
|
|
|
case RAW:
|
|
|
|
default:
|
|
|
|
return sample.z;
|
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
2015-09-19 20:00:38 +00:00
|
|
|
|
2015-12-17 14:08:30 +00:00
|
|
|
/**
|
|
|
|
* Provides a rotation compensated pitch of the device, based on the latest update from the accelerometer.
|
|
|
|
* @return The pitch of the device, in degrees.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.accelerometer.getPitch();
|
|
|
|
* @endcode
|
|
|
|
*/
|
|
|
|
int MicroBitAccelerometer::getPitch()
|
|
|
|
{
|
|
|
|
return (int) ((360*getPitchRadians()) / (2*PI));
|
|
|
|
}
|
|
|
|
|
|
|
|
float MicroBitAccelerometer::getPitchRadians()
|
|
|
|
{
|
|
|
|
if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID))
|
|
|
|
recalculatePitchRoll();
|
|
|
|
|
|
|
|
return pitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Provides a rotation compensated roll of the device, based on the latest update from the accelerometer.
|
|
|
|
* @return The roll of the device, in degrees.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.accelerometer.getRoll();
|
|
|
|
* @endcode
|
|
|
|
*/
|
|
|
|
int MicroBitAccelerometer::getRoll()
|
|
|
|
{
|
|
|
|
return (int) ((360*getRollRadians()) / (2*PI));
|
|
|
|
}
|
|
|
|
|
|
|
|
float MicroBitAccelerometer::getRollRadians()
|
|
|
|
{
|
|
|
|
if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID))
|
|
|
|
recalculatePitchRoll();
|
|
|
|
|
|
|
|
return roll;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recalculate roll and pitch values for the current sample.
|
|
|
|
* We only do this at most once per sample, as the necessary trigonemteric functions are rather
|
|
|
|
* heavyweight for a CPU without a floating point unit...
|
|
|
|
*/
|
|
|
|
void MicroBitAccelerometer::recalculatePitchRoll()
|
|
|
|
{
|
|
|
|
float x = (float) getX(NORTH_EAST_DOWN);
|
|
|
|
float y = (float) getY(NORTH_EAST_DOWN);
|
|
|
|
float z = (float) getZ(NORTH_EAST_DOWN);
|
|
|
|
|
|
|
|
roll = atan2(getY(NORTH_EAST_DOWN), getZ(NORTH_EAST_DOWN));
|
|
|
|
pitch = atan(-x / (y*sin(roll) + z*cos(roll)));
|
|
|
|
status |= MICROBIT_ACCEL_PITCH_ROLL_VALID;
|
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
/**
|
2015-12-03 15:11:31 +00:00
|
|
|
* Reads the last recorded gesture detected.
|
|
|
|
* @return The last gesture detected.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* if (uBit.accelerometer.getGesture() == SHAKE)
|
|
|
|
* @endcode
|
|
|
|
*/
|
|
|
|
BasicGesture MicroBitAccelerometer::getGesture()
|
|
|
|
{
|
2016-01-05 15:44:57 +00:00
|
|
|
return lastGesture;
|
2015-12-03 15:11:31 +00:00
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* periodic callback from MicroBit clock.
|
|
|
|
* Check if any data is ready for reading by checking the interrupt flag on the accelerometer
|
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
void MicroBitAccelerometer::idleTick()
|
|
|
|
{
|
|
|
|
// Poll interrupt line from accelerometer.
|
|
|
|
// n.b. Default is Active LO. Interrupt is cleared in data read.
|
2015-09-08 22:16:15 +00:00
|
|
|
//
|
2015-08-12 10:53:41 +00:00
|
|
|
if(!int1)
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-05 15:44:57 +00:00
|
|
|
* Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
|
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
int MicroBitAccelerometer::isIdleCallbackNeeded()
|
|
|
|
{
|
|
|
|
return !int1;
|
|
|
|
}
|
2015-09-08 22:16:15 +00:00
|
|
|
|
|
|
|
const MMA8653SampleRangeConfig MMA8653SampleRange[MMA8653_SAMPLE_RANGES] = {
|
|
|
|
{2, 0},
|
|
|
|
{4, 1},
|
|
|
|
{8, 2}
|
|
|
|
};
|
|
|
|
|
|
|
|
const MMA8653SampleRateConfig MMA8653SampleRate[MMA8653_SAMPLE_RATES] = {
|
|
|
|
{1250, 0x00},
|
|
|
|
{2500, 0x08},
|
|
|
|
{5000, 0x10},
|
|
|
|
{10000, 0x18},
|
|
|
|
{20000, 0x20},
|
|
|
|
{80000, 0x28},
|
|
|
|
{160000, 0x30},
|
|
|
|
{640000, 0x38}
|
|
|
|
};
|