Official Sheffield ARMBand micro:bit program
microbit/microbit-dal/source/drivers/MicroBitAccelerometer.cpp@0:b9164b348919, 2016-10-17 (annotated)
- Committer:
- MrBedfordVan
- Date:
- Mon Oct 17 12:41:20 2016 +0000
- Revision:
- 0:b9164b348919
Official Sheffield ARMBand Micro:bit program
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
MrBedfordVan | 0:b9164b348919 | 1 | /* |
MrBedfordVan | 0:b9164b348919 | 2 | The MIT License (MIT) |
MrBedfordVan | 0:b9164b348919 | 3 | |
MrBedfordVan | 0:b9164b348919 | 4 | Copyright (c) 2016 British Broadcasting Corporation. |
MrBedfordVan | 0:b9164b348919 | 5 | This software is provided by Lancaster University by arrangement with the BBC. |
MrBedfordVan | 0:b9164b348919 | 6 | |
MrBedfordVan | 0:b9164b348919 | 7 | Permission is hereby granted, free of charge, to any person obtaining a |
MrBedfordVan | 0:b9164b348919 | 8 | copy of this software and associated documentation files (the "Software"), |
MrBedfordVan | 0:b9164b348919 | 9 | to deal in the Software without restriction, including without limitation |
MrBedfordVan | 0:b9164b348919 | 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, |
MrBedfordVan | 0:b9164b348919 | 11 | and/or sell copies of the Software, and to permit persons to whom the |
MrBedfordVan | 0:b9164b348919 | 12 | Software is furnished to do so, subject to the following conditions: |
MrBedfordVan | 0:b9164b348919 | 13 | |
MrBedfordVan | 0:b9164b348919 | 14 | The above copyright notice and this permission notice shall be included in |
MrBedfordVan | 0:b9164b348919 | 15 | all copies or substantial portions of the Software. |
MrBedfordVan | 0:b9164b348919 | 16 | |
MrBedfordVan | 0:b9164b348919 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
MrBedfordVan | 0:b9164b348919 | 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
MrBedfordVan | 0:b9164b348919 | 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
MrBedfordVan | 0:b9164b348919 | 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
MrBedfordVan | 0:b9164b348919 | 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
MrBedfordVan | 0:b9164b348919 | 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
MrBedfordVan | 0:b9164b348919 | 23 | DEALINGS IN THE SOFTWARE. |
MrBedfordVan | 0:b9164b348919 | 24 | */ |
MrBedfordVan | 0:b9164b348919 | 25 | |
MrBedfordVan | 0:b9164b348919 | 26 | /** |
MrBedfordVan | 0:b9164b348919 | 27 | * Class definition for MicroBit Accelerometer. |
MrBedfordVan | 0:b9164b348919 | 28 | * |
MrBedfordVan | 0:b9164b348919 | 29 | * Represents an implementation of the Freescale MMA8653 3 axis accelerometer |
MrBedfordVan | 0:b9164b348919 | 30 | * Also includes basic data caching and on demand activation. |
MrBedfordVan | 0:b9164b348919 | 31 | */ |
MrBedfordVan | 0:b9164b348919 | 32 | #include "MicroBitConfig.h" |
MrBedfordVan | 0:b9164b348919 | 33 | #include "MicroBitAccelerometer.h" |
MrBedfordVan | 0:b9164b348919 | 34 | #include "ErrorNo.h" |
MrBedfordVan | 0:b9164b348919 | 35 | #include "MicroBitConfig.h" |
MrBedfordVan | 0:b9164b348919 | 36 | #include "MicroBitEvent.h" |
MrBedfordVan | 0:b9164b348919 | 37 | #include "MicroBitCompat.h" |
MrBedfordVan | 0:b9164b348919 | 38 | #include "MicroBitFiber.h" |
MrBedfordVan | 0:b9164b348919 | 39 | |
MrBedfordVan | 0:b9164b348919 | 40 | /** |
MrBedfordVan | 0:b9164b348919 | 41 | * Configures the accelerometer for G range and sample rate defined |
MrBedfordVan | 0:b9164b348919 | 42 | * in this object. The nearest values are chosen to those defined |
MrBedfordVan | 0:b9164b348919 | 43 | * that are supported by the hardware. The instance variables are then |
MrBedfordVan | 0:b9164b348919 | 44 | * updated to reflect reality. |
MrBedfordVan | 0:b9164b348919 | 45 | * |
MrBedfordVan | 0:b9164b348919 | 46 | * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the accelerometer could not be configured. |
MrBedfordVan | 0:b9164b348919 | 47 | */ |
MrBedfordVan | 0:b9164b348919 | 48 | int MicroBitAccelerometer::configure() |
MrBedfordVan | 0:b9164b348919 | 49 | { |
MrBedfordVan | 0:b9164b348919 | 50 | const MMA8653SampleRangeConfig *actualSampleRange; |
MrBedfordVan | 0:b9164b348919 | 51 | const MMA8653SampleRateConfig *actualSampleRate; |
MrBedfordVan | 0:b9164b348919 | 52 | int result; |
MrBedfordVan | 0:b9164b348919 | 53 | |
MrBedfordVan | 0:b9164b348919 | 54 | // First find the nearest sample rate to that specified. |
MrBedfordVan | 0:b9164b348919 | 55 | actualSampleRate = &MMA8653SampleRate[MMA8653_SAMPLE_RATES-1]; |
MrBedfordVan | 0:b9164b348919 | 56 | for (int i=MMA8653_SAMPLE_RATES-1; i>=0; i--) |
MrBedfordVan | 0:b9164b348919 | 57 | { |
MrBedfordVan | 0:b9164b348919 | 58 | if(MMA8653SampleRate[i].sample_period < this->samplePeriod * 1000) |
MrBedfordVan | 0:b9164b348919 | 59 | break; |
MrBedfordVan | 0:b9164b348919 | 60 | |
MrBedfordVan | 0:b9164b348919 | 61 | actualSampleRate = &MMA8653SampleRate[i]; |
MrBedfordVan | 0:b9164b348919 | 62 | } |
MrBedfordVan | 0:b9164b348919 | 63 | |
MrBedfordVan | 0:b9164b348919 | 64 | // Now find the nearest sample range to that specified. |
MrBedfordVan | 0:b9164b348919 | 65 | actualSampleRange = &MMA8653SampleRange[MMA8653_SAMPLE_RANGES-1]; |
MrBedfordVan | 0:b9164b348919 | 66 | for (int i=MMA8653_SAMPLE_RANGES-1; i>=0; i--) |
MrBedfordVan | 0:b9164b348919 | 67 | { |
MrBedfordVan | 0:b9164b348919 | 68 | if(MMA8653SampleRange[i].sample_range < this->sampleRange) |
MrBedfordVan | 0:b9164b348919 | 69 | break; |
MrBedfordVan | 0:b9164b348919 | 70 | |
MrBedfordVan | 0:b9164b348919 | 71 | actualSampleRange = &MMA8653SampleRange[i]; |
MrBedfordVan | 0:b9164b348919 | 72 | } |
MrBedfordVan | 0:b9164b348919 | 73 | |
MrBedfordVan | 0:b9164b348919 | 74 | // OK, we have the correct data. Update our local state. |
MrBedfordVan | 0:b9164b348919 | 75 | this->samplePeriod = actualSampleRate->sample_period / 1000; |
MrBedfordVan | 0:b9164b348919 | 76 | this->sampleRange = actualSampleRange->sample_range; |
MrBedfordVan | 0:b9164b348919 | 77 | |
MrBedfordVan | 0:b9164b348919 | 78 | // Now configure the accelerometer accordingly. |
MrBedfordVan | 0:b9164b348919 | 79 | // First place the device into standby mode, so it can be configured. |
MrBedfordVan | 0:b9164b348919 | 80 | result = writeCommand(MMA8653_CTRL_REG1, 0x00); |
MrBedfordVan | 0:b9164b348919 | 81 | if (result != 0) |
MrBedfordVan | 0:b9164b348919 | 82 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 83 | |
MrBedfordVan | 0:b9164b348919 | 84 | // Enable high precisiosn mode. This consumes a bit more power, but still only 184 uA! |
MrBedfordVan | 0:b9164b348919 | 85 | result = writeCommand(MMA8653_CTRL_REG2, 0x10); |
MrBedfordVan | 0:b9164b348919 | 86 | if (result != 0) |
MrBedfordVan | 0:b9164b348919 | 87 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 88 | |
MrBedfordVan | 0:b9164b348919 | 89 | // Enable the INT1 interrupt pin. |
MrBedfordVan | 0:b9164b348919 | 90 | result = writeCommand(MMA8653_CTRL_REG4, 0x01); |
MrBedfordVan | 0:b9164b348919 | 91 | if (result != 0) |
MrBedfordVan | 0:b9164b348919 | 92 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 93 | |
MrBedfordVan | 0:b9164b348919 | 94 | // Select the DATA_READY event source to be routed to INT1 |
MrBedfordVan | 0:b9164b348919 | 95 | result = writeCommand(MMA8653_CTRL_REG5, 0x01); |
MrBedfordVan | 0:b9164b348919 | 96 | if (result != 0) |
MrBedfordVan | 0:b9164b348919 | 97 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 98 | |
MrBedfordVan | 0:b9164b348919 | 99 | // Configure for the selected g range. |
MrBedfordVan | 0:b9164b348919 | 100 | result = writeCommand(MMA8653_XYZ_DATA_CFG, actualSampleRange->xyz_data_cfg); |
MrBedfordVan | 0:b9164b348919 | 101 | if (result != 0) |
MrBedfordVan | 0:b9164b348919 | 102 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 103 | |
MrBedfordVan | 0:b9164b348919 | 104 | // Bring the device back online, with 10bit wide samples at the requested frequency. |
MrBedfordVan | 0:b9164b348919 | 105 | result = writeCommand(MMA8653_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01); |
MrBedfordVan | 0:b9164b348919 | 106 | if (result != 0) |
MrBedfordVan | 0:b9164b348919 | 107 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 108 | |
MrBedfordVan | 0:b9164b348919 | 109 | return MICROBIT_OK; |
MrBedfordVan | 0:b9164b348919 | 110 | } |
MrBedfordVan | 0:b9164b348919 | 111 | |
MrBedfordVan | 0:b9164b348919 | 112 | /** |
MrBedfordVan | 0:b9164b348919 | 113 | * Issues a standard, 2 byte I2C command write to the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 114 | * |
MrBedfordVan | 0:b9164b348919 | 115 | * Blocks the calling thread until complete. |
MrBedfordVan | 0:b9164b348919 | 116 | * |
MrBedfordVan | 0:b9164b348919 | 117 | * @param reg The address of the register to write to. |
MrBedfordVan | 0:b9164b348919 | 118 | * |
MrBedfordVan | 0:b9164b348919 | 119 | * @param value The value to write. |
MrBedfordVan | 0:b9164b348919 | 120 | * |
MrBedfordVan | 0:b9164b348919 | 121 | * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed. |
MrBedfordVan | 0:b9164b348919 | 122 | */ |
MrBedfordVan | 0:b9164b348919 | 123 | int MicroBitAccelerometer::writeCommand(uint8_t reg, uint8_t value) |
MrBedfordVan | 0:b9164b348919 | 124 | { |
MrBedfordVan | 0:b9164b348919 | 125 | uint8_t command[2]; |
MrBedfordVan | 0:b9164b348919 | 126 | command[0] = reg; |
MrBedfordVan | 0:b9164b348919 | 127 | command[1] = value; |
MrBedfordVan | 0:b9164b348919 | 128 | |
MrBedfordVan | 0:b9164b348919 | 129 | return i2c.write(address, (const char *)command, 2); |
MrBedfordVan | 0:b9164b348919 | 130 | } |
MrBedfordVan | 0:b9164b348919 | 131 | |
MrBedfordVan | 0:b9164b348919 | 132 | /** |
MrBedfordVan | 0:b9164b348919 | 133 | * Issues a read command, copying data into the specified buffer. |
MrBedfordVan | 0:b9164b348919 | 134 | * |
MrBedfordVan | 0:b9164b348919 | 135 | * Blocks the calling thread until complete. |
MrBedfordVan | 0:b9164b348919 | 136 | * |
MrBedfordVan | 0:b9164b348919 | 137 | * @param reg The address of the register to access. |
MrBedfordVan | 0:b9164b348919 | 138 | * |
MrBedfordVan | 0:b9164b348919 | 139 | * @param buffer Memory area to read the data into. |
MrBedfordVan | 0:b9164b348919 | 140 | * |
MrBedfordVan | 0:b9164b348919 | 141 | * @param length The number of bytes to read. |
MrBedfordVan | 0:b9164b348919 | 142 | * |
MrBedfordVan | 0:b9164b348919 | 143 | * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed. |
MrBedfordVan | 0:b9164b348919 | 144 | */ |
MrBedfordVan | 0:b9164b348919 | 145 | int MicroBitAccelerometer::readCommand(uint8_t reg, uint8_t* buffer, int length) |
MrBedfordVan | 0:b9164b348919 | 146 | { |
MrBedfordVan | 0:b9164b348919 | 147 | int result; |
MrBedfordVan | 0:b9164b348919 | 148 | |
MrBedfordVan | 0:b9164b348919 | 149 | if (buffer == NULL || length <= 0 ) |
MrBedfordVan | 0:b9164b348919 | 150 | return MICROBIT_INVALID_PARAMETER; |
MrBedfordVan | 0:b9164b348919 | 151 | |
MrBedfordVan | 0:b9164b348919 | 152 | result = i2c.write(address, (const char *)®, 1, true); |
MrBedfordVan | 0:b9164b348919 | 153 | if (result !=0) |
MrBedfordVan | 0:b9164b348919 | 154 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 155 | |
MrBedfordVan | 0:b9164b348919 | 156 | result = i2c.read(address, (char *)buffer, length); |
MrBedfordVan | 0:b9164b348919 | 157 | if (result !=0) |
MrBedfordVan | 0:b9164b348919 | 158 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 159 | |
MrBedfordVan | 0:b9164b348919 | 160 | return MICROBIT_OK; |
MrBedfordVan | 0:b9164b348919 | 161 | } |
MrBedfordVan | 0:b9164b348919 | 162 | |
MrBedfordVan | 0:b9164b348919 | 163 | /** |
MrBedfordVan | 0:b9164b348919 | 164 | * Constructor. |
MrBedfordVan | 0:b9164b348919 | 165 | * Create a software abstraction of an accelerometer. |
MrBedfordVan | 0:b9164b348919 | 166 | * |
MrBedfordVan | 0:b9164b348919 | 167 | * @param _i2c an instance of MicroBitI2C used to communicate with the onboard accelerometer. |
MrBedfordVan | 0:b9164b348919 | 168 | * |
MrBedfordVan | 0:b9164b348919 | 169 | * @param address the default I2C address of the accelerometer. Defaults to: MMA8653_DEFAULT_ADDR. |
MrBedfordVan | 0:b9164b348919 | 170 | * |
MrBedfordVan | 0:b9164b348919 | 171 | * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER |
MrBedfordVan | 0:b9164b348919 | 172 | * |
MrBedfordVan | 0:b9164b348919 | 173 | * @code |
MrBedfordVan | 0:b9164b348919 | 174 | * MicroBitI2C i2c = MicroBitI2C(I2C_SDA0, I2C_SCL0); |
MrBedfordVan | 0:b9164b348919 | 175 | * |
MrBedfordVan | 0:b9164b348919 | 176 | * MicroBitAccelerometer accelerometer = MicroBitAccelerometer(i2c); |
MrBedfordVan | 0:b9164b348919 | 177 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 178 | */ |
MrBedfordVan | 0:b9164b348919 | 179 | MicroBitAccelerometer::MicroBitAccelerometer(MicroBitI2C& _i2c, uint16_t address, uint16_t id) : sample(), int1(MICROBIT_PIN_ACCEL_DATA_READY), i2c(_i2c) |
MrBedfordVan | 0:b9164b348919 | 180 | { |
MrBedfordVan | 0:b9164b348919 | 181 | // Store our identifiers. |
MrBedfordVan | 0:b9164b348919 | 182 | this->id = id; |
MrBedfordVan | 0:b9164b348919 | 183 | this->status = 0; |
MrBedfordVan | 0:b9164b348919 | 184 | this->address = address; |
MrBedfordVan | 0:b9164b348919 | 185 | |
MrBedfordVan | 0:b9164b348919 | 186 | // Update our internal state for 50Hz at +/- 2g (50Hz has a period af 20ms). |
MrBedfordVan | 0:b9164b348919 | 187 | this->samplePeriod = 20; |
MrBedfordVan | 0:b9164b348919 | 188 | this->sampleRange = 2; |
MrBedfordVan | 0:b9164b348919 | 189 | |
MrBedfordVan | 0:b9164b348919 | 190 | // Initialise gesture history |
MrBedfordVan | 0:b9164b348919 | 191 | this->sigma = 0; |
MrBedfordVan | 0:b9164b348919 | 192 | this->impulseSigma = 0; |
MrBedfordVan | 0:b9164b348919 | 193 | this->lastGesture = MICROBIT_ACCELEROMETER_EVT_NONE; |
MrBedfordVan | 0:b9164b348919 | 194 | this->currentGesture = MICROBIT_ACCELEROMETER_EVT_NONE; |
MrBedfordVan | 0:b9164b348919 | 195 | this->shake.x = 0; |
MrBedfordVan | 0:b9164b348919 | 196 | this->shake.y = 0; |
MrBedfordVan | 0:b9164b348919 | 197 | this->shake.z = 0; |
MrBedfordVan | 0:b9164b348919 | 198 | this->shake.count = 0; |
MrBedfordVan | 0:b9164b348919 | 199 | this->shake.timer = 0; |
MrBedfordVan | 0:b9164b348919 | 200 | this->shake.impulse_3 = 1; |
MrBedfordVan | 0:b9164b348919 | 201 | this->shake.impulse_6 = 1; |
MrBedfordVan | 0:b9164b348919 | 202 | this->shake.impulse_8 = 1; |
MrBedfordVan | 0:b9164b348919 | 203 | |
MrBedfordVan | 0:b9164b348919 | 204 | // Configure and enable the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 205 | if (this->configure() == MICROBIT_OK) |
MrBedfordVan | 0:b9164b348919 | 206 | status |= MICROBIT_COMPONENT_RUNNING; |
MrBedfordVan | 0:b9164b348919 | 207 | } |
MrBedfordVan | 0:b9164b348919 | 208 | |
MrBedfordVan | 0:b9164b348919 | 209 | /** |
MrBedfordVan | 0:b9164b348919 | 210 | * Attempts to read the 8 bit ID from the accelerometer, this can be used for |
MrBedfordVan | 0:b9164b348919 | 211 | * validation purposes. |
MrBedfordVan | 0:b9164b348919 | 212 | * |
MrBedfordVan | 0:b9164b348919 | 213 | * @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails. |
MrBedfordVan | 0:b9164b348919 | 214 | * |
MrBedfordVan | 0:b9164b348919 | 215 | * @code |
MrBedfordVan | 0:b9164b348919 | 216 | * accelerometer.whoAmI(); |
MrBedfordVan | 0:b9164b348919 | 217 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 218 | */ |
MrBedfordVan | 0:b9164b348919 | 219 | int MicroBitAccelerometer::whoAmI() |
MrBedfordVan | 0:b9164b348919 | 220 | { |
MrBedfordVan | 0:b9164b348919 | 221 | uint8_t data; |
MrBedfordVan | 0:b9164b348919 | 222 | int result; |
MrBedfordVan | 0:b9164b348919 | 223 | |
MrBedfordVan | 0:b9164b348919 | 224 | result = readCommand(MMA8653_WHOAMI, &data, 1); |
MrBedfordVan | 0:b9164b348919 | 225 | if (result !=0) |
MrBedfordVan | 0:b9164b348919 | 226 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 227 | |
MrBedfordVan | 0:b9164b348919 | 228 | return (int)data; |
MrBedfordVan | 0:b9164b348919 | 229 | } |
MrBedfordVan | 0:b9164b348919 | 230 | |
MrBedfordVan | 0:b9164b348919 | 231 | /** |
MrBedfordVan | 0:b9164b348919 | 232 | * Reads the acceleration data from the accelerometer, and stores it in our buffer. |
MrBedfordVan | 0:b9164b348919 | 233 | * This only happens if the accelerometer indicates that it has new data via int1. |
MrBedfordVan | 0:b9164b348919 | 234 | * |
MrBedfordVan | 0:b9164b348919 | 235 | * On first use, this member function will attempt to add this component to the |
MrBedfordVan | 0:b9164b348919 | 236 | * list of fiber components in order to constantly update the values stored |
MrBedfordVan | 0:b9164b348919 | 237 | * by this object. |
MrBedfordVan | 0:b9164b348919 | 238 | * |
MrBedfordVan | 0:b9164b348919 | 239 | * This technique is called lazy instantiation, and it means that we do not |
MrBedfordVan | 0:b9164b348919 | 240 | * obtain the overhead from non-chalantly adding this component to fiber components. |
MrBedfordVan | 0:b9164b348919 | 241 | * |
MrBedfordVan | 0:b9164b348919 | 242 | * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails. |
MrBedfordVan | 0:b9164b348919 | 243 | */ |
MrBedfordVan | 0:b9164b348919 | 244 | int MicroBitAccelerometer::updateSample() |
MrBedfordVan | 0:b9164b348919 | 245 | { |
MrBedfordVan | 0:b9164b348919 | 246 | if(!(status & MICROBIT_ACCEL_ADDED_TO_IDLE)) |
MrBedfordVan | 0:b9164b348919 | 247 | { |
MrBedfordVan | 0:b9164b348919 | 248 | fiber_add_idle_component(this); |
MrBedfordVan | 0:b9164b348919 | 249 | status |= MICROBIT_ACCEL_ADDED_TO_IDLE; |
MrBedfordVan | 0:b9164b348919 | 250 | } |
MrBedfordVan | 0:b9164b348919 | 251 | |
MrBedfordVan | 0:b9164b348919 | 252 | // Poll interrupt line from accelerometer. |
MrBedfordVan | 0:b9164b348919 | 253 | // n.b. Default is Active LO. Interrupt is cleared in data read. |
MrBedfordVan | 0:b9164b348919 | 254 | if(!int1) |
MrBedfordVan | 0:b9164b348919 | 255 | { |
MrBedfordVan | 0:b9164b348919 | 256 | int8_t data[6]; |
MrBedfordVan | 0:b9164b348919 | 257 | int result; |
MrBedfordVan | 0:b9164b348919 | 258 | |
MrBedfordVan | 0:b9164b348919 | 259 | result = readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6); |
MrBedfordVan | 0:b9164b348919 | 260 | if (result !=0) |
MrBedfordVan | 0:b9164b348919 | 261 | return MICROBIT_I2C_ERROR; |
MrBedfordVan | 0:b9164b348919 | 262 | |
MrBedfordVan | 0:b9164b348919 | 263 | // read MSB values... |
MrBedfordVan | 0:b9164b348919 | 264 | sample.x = data[0]; |
MrBedfordVan | 0:b9164b348919 | 265 | sample.y = data[2]; |
MrBedfordVan | 0:b9164b348919 | 266 | sample.z = data[4]; |
MrBedfordVan | 0:b9164b348919 | 267 | |
MrBedfordVan | 0:b9164b348919 | 268 | // Normalize the data in the 0..1024 range. |
MrBedfordVan | 0:b9164b348919 | 269 | sample.x *= 8; |
MrBedfordVan | 0:b9164b348919 | 270 | sample.y *= 8; |
MrBedfordVan | 0:b9164b348919 | 271 | sample.z *= 8; |
MrBedfordVan | 0:b9164b348919 | 272 | |
MrBedfordVan | 0:b9164b348919 | 273 | #if CONFIG_ENABLED(USE_ACCEL_LSB) |
MrBedfordVan | 0:b9164b348919 | 274 | // Add in LSB values. |
MrBedfordVan | 0:b9164b348919 | 275 | sample.x += (data[1] / 64); |
MrBedfordVan | 0:b9164b348919 | 276 | sample.y += (data[3] / 64); |
MrBedfordVan | 0:b9164b348919 | 277 | sample.z += (data[5] / 64); |
MrBedfordVan | 0:b9164b348919 | 278 | #endif |
MrBedfordVan | 0:b9164b348919 | 279 | |
MrBedfordVan | 0:b9164b348919 | 280 | // Scale into millig (approx!) |
MrBedfordVan | 0:b9164b348919 | 281 | sample.x *= this->sampleRange; |
MrBedfordVan | 0:b9164b348919 | 282 | sample.y *= this->sampleRange; |
MrBedfordVan | 0:b9164b348919 | 283 | sample.z *= this->sampleRange; |
MrBedfordVan | 0:b9164b348919 | 284 | |
MrBedfordVan | 0:b9164b348919 | 285 | // Indicate that pitch and roll data is now stale, and needs to be recalculated if needed. |
MrBedfordVan | 0:b9164b348919 | 286 | status &= ~MICROBIT_ACCEL_PITCH_ROLL_VALID; |
MrBedfordVan | 0:b9164b348919 | 287 | |
MrBedfordVan | 0:b9164b348919 | 288 | // Update gesture tracking |
MrBedfordVan | 0:b9164b348919 | 289 | updateGesture(); |
MrBedfordVan | 0:b9164b348919 | 290 | |
MrBedfordVan | 0:b9164b348919 | 291 | // Indicate that a new sample is available |
MrBedfordVan | 0:b9164b348919 | 292 | MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE); |
MrBedfordVan | 0:b9164b348919 | 293 | } |
MrBedfordVan | 0:b9164b348919 | 294 | |
MrBedfordVan | 0:b9164b348919 | 295 | return MICROBIT_OK; |
MrBedfordVan | 0:b9164b348919 | 296 | }; |
MrBedfordVan | 0:b9164b348919 | 297 | |
MrBedfordVan | 0:b9164b348919 | 298 | /** |
MrBedfordVan | 0:b9164b348919 | 299 | * A service function. |
MrBedfordVan | 0:b9164b348919 | 300 | * It calculates the current scalar acceleration of the device (x^2 + y^2 + z^2). |
MrBedfordVan | 0:b9164b348919 | 301 | * It does not, however, square root the result, as this is a relatively high cost operation. |
MrBedfordVan | 0:b9164b348919 | 302 | * |
MrBedfordVan | 0:b9164b348919 | 303 | * This is left to application code should it be needed. |
MrBedfordVan | 0:b9164b348919 | 304 | * |
MrBedfordVan | 0:b9164b348919 | 305 | * @return the sum of the square of the acceleration of the device across all axes. |
MrBedfordVan | 0:b9164b348919 | 306 | */ |
MrBedfordVan | 0:b9164b348919 | 307 | int MicroBitAccelerometer::instantaneousAccelerationSquared() |
MrBedfordVan | 0:b9164b348919 | 308 | { |
MrBedfordVan | 0:b9164b348919 | 309 | updateSample(); |
MrBedfordVan | 0:b9164b348919 | 310 | |
MrBedfordVan | 0:b9164b348919 | 311 | // Use pythagoras theorem to determine the combined force acting on the device. |
MrBedfordVan | 0:b9164b348919 | 312 | return (int)sample.x*(int)sample.x + (int)sample.y*(int)sample.y + (int)sample.z*(int)sample.z; |
MrBedfordVan | 0:b9164b348919 | 313 | } |
MrBedfordVan | 0:b9164b348919 | 314 | |
MrBedfordVan | 0:b9164b348919 | 315 | /** |
MrBedfordVan | 0:b9164b348919 | 316 | * Service function. |
MrBedfordVan | 0:b9164b348919 | 317 | * Determines a 'best guess' posture of the device based on instantaneous data. |
MrBedfordVan | 0:b9164b348919 | 318 | * |
MrBedfordVan | 0:b9164b348919 | 319 | * This makes no use of historic data, and forms the input to the filter implemented in updateGesture(). |
MrBedfordVan | 0:b9164b348919 | 320 | * |
MrBedfordVan | 0:b9164b348919 | 321 | * @return A 'best guess' of the current posture of the device, based on instanataneous data. |
MrBedfordVan | 0:b9164b348919 | 322 | */ |
MrBedfordVan | 0:b9164b348919 | 323 | uint16_t MicroBitAccelerometer::instantaneousPosture() |
MrBedfordVan | 0:b9164b348919 | 324 | { |
MrBedfordVan | 0:b9164b348919 | 325 | bool shakeDetected = false; |
MrBedfordVan | 0:b9164b348919 | 326 | |
MrBedfordVan | 0:b9164b348919 | 327 | // Test for shake events. |
MrBedfordVan | 0:b9164b348919 | 328 | // 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 |
MrBedfordVan | 0:b9164b348919 | 329 | // a strong acceleration to the right, then we can infer a shake. Similarly, we can do this for each axis (left/right, up/down, in/out). |
MrBedfordVan | 0:b9164b348919 | 330 | // |
MrBedfordVan | 0:b9164b348919 | 331 | // If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device |
MrBedfordVan | 0:b9164b348919 | 332 | // has been shaken. |
MrBedfordVan | 0:b9164b348919 | 333 | if ((getX() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (getX() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x)) |
MrBedfordVan | 0:b9164b348919 | 334 | { |
MrBedfordVan | 0:b9164b348919 | 335 | shakeDetected = true; |
MrBedfordVan | 0:b9164b348919 | 336 | shake.x = !shake.x; |
MrBedfordVan | 0:b9164b348919 | 337 | } |
MrBedfordVan | 0:b9164b348919 | 338 | |
MrBedfordVan | 0:b9164b348919 | 339 | if ((getY() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (getY() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y)) |
MrBedfordVan | 0:b9164b348919 | 340 | { |
MrBedfordVan | 0:b9164b348919 | 341 | shakeDetected = true; |
MrBedfordVan | 0:b9164b348919 | 342 | shake.y = !shake.y; |
MrBedfordVan | 0:b9164b348919 | 343 | } |
MrBedfordVan | 0:b9164b348919 | 344 | |
MrBedfordVan | 0:b9164b348919 | 345 | if ((getZ() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (getZ() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z)) |
MrBedfordVan | 0:b9164b348919 | 346 | { |
MrBedfordVan | 0:b9164b348919 | 347 | shakeDetected = true; |
MrBedfordVan | 0:b9164b348919 | 348 | shake.z = !shake.z; |
MrBedfordVan | 0:b9164b348919 | 349 | } |
MrBedfordVan | 0:b9164b348919 | 350 | |
MrBedfordVan | 0:b9164b348919 | 351 | // If we detected a zero crossing in this sample period, count this. |
MrBedfordVan | 0:b9164b348919 | 352 | if (shakeDetected && shake.count < MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD) |
MrBedfordVan | 0:b9164b348919 | 353 | { |
MrBedfordVan | 0:b9164b348919 | 354 | shake.count++; |
MrBedfordVan | 0:b9164b348919 | 355 | |
MrBedfordVan | 0:b9164b348919 | 356 | if (shake.count == 1) |
MrBedfordVan | 0:b9164b348919 | 357 | shake.timer = 0; |
MrBedfordVan | 0:b9164b348919 | 358 | |
MrBedfordVan | 0:b9164b348919 | 359 | if (shake.count == MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD) |
MrBedfordVan | 0:b9164b348919 | 360 | { |
MrBedfordVan | 0:b9164b348919 | 361 | shake.shaken = 1; |
MrBedfordVan | 0:b9164b348919 | 362 | shake.timer = 0; |
MrBedfordVan | 0:b9164b348919 | 363 | return MICROBIT_ACCELEROMETER_EVT_SHAKE; |
MrBedfordVan | 0:b9164b348919 | 364 | } |
MrBedfordVan | 0:b9164b348919 | 365 | } |
MrBedfordVan | 0:b9164b348919 | 366 | |
MrBedfordVan | 0:b9164b348919 | 367 | // measure how long we have been detecting a SHAKE event. |
MrBedfordVan | 0:b9164b348919 | 368 | if (shake.count > 0) |
MrBedfordVan | 0:b9164b348919 | 369 | { |
MrBedfordVan | 0:b9164b348919 | 370 | shake.timer++; |
MrBedfordVan | 0:b9164b348919 | 371 | |
MrBedfordVan | 0:b9164b348919 | 372 | // If we've issued a SHAKE event already, and sufficient time has assed, allow another SHAKE event to be issued. |
MrBedfordVan | 0:b9164b348919 | 373 | if (shake.shaken && shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_RTX) |
MrBedfordVan | 0:b9164b348919 | 374 | { |
MrBedfordVan | 0:b9164b348919 | 375 | shake.shaken = 0; |
MrBedfordVan | 0:b9164b348919 | 376 | shake.timer = 0; |
MrBedfordVan | 0:b9164b348919 | 377 | shake.count = 0; |
MrBedfordVan | 0:b9164b348919 | 378 | } |
MrBedfordVan | 0:b9164b348919 | 379 | |
MrBedfordVan | 0:b9164b348919 | 380 | // Decay our count of zero crossings over time. We don't want them to accumulate if the user performs slow moving motions. |
MrBedfordVan | 0:b9164b348919 | 381 | else if (!shake.shaken && shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_DAMPING) |
MrBedfordVan | 0:b9164b348919 | 382 | { |
MrBedfordVan | 0:b9164b348919 | 383 | shake.timer = 0; |
MrBedfordVan | 0:b9164b348919 | 384 | if (shake.count > 0) |
MrBedfordVan | 0:b9164b348919 | 385 | shake.count--; |
MrBedfordVan | 0:b9164b348919 | 386 | } |
MrBedfordVan | 0:b9164b348919 | 387 | } |
MrBedfordVan | 0:b9164b348919 | 388 | |
MrBedfordVan | 0:b9164b348919 | 389 | if (instantaneousAccelerationSquared() < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD) |
MrBedfordVan | 0:b9164b348919 | 390 | return MICROBIT_ACCELEROMETER_EVT_FREEFALL; |
MrBedfordVan | 0:b9164b348919 | 391 | |
MrBedfordVan | 0:b9164b348919 | 392 | // Determine our posture. |
MrBedfordVan | 0:b9164b348919 | 393 | if (getX() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) |
MrBedfordVan | 0:b9164b348919 | 394 | return MICROBIT_ACCELEROMETER_EVT_TILT_LEFT; |
MrBedfordVan | 0:b9164b348919 | 395 | |
MrBedfordVan | 0:b9164b348919 | 396 | if (getX() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) |
MrBedfordVan | 0:b9164b348919 | 397 | return MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT; |
MrBedfordVan | 0:b9164b348919 | 398 | |
MrBedfordVan | 0:b9164b348919 | 399 | if (getY() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) |
MrBedfordVan | 0:b9164b348919 | 400 | return MICROBIT_ACCELEROMETER_EVT_TILT_DOWN; |
MrBedfordVan | 0:b9164b348919 | 401 | |
MrBedfordVan | 0:b9164b348919 | 402 | if (getY() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) |
MrBedfordVan | 0:b9164b348919 | 403 | return MICROBIT_ACCELEROMETER_EVT_TILT_UP; |
MrBedfordVan | 0:b9164b348919 | 404 | |
MrBedfordVan | 0:b9164b348919 | 405 | if (getZ() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) |
MrBedfordVan | 0:b9164b348919 | 406 | return MICROBIT_ACCELEROMETER_EVT_FACE_UP; |
MrBedfordVan | 0:b9164b348919 | 407 | |
MrBedfordVan | 0:b9164b348919 | 408 | if (getZ() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) |
MrBedfordVan | 0:b9164b348919 | 409 | return MICROBIT_ACCELEROMETER_EVT_FACE_DOWN; |
MrBedfordVan | 0:b9164b348919 | 410 | |
MrBedfordVan | 0:b9164b348919 | 411 | return MICROBIT_ACCELEROMETER_EVT_NONE; |
MrBedfordVan | 0:b9164b348919 | 412 | } |
MrBedfordVan | 0:b9164b348919 | 413 | |
MrBedfordVan | 0:b9164b348919 | 414 | /** |
MrBedfordVan | 0:b9164b348919 | 415 | * Updates the basic gesture recognizer. This performs instantaneous pose recognition, and also some low pass filtering to promote |
MrBedfordVan | 0:b9164b348919 | 416 | * stability. |
MrBedfordVan | 0:b9164b348919 | 417 | */ |
MrBedfordVan | 0:b9164b348919 | 418 | void MicroBitAccelerometer::updateGesture() |
MrBedfordVan | 0:b9164b348919 | 419 | { |
MrBedfordVan | 0:b9164b348919 | 420 | // Check for High/Low G force events - typically impulses, impacts etc. |
MrBedfordVan | 0:b9164b348919 | 421 | // Again, during such spikes, these event take priority of the posture of the device. |
MrBedfordVan | 0:b9164b348919 | 422 | // For these events, we don't perform any low pass filtering. |
MrBedfordVan | 0:b9164b348919 | 423 | int force = instantaneousAccelerationSquared(); |
MrBedfordVan | 0:b9164b348919 | 424 | |
MrBedfordVan | 0:b9164b348919 | 425 | if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD) |
MrBedfordVan | 0:b9164b348919 | 426 | { |
MrBedfordVan | 0:b9164b348919 | 427 | if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD && !shake.impulse_3) |
MrBedfordVan | 0:b9164b348919 | 428 | { |
MrBedfordVan | 0:b9164b348919 | 429 | MicroBitEvent e(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_3G); |
MrBedfordVan | 0:b9164b348919 | 430 | shake.impulse_3 = 1; |
MrBedfordVan | 0:b9164b348919 | 431 | } |
MrBedfordVan | 0:b9164b348919 | 432 | if (force > MICROBIT_ACCELEROMETER_6G_THRESHOLD && !shake.impulse_6) |
MrBedfordVan | 0:b9164b348919 | 433 | { |
MrBedfordVan | 0:b9164b348919 | 434 | MicroBitEvent e(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_6G); |
MrBedfordVan | 0:b9164b348919 | 435 | shake.impulse_6 = 1; |
MrBedfordVan | 0:b9164b348919 | 436 | } |
MrBedfordVan | 0:b9164b348919 | 437 | if (force > MICROBIT_ACCELEROMETER_8G_THRESHOLD && !shake.impulse_8) |
MrBedfordVan | 0:b9164b348919 | 438 | { |
MrBedfordVan | 0:b9164b348919 | 439 | MicroBitEvent e(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_8G); |
MrBedfordVan | 0:b9164b348919 | 440 | shake.impulse_8 = 1; |
MrBedfordVan | 0:b9164b348919 | 441 | } |
MrBedfordVan | 0:b9164b348919 | 442 | |
MrBedfordVan | 0:b9164b348919 | 443 | impulseSigma = 0; |
MrBedfordVan | 0:b9164b348919 | 444 | } |
MrBedfordVan | 0:b9164b348919 | 445 | |
MrBedfordVan | 0:b9164b348919 | 446 | // Reset the impulse event onve the acceleration has subsided. |
MrBedfordVan | 0:b9164b348919 | 447 | if (impulseSigma < MICROBIT_ACCELEROMETER_GESTURE_DAMPING) |
MrBedfordVan | 0:b9164b348919 | 448 | impulseSigma++; |
MrBedfordVan | 0:b9164b348919 | 449 | else |
MrBedfordVan | 0:b9164b348919 | 450 | shake.impulse_3 = shake.impulse_6 = shake.impulse_8 = 0; |
MrBedfordVan | 0:b9164b348919 | 451 | |
MrBedfordVan | 0:b9164b348919 | 452 | |
MrBedfordVan | 0:b9164b348919 | 453 | // Determine what it looks like we're doing based on the latest sample... |
MrBedfordVan | 0:b9164b348919 | 454 | uint16_t g = instantaneousPosture(); |
MrBedfordVan | 0:b9164b348919 | 455 | |
MrBedfordVan | 0:b9164b348919 | 456 | if (g == MICROBIT_ACCELEROMETER_EVT_SHAKE) |
MrBedfordVan | 0:b9164b348919 | 457 | { |
MrBedfordVan | 0:b9164b348919 | 458 | MicroBitEvent e(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_SHAKE); |
MrBedfordVan | 0:b9164b348919 | 459 | return; |
MrBedfordVan | 0:b9164b348919 | 460 | } |
MrBedfordVan | 0:b9164b348919 | 461 | |
MrBedfordVan | 0:b9164b348919 | 462 | // Perform some low pass filtering to reduce jitter from any detected effects |
MrBedfordVan | 0:b9164b348919 | 463 | if (g == currentGesture) |
MrBedfordVan | 0:b9164b348919 | 464 | { |
MrBedfordVan | 0:b9164b348919 | 465 | if (sigma < MICROBIT_ACCELEROMETER_GESTURE_DAMPING) |
MrBedfordVan | 0:b9164b348919 | 466 | sigma++; |
MrBedfordVan | 0:b9164b348919 | 467 | } |
MrBedfordVan | 0:b9164b348919 | 468 | else |
MrBedfordVan | 0:b9164b348919 | 469 | { |
MrBedfordVan | 0:b9164b348919 | 470 | currentGesture = g; |
MrBedfordVan | 0:b9164b348919 | 471 | sigma = 0; |
MrBedfordVan | 0:b9164b348919 | 472 | } |
MrBedfordVan | 0:b9164b348919 | 473 | |
MrBedfordVan | 0:b9164b348919 | 474 | // If we've reached threshold, update our record and raise the relevant event... |
MrBedfordVan | 0:b9164b348919 | 475 | if (currentGesture != lastGesture && sigma >= MICROBIT_ACCELEROMETER_GESTURE_DAMPING) |
MrBedfordVan | 0:b9164b348919 | 476 | { |
MrBedfordVan | 0:b9164b348919 | 477 | lastGesture = currentGesture; |
MrBedfordVan | 0:b9164b348919 | 478 | MicroBitEvent e(MICROBIT_ID_GESTURE, lastGesture); |
MrBedfordVan | 0:b9164b348919 | 479 | } |
MrBedfordVan | 0:b9164b348919 | 480 | } |
MrBedfordVan | 0:b9164b348919 | 481 | |
MrBedfordVan | 0:b9164b348919 | 482 | /** |
MrBedfordVan | 0:b9164b348919 | 483 | * Attempts to set the sample rate of the accelerometer to the specified value (in ms). |
MrBedfordVan | 0:b9164b348919 | 484 | * |
MrBedfordVan | 0:b9164b348919 | 485 | * @param period the requested time between samples, in milliseconds. |
MrBedfordVan | 0:b9164b348919 | 486 | * |
MrBedfordVan | 0:b9164b348919 | 487 | * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. |
MrBedfordVan | 0:b9164b348919 | 488 | * |
MrBedfordVan | 0:b9164b348919 | 489 | * @code |
MrBedfordVan | 0:b9164b348919 | 490 | * // sample rate is now 20 ms. |
MrBedfordVan | 0:b9164b348919 | 491 | * accelerometer.setPeriod(20); |
MrBedfordVan | 0:b9164b348919 | 492 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 493 | * |
MrBedfordVan | 0:b9164b348919 | 494 | * @note The requested rate may not be possible on the hardware. In this case, the |
MrBedfordVan | 0:b9164b348919 | 495 | * nearest lower rate is chosen. |
MrBedfordVan | 0:b9164b348919 | 496 | */ |
MrBedfordVan | 0:b9164b348919 | 497 | int MicroBitAccelerometer::setPeriod(int period) |
MrBedfordVan | 0:b9164b348919 | 498 | { |
MrBedfordVan | 0:b9164b348919 | 499 | this->samplePeriod = period; |
MrBedfordVan | 0:b9164b348919 | 500 | return this->configure(); |
MrBedfordVan | 0:b9164b348919 | 501 | } |
MrBedfordVan | 0:b9164b348919 | 502 | |
MrBedfordVan | 0:b9164b348919 | 503 | /** |
MrBedfordVan | 0:b9164b348919 | 504 | * Reads the currently configured sample rate of the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 505 | * |
MrBedfordVan | 0:b9164b348919 | 506 | * @return The time between samples, in milliseconds. |
MrBedfordVan | 0:b9164b348919 | 507 | */ |
MrBedfordVan | 0:b9164b348919 | 508 | int MicroBitAccelerometer::getPeriod() |
MrBedfordVan | 0:b9164b348919 | 509 | { |
MrBedfordVan | 0:b9164b348919 | 510 | return (int)samplePeriod; |
MrBedfordVan | 0:b9164b348919 | 511 | } |
MrBedfordVan | 0:b9164b348919 | 512 | |
MrBedfordVan | 0:b9164b348919 | 513 | /** |
MrBedfordVan | 0:b9164b348919 | 514 | * Attempts to set the sample range of the accelerometer to the specified value (in g). |
MrBedfordVan | 0:b9164b348919 | 515 | * |
MrBedfordVan | 0:b9164b348919 | 516 | * @param range The requested sample range of samples, in g. |
MrBedfordVan | 0:b9164b348919 | 517 | * |
MrBedfordVan | 0:b9164b348919 | 518 | * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. |
MrBedfordVan | 0:b9164b348919 | 519 | * |
MrBedfordVan | 0:b9164b348919 | 520 | * @code |
MrBedfordVan | 0:b9164b348919 | 521 | * // the sample range of the accelerometer is now 8G. |
MrBedfordVan | 0:b9164b348919 | 522 | * accelerometer.setRange(8); |
MrBedfordVan | 0:b9164b348919 | 523 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 524 | * |
MrBedfordVan | 0:b9164b348919 | 525 | * @note The requested range may not be possible on the hardware. In this case, the |
MrBedfordVan | 0:b9164b348919 | 526 | * nearest lower range is chosen. |
MrBedfordVan | 0:b9164b348919 | 527 | */ |
MrBedfordVan | 0:b9164b348919 | 528 | int MicroBitAccelerometer::setRange(int range) |
MrBedfordVan | 0:b9164b348919 | 529 | { |
MrBedfordVan | 0:b9164b348919 | 530 | this->sampleRange = range; |
MrBedfordVan | 0:b9164b348919 | 531 | return this->configure(); |
MrBedfordVan | 0:b9164b348919 | 532 | } |
MrBedfordVan | 0:b9164b348919 | 533 | |
MrBedfordVan | 0:b9164b348919 | 534 | /** |
MrBedfordVan | 0:b9164b348919 | 535 | * Reads the currently configured sample range of the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 536 | * |
MrBedfordVan | 0:b9164b348919 | 537 | * @return The sample range, in g. |
MrBedfordVan | 0:b9164b348919 | 538 | */ |
MrBedfordVan | 0:b9164b348919 | 539 | int MicroBitAccelerometer::getRange() |
MrBedfordVan | 0:b9164b348919 | 540 | { |
MrBedfordVan | 0:b9164b348919 | 541 | return (int)sampleRange; |
MrBedfordVan | 0:b9164b348919 | 542 | } |
MrBedfordVan | 0:b9164b348919 | 543 | |
MrBedfordVan | 0:b9164b348919 | 544 | /** |
MrBedfordVan | 0:b9164b348919 | 545 | * Reads the value of the X axis from the latest update retrieved from the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 546 | * |
MrBedfordVan | 0:b9164b348919 | 547 | * @param system The coordinate system to use. By default, a simple cartesian system is provided. |
MrBedfordVan | 0:b9164b348919 | 548 | * |
MrBedfordVan | 0:b9164b348919 | 549 | * @return The force measured in the X axis, in milli-g. |
MrBedfordVan | 0:b9164b348919 | 550 | * |
MrBedfordVan | 0:b9164b348919 | 551 | * @code |
MrBedfordVan | 0:b9164b348919 | 552 | * accelerometer.getX(); |
MrBedfordVan | 0:b9164b348919 | 553 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 554 | */ |
MrBedfordVan | 0:b9164b348919 | 555 | int MicroBitAccelerometer::getX(MicroBitCoordinateSystem system) |
MrBedfordVan | 0:b9164b348919 | 556 | { |
MrBedfordVan | 0:b9164b348919 | 557 | updateSample(); |
MrBedfordVan | 0:b9164b348919 | 558 | |
MrBedfordVan | 0:b9164b348919 | 559 | switch (system) |
MrBedfordVan | 0:b9164b348919 | 560 | { |
MrBedfordVan | 0:b9164b348919 | 561 | case SIMPLE_CARTESIAN: |
MrBedfordVan | 0:b9164b348919 | 562 | return -sample.x; |
MrBedfordVan | 0:b9164b348919 | 563 | |
MrBedfordVan | 0:b9164b348919 | 564 | case NORTH_EAST_DOWN: |
MrBedfordVan | 0:b9164b348919 | 565 | return sample.y; |
MrBedfordVan | 0:b9164b348919 | 566 | |
MrBedfordVan | 0:b9164b348919 | 567 | case RAW: |
MrBedfordVan | 0:b9164b348919 | 568 | default: |
MrBedfordVan | 0:b9164b348919 | 569 | return sample.x; |
MrBedfordVan | 0:b9164b348919 | 570 | } |
MrBedfordVan | 0:b9164b348919 | 571 | } |
MrBedfordVan | 0:b9164b348919 | 572 | |
MrBedfordVan | 0:b9164b348919 | 573 | /** |
MrBedfordVan | 0:b9164b348919 | 574 | * Reads the value of the Y axis from the latest update retrieved from the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 575 | * |
MrBedfordVan | 0:b9164b348919 | 576 | * @return The force measured in the Y axis, in milli-g. |
MrBedfordVan | 0:b9164b348919 | 577 | * |
MrBedfordVan | 0:b9164b348919 | 578 | * @code |
MrBedfordVan | 0:b9164b348919 | 579 | * accelerometer.getY(); |
MrBedfordVan | 0:b9164b348919 | 580 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 581 | */ |
MrBedfordVan | 0:b9164b348919 | 582 | int MicroBitAccelerometer::getY(MicroBitCoordinateSystem system) |
MrBedfordVan | 0:b9164b348919 | 583 | { |
MrBedfordVan | 0:b9164b348919 | 584 | updateSample(); |
MrBedfordVan | 0:b9164b348919 | 585 | |
MrBedfordVan | 0:b9164b348919 | 586 | switch (system) |
MrBedfordVan | 0:b9164b348919 | 587 | { |
MrBedfordVan | 0:b9164b348919 | 588 | case SIMPLE_CARTESIAN: |
MrBedfordVan | 0:b9164b348919 | 589 | return -sample.y; |
MrBedfordVan | 0:b9164b348919 | 590 | |
MrBedfordVan | 0:b9164b348919 | 591 | case NORTH_EAST_DOWN: |
MrBedfordVan | 0:b9164b348919 | 592 | return -sample.x; |
MrBedfordVan | 0:b9164b348919 | 593 | |
MrBedfordVan | 0:b9164b348919 | 594 | case RAW: |
MrBedfordVan | 0:b9164b348919 | 595 | default: |
MrBedfordVan | 0:b9164b348919 | 596 | return sample.y; |
MrBedfordVan | 0:b9164b348919 | 597 | } |
MrBedfordVan | 0:b9164b348919 | 598 | } |
MrBedfordVan | 0:b9164b348919 | 599 | |
MrBedfordVan | 0:b9164b348919 | 600 | /** |
MrBedfordVan | 0:b9164b348919 | 601 | * Reads the value of the Z axis from the latest update retrieved from the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 602 | * |
MrBedfordVan | 0:b9164b348919 | 603 | * @return The force measured in the Z axis, in milli-g. |
MrBedfordVan | 0:b9164b348919 | 604 | * |
MrBedfordVan | 0:b9164b348919 | 605 | * @code |
MrBedfordVan | 0:b9164b348919 | 606 | * accelerometer.getZ(); |
MrBedfordVan | 0:b9164b348919 | 607 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 608 | */ |
MrBedfordVan | 0:b9164b348919 | 609 | int MicroBitAccelerometer::getZ(MicroBitCoordinateSystem system) |
MrBedfordVan | 0:b9164b348919 | 610 | { |
MrBedfordVan | 0:b9164b348919 | 611 | updateSample(); |
MrBedfordVan | 0:b9164b348919 | 612 | |
MrBedfordVan | 0:b9164b348919 | 613 | switch (system) |
MrBedfordVan | 0:b9164b348919 | 614 | { |
MrBedfordVan | 0:b9164b348919 | 615 | case NORTH_EAST_DOWN: |
MrBedfordVan | 0:b9164b348919 | 616 | return -sample.z; |
MrBedfordVan | 0:b9164b348919 | 617 | |
MrBedfordVan | 0:b9164b348919 | 618 | case SIMPLE_CARTESIAN: |
MrBedfordVan | 0:b9164b348919 | 619 | case RAW: |
MrBedfordVan | 0:b9164b348919 | 620 | default: |
MrBedfordVan | 0:b9164b348919 | 621 | return sample.z; |
MrBedfordVan | 0:b9164b348919 | 622 | } |
MrBedfordVan | 0:b9164b348919 | 623 | } |
MrBedfordVan | 0:b9164b348919 | 624 | |
MrBedfordVan | 0:b9164b348919 | 625 | /** |
MrBedfordVan | 0:b9164b348919 | 626 | * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 627 | * |
MrBedfordVan | 0:b9164b348919 | 628 | * @return The pitch of the device, in degrees. |
MrBedfordVan | 0:b9164b348919 | 629 | * |
MrBedfordVan | 0:b9164b348919 | 630 | * @code |
MrBedfordVan | 0:b9164b348919 | 631 | * accelerometer.getPitch(); |
MrBedfordVan | 0:b9164b348919 | 632 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 633 | */ |
MrBedfordVan | 0:b9164b348919 | 634 | int MicroBitAccelerometer::getPitch() |
MrBedfordVan | 0:b9164b348919 | 635 | { |
MrBedfordVan | 0:b9164b348919 | 636 | return (int) ((360*getPitchRadians()) / (2*PI)); |
MrBedfordVan | 0:b9164b348919 | 637 | } |
MrBedfordVan | 0:b9164b348919 | 638 | |
MrBedfordVan | 0:b9164b348919 | 639 | /** |
MrBedfordVan | 0:b9164b348919 | 640 | * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 641 | * |
MrBedfordVan | 0:b9164b348919 | 642 | * @return The pitch of the device, in radians. |
MrBedfordVan | 0:b9164b348919 | 643 | * |
MrBedfordVan | 0:b9164b348919 | 644 | * @code |
MrBedfordVan | 0:b9164b348919 | 645 | * accelerometer.getPitchRadians(); |
MrBedfordVan | 0:b9164b348919 | 646 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 647 | */ |
MrBedfordVan | 0:b9164b348919 | 648 | float MicroBitAccelerometer::getPitchRadians() |
MrBedfordVan | 0:b9164b348919 | 649 | { |
MrBedfordVan | 0:b9164b348919 | 650 | if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID)) |
MrBedfordVan | 0:b9164b348919 | 651 | recalculatePitchRoll(); |
MrBedfordVan | 0:b9164b348919 | 652 | |
MrBedfordVan | 0:b9164b348919 | 653 | return pitch; |
MrBedfordVan | 0:b9164b348919 | 654 | } |
MrBedfordVan | 0:b9164b348919 | 655 | |
MrBedfordVan | 0:b9164b348919 | 656 | /** |
MrBedfordVan | 0:b9164b348919 | 657 | * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 658 | * |
MrBedfordVan | 0:b9164b348919 | 659 | * @return The roll of the device, in degrees. |
MrBedfordVan | 0:b9164b348919 | 660 | * |
MrBedfordVan | 0:b9164b348919 | 661 | * @code |
MrBedfordVan | 0:b9164b348919 | 662 | * accelerometer.getRoll(); |
MrBedfordVan | 0:b9164b348919 | 663 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 664 | */ |
MrBedfordVan | 0:b9164b348919 | 665 | int MicroBitAccelerometer::getRoll() |
MrBedfordVan | 0:b9164b348919 | 666 | { |
MrBedfordVan | 0:b9164b348919 | 667 | return (int) ((360*getRollRadians()) / (2*PI)); |
MrBedfordVan | 0:b9164b348919 | 668 | } |
MrBedfordVan | 0:b9164b348919 | 669 | |
MrBedfordVan | 0:b9164b348919 | 670 | /** |
MrBedfordVan | 0:b9164b348919 | 671 | * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer. |
MrBedfordVan | 0:b9164b348919 | 672 | * |
MrBedfordVan | 0:b9164b348919 | 673 | * @return The roll of the device, in radians. |
MrBedfordVan | 0:b9164b348919 | 674 | * |
MrBedfordVan | 0:b9164b348919 | 675 | * @code |
MrBedfordVan | 0:b9164b348919 | 676 | * accelerometer.getRollRadians(); |
MrBedfordVan | 0:b9164b348919 | 677 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 678 | */ |
MrBedfordVan | 0:b9164b348919 | 679 | float MicroBitAccelerometer::getRollRadians() |
MrBedfordVan | 0:b9164b348919 | 680 | { |
MrBedfordVan | 0:b9164b348919 | 681 | if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID)) |
MrBedfordVan | 0:b9164b348919 | 682 | recalculatePitchRoll(); |
MrBedfordVan | 0:b9164b348919 | 683 | |
MrBedfordVan | 0:b9164b348919 | 684 | return roll; |
MrBedfordVan | 0:b9164b348919 | 685 | } |
MrBedfordVan | 0:b9164b348919 | 686 | |
MrBedfordVan | 0:b9164b348919 | 687 | /** |
MrBedfordVan | 0:b9164b348919 | 688 | * Recalculate roll and pitch values for the current sample. |
MrBedfordVan | 0:b9164b348919 | 689 | * |
MrBedfordVan | 0:b9164b348919 | 690 | * @note We only do this at most once per sample, as the necessary trigonemteric functions are rather |
MrBedfordVan | 0:b9164b348919 | 691 | * heavyweight for a CPU without a floating point unit. |
MrBedfordVan | 0:b9164b348919 | 692 | */ |
MrBedfordVan | 0:b9164b348919 | 693 | void MicroBitAccelerometer::recalculatePitchRoll() |
MrBedfordVan | 0:b9164b348919 | 694 | { |
MrBedfordVan | 0:b9164b348919 | 695 | double x = (double) getX(NORTH_EAST_DOWN); |
MrBedfordVan | 0:b9164b348919 | 696 | double y = (double) getY(NORTH_EAST_DOWN); |
MrBedfordVan | 0:b9164b348919 | 697 | double z = (double) getZ(NORTH_EAST_DOWN); |
MrBedfordVan | 0:b9164b348919 | 698 | |
MrBedfordVan | 0:b9164b348919 | 699 | roll = atan2(y, z); |
MrBedfordVan | 0:b9164b348919 | 700 | pitch = atan(-x / (y*sin(roll) + z*cos(roll))); |
MrBedfordVan | 0:b9164b348919 | 701 | |
MrBedfordVan | 0:b9164b348919 | 702 | status |= MICROBIT_ACCEL_PITCH_ROLL_VALID; |
MrBedfordVan | 0:b9164b348919 | 703 | } |
MrBedfordVan | 0:b9164b348919 | 704 | |
MrBedfordVan | 0:b9164b348919 | 705 | /** |
MrBedfordVan | 0:b9164b348919 | 706 | * Retrieves the last recorded gesture. |
MrBedfordVan | 0:b9164b348919 | 707 | * |
MrBedfordVan | 0:b9164b348919 | 708 | * @return The last gesture that was detected. |
MrBedfordVan | 0:b9164b348919 | 709 | * |
MrBedfordVan | 0:b9164b348919 | 710 | * Example: |
MrBedfordVan | 0:b9164b348919 | 711 | * @code |
MrBedfordVan | 0:b9164b348919 | 712 | * MicroBitDisplay display; |
MrBedfordVan | 0:b9164b348919 | 713 | * |
MrBedfordVan | 0:b9164b348919 | 714 | * if (accelerometer.getGesture() == SHAKE) |
MrBedfordVan | 0:b9164b348919 | 715 | * display.scroll("SHAKE!"); |
MrBedfordVan | 0:b9164b348919 | 716 | * @endcode |
MrBedfordVan | 0:b9164b348919 | 717 | */ |
MrBedfordVan | 0:b9164b348919 | 718 | uint16_t MicroBitAccelerometer::getGesture() |
MrBedfordVan | 0:b9164b348919 | 719 | { |
MrBedfordVan | 0:b9164b348919 | 720 | return lastGesture; |
MrBedfordVan | 0:b9164b348919 | 721 | } |
MrBedfordVan | 0:b9164b348919 | 722 | |
MrBedfordVan | 0:b9164b348919 | 723 | /** |
MrBedfordVan | 0:b9164b348919 | 724 | * A periodic callback invoked by the fiber scheduler idle thread. |
MrBedfordVan | 0:b9164b348919 | 725 | * |
MrBedfordVan | 0:b9164b348919 | 726 | * Internally calls updateSample(). |
MrBedfordVan | 0:b9164b348919 | 727 | */ |
MrBedfordVan | 0:b9164b348919 | 728 | void MicroBitAccelerometer::idleTick() |
MrBedfordVan | 0:b9164b348919 | 729 | { |
MrBedfordVan | 0:b9164b348919 | 730 | updateSample(); |
MrBedfordVan | 0:b9164b348919 | 731 | } |
MrBedfordVan | 0:b9164b348919 | 732 | |
MrBedfordVan | 0:b9164b348919 | 733 | /** |
MrBedfordVan | 0:b9164b348919 | 734 | * Destructor for MicroBitAccelerometer, where we deregister from the array of fiber components. |
MrBedfordVan | 0:b9164b348919 | 735 | */ |
MrBedfordVan | 0:b9164b348919 | 736 | MicroBitAccelerometer::~MicroBitAccelerometer() |
MrBedfordVan | 0:b9164b348919 | 737 | { |
MrBedfordVan | 0:b9164b348919 | 738 | fiber_remove_idle_component(this); |
MrBedfordVan | 0:b9164b348919 | 739 | } |
MrBedfordVan | 0:b9164b348919 | 740 | |
MrBedfordVan | 0:b9164b348919 | 741 | const MMA8653SampleRangeConfig MMA8653SampleRange[MMA8653_SAMPLE_RANGES] = { |
MrBedfordVan | 0:b9164b348919 | 742 | {2, 0}, |
MrBedfordVan | 0:b9164b348919 | 743 | {4, 1}, |
MrBedfordVan | 0:b9164b348919 | 744 | {8, 2} |
MrBedfordVan | 0:b9164b348919 | 745 | }; |
MrBedfordVan | 0:b9164b348919 | 746 | |
MrBedfordVan | 0:b9164b348919 | 747 | const MMA8653SampleRateConfig MMA8653SampleRate[MMA8653_SAMPLE_RATES] = { |
MrBedfordVan | 0:b9164b348919 | 748 | {1250, 0x00}, |
MrBedfordVan | 0:b9164b348919 | 749 | {2500, 0x08}, |
MrBedfordVan | 0:b9164b348919 | 750 | {5000, 0x10}, |
MrBedfordVan | 0:b9164b348919 | 751 | {10000, 0x18}, |
MrBedfordVan | 0:b9164b348919 | 752 | {20000, 0x20}, |
MrBedfordVan | 0:b9164b348919 | 753 | {80000, 0x28}, |
MrBedfordVan | 0:b9164b348919 | 754 | {160000, 0x30}, |
MrBedfordVan | 0:b9164b348919 | 755 | {640000, 0x38} |
MrBedfordVan | 0:b9164b348919 | 756 | }; |