From 9ccf0a76882de93f481139ecb7d1f67f57b71ab6 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 8 Sep 2015 23:16:15 +0100 Subject: [PATCH] microbit: Added configuration support for accelerometer sample rate and range MicroBitAccelerometer now contans methods to allow dynamic selection of sample range (from +/-2g to +/-8g) and sample frequency (up to 800Hz). The default settings and behaviour remain consistent. --- inc/MicroBitAccelerometer.h | 59 +++++++++++- source/MicroBitAccelerometer.cpp | 153 ++++++++++++++++++++++++++----- 2 files changed, 188 insertions(+), 24 deletions(-) diff --git a/inc/MicroBitAccelerometer.h b/inc/MicroBitAccelerometer.h index a4cda4a..a6008bf 100644 --- a/inc/MicroBitAccelerometer.h +++ b/inc/MicroBitAccelerometer.h @@ -32,6 +32,10 @@ */ #define MMA8653_WHOAMI_VAL 0x5A +#define MMA8653_SAMPLE_RANGES 3 +#define MMA8653_SAMPLE_RATES 8 + + struct MMA8653Sample { int16_t x; @@ -39,6 +43,21 @@ struct MMA8653Sample int16_t z; }; +struct MMA8653SampleRateConfig +{ + uint32_t sample_period; + uint8_t ctrl_reg1; +}; + +struct MMA8653SampleRangeConfig +{ + uint8_t sample_range; + uint8_t xyz_data_cfg; +}; + +extern const MMA8653SampleRangeConfig MMA8653SampleRange[]; +extern const MMA8653SampleRateConfig MMA8653SampleRate[]; + /** * Class definition for MicroBit Accelerometer. * @@ -52,8 +71,9 @@ class MicroBitAccelerometer : public MicroBitComponent * Used to track asynchronous events in the event bus. */ - //if you are adding status here - don't it's in MicroBitComponent!!! uint16_t address; // I2C address of this accelerometer. + uint16_t samplePeriod; // The time between samples, in milliseconds. + uint8_t sampleRange; // The sample range of the accelerometer in g. MMA8653Sample sample; // The last sample read. DigitalIn int1; // Data ready interrupt. @@ -72,13 +92,48 @@ class MicroBitAccelerometer : public MicroBitComponent */ MicroBitAccelerometer(uint16_t id, uint16_t address); + /** + * 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. + */ + void configure(); + /** * 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 update(); - + /** + * 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. + */ + void setPeriod(int period); + + /** + * Reads the currently configured sample rate of the accelerometer. + * @return The time between samples, in milliseconds. + */ + int getPeriod(); + + /** + * 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. + */ + void setRange(int range); + + /** + * Reads the currently configured sample range of the accelerometer. + * @return The sample range, in g. + */ + int getRange(); + /** * Attempts to determine the 8 bit ID from the accelerometer. * @return the 8 bit ID returned by the accelerometer diff --git a/source/MicroBitAccelerometer.cpp b/source/MicroBitAccelerometer.cpp index 7c04c0a..c87e117 100644 --- a/source/MicroBitAccelerometer.cpp +++ b/source/MicroBitAccelerometer.cpp @@ -7,6 +7,61 @@ #include "MicroBit.h" +/** + * 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. + */ +void MicroBitAccelerometer::configure() +{ + const MMA8653SampleRangeConfig *actualSampleRange; + const MMA8653SampleRateConfig *actualSampleRate; + + // 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. + writeCommand(MMA8653_CTRL_REG1, 0x00); + + // Enable high precisiosn mode. This consumes a bit more power, but still only 184 uA! + writeCommand(MMA8653_CTRL_REG2, 0x10); + + // 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 the selected g range. + writeCommand(MMA8653_XYZ_DATA_CFG, actualSampleRange->xyz_data_cfg); + + // Bring the device back online, with 10bit wide samples at the requested frequency. + writeCommand(MMA8653_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01); +} + /** * Issues a standard, 2 byte I2C command write to the accelerometer. * Blocks the calling thread until complete. @@ -54,21 +109,12 @@ MicroBitAccelerometer::MicroBitAccelerometer(uint16_t id, uint16_t address) : sa 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); + // Update our internal state for 50Hz at +/- 2g (50Hz has a period af 20ms). + this->samplePeriod = 20; + this->sampleRange = 2; - // 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); + // Configure and enable the accelerometer. + this->configure(); // indicate that we're ready to receive tick callbacks. uBit.flags |= MICROBIT_FLAG_ACCELEROMETER_RUNNING; @@ -98,6 +144,7 @@ int MicroBitAccelerometer::whoAmI() void MicroBitAccelerometer::update() { int8_t data[6]; + static int count=0; readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6); @@ -105,19 +152,16 @@ void MicroBitAccelerometer::update() 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; + + // Normalize the data in the 0..1024 range. + sample.x *= 8; + sample.y *= 8; + sample.z *= 8; // 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. - #if CONFIG_ENABLED(USE_ACCEL_LSB) // Add in LSB values. sample.x += (data[1] / 64); @@ -125,9 +169,56 @@ void MicroBitAccelerometer::update() sample.z += (data[5] / 64); #endif + // Scale into millig (approx!) + sample.x *= this->sampleRange; + sample.y *= this->sampleRange; + sample.z *= this->sampleRange; + //TODO: Issue an event. }; +/** + * 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. + */ +void MicroBitAccelerometer::setPeriod(int period) +{ + this->samplePeriod = period; + this->configure(); +} + +/** + * 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. + */ +void MicroBitAccelerometer::setRange(int range) +{ + this->sampleRange = range; + this->configure(); +} + +/** + * Reads the currently configured sample range of the accelerometer. + * @return The sample range, in g. + */ +int MicroBitAccelerometer::getRange() +{ + return (int)sampleRange; +} + /** * Reads the X axis value of the latest update from the accelerometer. * Currently limited to +/- 2g @@ -182,6 +273,7 @@ void MicroBitAccelerometer::idleTick() { // Poll interrupt line from accelerometer. // n.b. Default is Active LO. Interrupt is cleared in data read. + // if(!int1) update(); } @@ -193,3 +285,20 @@ int MicroBitAccelerometer::isIdleCallbackNeeded() { return !int1; } + +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} +};