diff --git a/.gitignore b/.gitignore index 05fe5a5..e57cb44 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ build yotta_modules yotta_targets *.swp +*~ Makefile diff --git a/inc/core/MicroBitComponent.h b/inc/core/MicroBitComponent.h index a161aee..fd30f88 100644 --- a/inc/core/MicroBitComponent.h +++ b/inc/core/MicroBitComponent.h @@ -68,6 +68,9 @@ DEALINGS IN THE SOFTWARE. #define MICROBIT_ID_MULTIBUTTON_ATTACH 31 #define MICROBIT_ID_SERIAL 32 +#define MICROBIT_ID_IO_INT1 33 //INT1 +#define MICROBIT_ID_IO_INT2 34 //INT2 + #define MICROBIT_ID_MESSAGE_BUS_LISTENER 1021 // Message bus indication that a handler for a given ID has been registered. #define MICROBIT_ID_NOTIFY_ONE 1022 // Notfication channel, for general purpose synchronisation #define MICROBIT_ID_NOTIFY 1023 // Notfication channel, for general purpose synchronisation diff --git a/inc/core/MicroBitUtil.h b/inc/core/MicroBitUtil.h new file mode 100644 index 0000000..d9fc5f7 --- /dev/null +++ b/inc/core/MicroBitUtil.h @@ -0,0 +1,61 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * This file contains functions used to maintain compatability and portability. + * It also contains constants that are used elsewhere in the DAL. + */ + +#ifndef MICROBIT_UTIL_H +#define MICROBIT_UTIL_H + +#include "MicroBitConfig.h" + +#define CREATE_KEY_VALUE_TABLE(NAME, PAIRS) const KeyValueTable NAME { PAIRS, sizeof(PAIRS) / sizeof(KeyValueTableEntry) }; + +/** + * Provides a simple key/value pair lookup table with range lookup support. + * Normally stored in FLASH to reduce RAM usage. Keys should be pre-sorted + * in ascending order. + */ + +struct KeyValueTableEntry +{ + const uint32_t key; + const uint32_t value; +}; + +struct KeyValueTable +{ + const KeyValueTableEntry *data; + const int length; + + KeyValueTableEntry* find(const uint32_t key) const; + uint32_t get(const uint32_t key) const; + uint32_t getKey(const uint32_t key) const; + bool hasKey(const uint32_t key) const; +}; + + +#endif diff --git a/inc/drivers/FXOS8700.h b/inc/drivers/FXOS8700.h new file mode 100644 index 0000000..464e49e --- /dev/null +++ b/inc/drivers/FXOS8700.h @@ -0,0 +1,240 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef FXOS8700_H +#define FXOS8700_H + +#include "MicroBitConfig.h" +#include "MicroBitComponent.h" +#include "MicroBitPin.h" +#include "MicroBitI2C.h" +#include "MicroBitAccelerometer.h" +#include "MicroBitCompass.h" +#include "CoordinateSystem.h" +#include "MicroBitUtil.h" + +/** + * I2C constants + */ +#define FXOS8700_DEFAULT_ADDR 0x3C + +/** + * FXOS8700 Register map + */ +#define FXOS8700_STATUS_REG 0x00 +#define FXOS8700_OUT_X_MSB 0x01 +#define FXOS8700_OUT_X_LSB 0x02 +#define FXOS8700_OUT_Y_MSB 0x03 +#define FXOS8700_OUT_Y_LSB 0x04 +#define FXOS8700_OUT_Z_MSB 0x05 +#define FXOS8700_OUT_Z_LSB 0x06 +#define FXOS8700_F_SETUP 0x09 +#define FXOS8700_TRIG_CFG 0x0A +#define FXOS8700_SYSMOD 0x0B +#define FXOS8700_INT_SOURCE 0x0C +#define FXOS8700_WHO_AM_I 0x0D +#define FXOS8700_XYZ_DATA_CFG 0x0E +#define FXOS8700_HP_FILTER_CUTOFF 0x0F +#define FXOS8700_PL_STATUS 0x10 +#define FXOS8700_PL_CFG 0x11 +#define FXOS8700_PL_COUNT 0x12 +#define FXOS8700_PL_BF_ZCOMP 0x13 +#define FXOS8700_PL_THS_REG 0x14 +#define FXOS8700_A_FFMT_CFG 0x15 +#define FXOS8700_A_FFMT_SRC 0x16 +#define FXOS8700_A_FFMT_THS 0x17 +#define FXOS8700_A_FFMT_COUNT 0x18 +#define FXOS8700_TRANSIENT_CFG 0x1D +#define FXOS8700_TRANSIENT_SRC 0x1E +#define FXOS8700_TRANSIENT_THS 0x1F +#define FXOS8700_TRANSIENT_COUNT 0x20 +#define FXOS8700_PULSE_CFG 0x21 +#define FXOS8700_PULSE_SRC 0x22 +#define FXOS8700_PULSE_THSX 0x23 +#define FXOS8700_PULSE_THSY 0x24 +#define FXOS8700_PULSE_THSZ 0x25 +#define FXOS8700_PULSE_TMLT 0x26 +#define FXOS8700_PULSE_LTCY 0x27 +#define FXOS8700_PULSE_WIND 0x28 +#define FXOS8700_ASLP_COUNT 0x29 +#define FXOS8700_CTRL_REG1 0x2A +#define FXOS8700_CTRL_REG2 0x2B +#define FXOS8700_CTRL_REG3 0x2C +#define FXOS8700_CTRL_REG4 0x2D +#define FXOS8700_CTRL_REG5 0x2E +#define FXOS8700_OFF_X 0x2F +#define FXOS8700_OFF_Y 0x30 +#define FXOS8700_OFF_Z 0x31 +#define FXOS8700_M_DR_STATUS 0x32 +#define FXOS8700_M_OUT_X_MSB 0x33 +#define FXOS8700_M_OUT_X_LSB 0x34 +#define FXOS8700_M_OUT_Y_MSB 0x35 +#define FXOS8700_M_OUT_Y_LSB 0x36 +#define FXOS8700_M_OUT_Z_MSB 0x37 +#define FXOS8700_M_OUT_Z_LSB 0x38 +#define FXOS8700_CMP_X_MSB 0x39 +#define FXOS8700_CMP_X_LSB 0x3A +#define FXOS8700_CMP_Y_MSB 0x3B +#define FXOS8700_CMP_Y_LSB 0x3C +#define FXOS8700_CMP_Z_MSB 0x3D +#define FXOS8700_CMP_Z_LSB 0x3E +#define FXOS8700_M_OFF_X_MSB 0x3F +#define FXOS8700_M_OFF_X_LSB 0x40 +#define FXOS8700_M_OFF_Y_MSB 0x41 +#define FXOS8700_M_OFF_Y_LSB 0x42 +#define FXOS8700_M_OFF_Z_MSB 0x43 +#define FXOS8700_M_OFF_Z_LSB 0x44 +#define FXOS8700_MAX_X_MSB 0x45 +#define FXOS8700_MAX_X_LSB 0x46 +#define FXOS8700_MAX_Y_MSB 0x47 +#define FXOS8700_MAX_Y_LSB 0x48 +#define FXOS8700_MAX_Z_MSB 0x49 +#define FXOS8700_MAX_Z_LSB 0x4A +#define FXOS8700_MIN_X_MSB 0x4B +#define FXOS8700_MIN_X_LSB 0x4C +#define FXOS8700_MIN_Y_MSB 0x4D +#define FXOS8700_MIN_Y_LSB 0x4E +#define FXOS8700_MIN_Z_MSB 0x4F +#define FXOS8700_MIN_Z_LSB 0x50 +#define FXOS8700_TEMP 0x51 +#define FXOS8700_M_THS_CFG 0x52 +#define FXOS8700_M_THS_SRC 0x53 +#define FXOS8700_M_THS_X_MSB 0x54 +#define FXOS8700_M_THS_X_LSB 0x55 +#define FXOS8700_M_THS_Y_MSB 0x56 +#define FXOS8700_M_THS_Y_LSB 0x57 +#define FXOS8700_M_THS_Z_MSB 0x58 +#define FXOS8700_M_THS_Z_LSB 0x59 +#define FXOS8700_M_THS_COUNT 0x5A +#define FXOS8700_M_CTRL_REG1 0x5B +#define FXOS8700_M_CTRL_REG2 0x5C +#define FXOS8700_M_CTRL_REG3 0x5D +#define FXOS8700_M_INT_SRC 0x5E +#define FXOS8700_A_VECM_CFG 0x5F +#define FXOS8700_A_VECM_THS_MSB 0x60 +#define FXOS8700_A_VECM_THS_LSB 0x61 +#define FXOS8700_A_VECM_CNT 0x62 +#define FXOS8700_A_VECM_INITX_MSB 0x63 +#define FXOS8700_A_VECM_INITX_LSB 0x64 +#define FXOS8700_A_VECM_INITY_MSB 0x65 +#define FXOS8700_A_VECM_INITY_LSB 0x66 +#define FXOS8700_A_VECM_INITZ_MSB 0x67 +#define FXOS8700_A_VECM_INITZ_LSB 0x68 +#define FXOS8700_M_VECM_CFG 0x69 +#define FXOS8700_M_VECM_THS_MSB 0x6A +#define FXOS8700_M_VECM_THS_LSB 0x6B +#define FXOS8700_M_VECM_CNT 0x6C +#define FXOS8700_M_VECM_INITX_MSB 0x6D +#define FXOS8700_M_VECM_INITX_LSB 0x6E +#define FXOS8700_M_VECM_INITY_MSB 0x6F +#define FXOS8700_M_VECM_INITY_LSB 0x70 +#define FXOS8700_M_VECM_INITZ_MSB 0x71 +#define FXOS8700_M_VECM_INITZ_LSB 0x72 +#define FXOS8700_A_FFMT_THS_X_MSB 0x73 +#define FXOS8700_A_FFMT_THS_X_LSB 0x74 +#define FXOS8700_A_FFMT_THS_Y_MSB 0x75 +#define FXOS8700_A_FFMT_THS_Y_LSB 0x76 +#define FXOS8700_A_FFMT_THS_Z_MSB 0x77 +#define FXOS8700_A_FFMT_THS_Z_LSB 0x78 + + +/** + * FXOS8700 constants + */ +#define FXOS8700_WHOAMI_VAL 0xC7 + +/** + * Class definition for an FXSO8700 hybrid Accelerometer/Magnetometer + */ +class FXOS8700 : public MicroBitAccelerometer, public MicroBitCompass +{ + MicroBitI2C& i2c; // The I2C interface to use. + MicroBitPin &int1; // Data ready interrupt. + uint16_t address; // I2C address of this accelerometer. + + public: + + /** + * Constructor. + * Create a software abstraction of an accelerometer. + * + * @param _i2c an instance of I2C used to communicate with the onboard accelerometer. + * @param _int1 a pin connected to the INT1 interrupt source of the sensor. + * @param address the default I2C address of the accelerometer. Defaults to: FXOS8700_DEFAULT_ADDR. + * + */ + FXOS8700(MicroBitI2C &_i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address = FXOS8700_DEFAULT_ADDR, uint16_t aid = MICROBIT_ID_ACCELEROMETER, uint16_t cid = MICROBIT_ID_COMPASS); + + /** + * 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 DEVICE_OK on success, DEVICE_I2C_ERROR if the accelerometer could not be configured. + */ + int configure(); + + /** + * Reads the acceleration data from the accelerometer, and stores it in our buffer. + * This only happens if the accelerometer indicates that it has new data via int1. + * + * On first use, this member function will attempt to add this component to the + * list of fiber components in order to constantly update the values stored + * by this object. + * + * This technique is called lazy instantiation, and it means that we do not + * obtain the overhead from non-chalantly adding this component to fiber components. + * + * @return DEVICE_OK on success, DEVICE_I2C_ERROR if the read request fails. + */ + virtual int requestUpdate(); + + /** + * Attempts to read the 8 bit ID from the accelerometer, this can be used for + * validation purposes. + * + * @return the 8 bit ID returned by the accelerometer, or DEVICE_I2C_ERROR if the request fails. + * + * @code + * accelerometer.whoAmI(); + * @endcode + */ + int whoAmI(); + + /** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ + virtual void idleCallback(); + + /** + * Destructor. + */ + ~FXOS8700(); + +}; + +#endif diff --git a/inc/drivers/LSM303Accelerometer.h b/inc/drivers/LSM303Accelerometer.h new file mode 100644 index 0000000..3a59651 --- /dev/null +++ b/inc/drivers/LSM303Accelerometer.h @@ -0,0 +1,152 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef LSM303_A_H +#define LSM303_A_H + +#include "MicroBitConfig.h" +#include "MicroBitComponent.h" +#include "CoordinateSystem.h" +#include "MicroBitAccelerometer.h" +#include "MicroBitI2C.h" +#include "MicroBitUtil.h" + +/** + * I2C constants + */ +#define LSM303_A_DEFAULT_ADDR 0x32 + +/** + * LSM303 Register map (partial) + */ + +#define LSM303_STATUS_REG_AUX_A 0x07 +#define LSM303_OUT_TEMP_L_A 0x0C +#define LSM303_OUT_TEMP_H_A 0x0D +#define LSM303_INT_COUNTER_REG_A 0x0E +#define LSM303_WHO_AM_I_A 0x0F +#define LSM303_TEMP_CFG_REG_A 0x1F +#define LSM303_CTRL_REG1_A 0x20 +#define LSM303_CTRL_REG2_A 0x21 +#define LSM303_CTRL_REG3_A 0x22 +#define LSM303_CTRL_REG4_A 0x23 +#define LSM303_CTRL_REG5_A 0x24 +#define LSM303_CTRL_REG6_A 0x25 +#define LSM303_DATACAPTURE_A 0x26 +#define LSM303_STATUS_REG_A 0x27 +#define LSM303_OUT_X_L_A 0x28 +#define LSM303_OUT_X_H_A 0x29 +#define LSM303_OUT_Y_L_A 0x2A +#define LSM303_OUT_Y_H_A 0x2B +#define LSM303_OUT_Z_L_A 0x2C +#define LSM303_OUT_Z_H_A 0x2D +#define LSM303_FIFO_CTRL_REG_A 0x2E +#define LSM303_FIFO_SRC_REG_A 0x2F +#define LSM303_INT1_CFG_A 0x30 +#define LSM303_INT1_SRC_A 0x31 +#define LSM303_INT1_THS_A 0x32 +#define LSM303_INT1_DURATION_A 0x33 +#define LSM303_INT2_CFG_A 0x34 +#define LSM303_INT2_SRC_A 0x35 +#define LSM303_INT2_THS_A 0x36 +#define LSM303_INT2_DURATION_A 0x37 +#define LSM303_CLICK_CFG_A 0x38 +#define LSM303_CLICK_SRC_A 0x39 +#define LSM303_CLICK_THS_A 0x3A +#define LSM303_TIME_LIMIT_A 0x3B +#define LSM303_TIME_LATENCY_A 0x3C +#define LSM303_TIME_WINDOW_A 0x3D +#define LSM303_ACT_THS_A 0x3E +#define LSM303_ACT_DUR_A 0x3F + +/** + * LSM303_A constants + */ +#define LSM303_A_WHOAMI_VAL 0x33 + +/** + * Class definition for LSM303Accelerometer. + * This class provides a simple wrapper between the hybrid FXOS8700 accelerometer and higher level accelerometer funcitonality. + */ +class LSM303Accelerometer : public MicroBitAccelerometer +{ + MicroBitI2C& i2c; // The I2C interface to use. + MicroBitPin& int1; // Data ready interrupt. + uint16_t address; // I2C address of this accelerometer. + + public: + + /** + * Constructor. + * Create a software abstraction of an accelerometer. + * + * @param coordinateSpace The orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id The unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER + * + */ + LSM303Accelerometer(MicroBitI2C& _i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address = LSM303_A_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_ACCELEROMETER); + + /** + * 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. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ + virtual int configure(); + + /** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ + virtual int requestUpdate(); + + /** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ + virtual void idleCallback(); + + + /** + * Destructor. + */ + ~LSM303Accelerometer(); + +}; + +#endif + diff --git a/inc/drivers/LSM303Magnetometer.h b/inc/drivers/LSM303Magnetometer.h new file mode 100644 index 0000000..c08222f --- /dev/null +++ b/inc/drivers/LSM303Magnetometer.h @@ -0,0 +1,139 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef LSM303_M_H +#define LSM303_M_H + +#include "MicroBitConfig.h" +#include "MicroBitComponent.h" +#include "CoordinateSystem.h" +#include "MicroBitCompass.h" +#include "MicroBitI2C.h" +#include "MicroBitUtil.h" + +/** + * Term to convert sample data into SI units. + * TODO: Write this, if necessary + */ +#define LSM303_M_NORMALIZE_SAMPLE(x) ((int)x) + +/** + * LSM303_M MAGIC ID value + * Returned from the MAG_WHO_AM_I register for ID purposes. + */ +#define LSM303_M_WHOAMI_VAL 0x20 + +/** + * I2C constants + */ +#define LSM303_M_DEFAULT_ADDR 0x3C + +/** + * LSM303_M Register map + */ +#define LSM303_OFFSET_X_REG_L_M 0x45 +#define LSM303_OFFSET_X_REG_H_M 0x46 +#define LSM303_OFFSET_Y_REG_L_M 0x47 +#define LSM303_OFFSET_Y_REG_H_M 0x48 +#define LSM303_OFFSET_Z_REG_L_M 0x49 +#define LSM303_OFFSET_Z_REG_H_M 0x4A +#define LSM303_WHO_AM_I_M 0x4F +#define LSM303_CFG_REG_A_M 0x60 +#define LSM303_CFG_REG_B_M 0x61 +#define LSM303_CFG_REG_C_M 0x62 +#define LSM303_INT_CRTL_REG_M 0x63 +#define LSM303_INT_SOURCE_REG_M 0x64 +#define LSM303_INT_THS_L_REG_M 0x65 +#define LSM303_INT_THS_H_REG_M 0x66 +#define LSM303_STATUS_REG_M 0x67 +#define LSM303_OUTX_L_REG_M 0x68 +#define LSM303_OUTX_H_REG_M 0x69 +#define LSM303_OUTY_L_REG_M 0x6A +#define LSM303_OUTY_H_REG_M 0x6B +#define LSM303_OUTZ_L_REG_M 0x6C +#define LSM303_OUTZ_H_REG_M 0x6D + + + +/** + * Class definition for LSM303_M. + * + * This class provides the low level driver implementation for the LSM303_M Magnetometer + * + */ +class LSM303Magnetometer : public MicroBitCompass +{ + MicroBitI2C& i2c; // The I2C interface to use. + MicroBitPin& int1; // Data ready interrupt. + uint16_t address; // I2C address of this compass. + + public: + + /** + * Constructor. + * Create a software abstraction of an compass. + * + * @param coordinateSpace The orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id The unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER + * + */ + LSM303Magnetometer(MicroBitI2C& _i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address = LSM303_M_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS); + + /** + * Configures the compass for the 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 compass could not be configured. + */ + virtual int configure(); + + /** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this function to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + */ + virtual int requestUpdate(); + + /** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ + virtual void idleCallback(); + + + /** + * Destructor. + */ + ~LSM303Magnetometer(); + +}; + +#endif + diff --git a/inc/drivers/MAG3110.h b/inc/drivers/MAG3110.h new file mode 100644 index 0000000..381d6a1 --- /dev/null +++ b/inc/drivers/MAG3110.h @@ -0,0 +1,134 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MAG3110_H +#define MAG3110_H + +#include "MicroBitConfig.h" +#include "MicroBitComponent.h" +#include "CoordinateSystem.h" +#include "MicroBitCompass.h" +#include "MicroBitI2C.h" +#include "MicroBitUtil.h" + +/** + * Term to convert sample data into SI units + */ +#define MAG3110_NORMALIZE_SAMPLE(x) (100 * (int)x) + +/** + * MAG3110 MAGIC ID value + * Returned from the MAG_WHO_AM_I register for ID purposes. + */ +#define MAG3110_WHOAMI_VAL 0xC4 + +/** + * I2C constants + */ +#define MAG3110_DEFAULT_ADDR 0x1D + +/** + * MAG3110 Register map + */ +#define MAG_DR_STATUS 0x00 +#define MAG_OUT_X_MSB 0x01 +#define MAG_OUT_X_LSB 0x02 +#define MAG_OUT_Y_MSB 0x03 +#define MAG_OUT_Y_LSB 0x04 +#define MAG_OUT_Z_MSB 0x05 +#define MAG_OUT_Z_LSB 0x06 +#define MAG_WHOAMI 0x07 +#define MAG_SYSMOD 0x08 +#define MAG_OFF_X_MSB 0x09 +#define MAG_OFF_X_LSB 0x0A +#define MAG_OFF_Y_MSB 0x0B +#define MAG_OFF_Y_LSB 0x0C +#define MAG_OFF_Z_MSB 0x0D +#define MAG_OFF_Z_LSB 0x0E +#define MAG_DIE_TEMP 0x0F +#define MAG_CTRL_REG1 0x10 +#define MAG_CTRL_REG2 0x11 + + +/** + * Class definition for MAG3110. + * + * This class provides the low level driver implementation for the MAG3110 Magnetometer + * + */ +class MAG3110 : public MicroBitCompass +{ + MicroBitI2C& i2c; // The I2C interface to use. + MicroBitPin& int1; // Data ready interrupt. + uint16_t address; // I2C address of this compass. + + public: + + /** + * Constructor. + * Create a software abstraction of an compass. + * + * @param coordinateSpace The orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id The unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER + * + */ + MAG3110(MicroBitI2C& _i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS); + + /** + * Configures the compass for the 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 compass could not be configured. + */ + virtual int configure(); + + /** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this function to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + */ + virtual int requestUpdate(); + + /** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ + virtual void idleCallback(); + + + /** + * Destructor. + */ + ~MAG3110(); + +}; + +#endif + diff --git a/inc/drivers/MMA8653.h b/inc/drivers/MMA8653.h new file mode 100644 index 0000000..855d648 --- /dev/null +++ b/inc/drivers/MMA8653.h @@ -0,0 +1,128 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MMA8653_H +#define MMA8653_H + +#include "MicroBitConfig.h" +#include "MicroBitComponent.h" +#include "CoordinateSystem.h" +#include "MicroBitAccelerometer.h" +#include "MicroBitI2C.h" +#include "MicroBitUtil.h" + +/** + * Relevant pin assignments + */ +#define MICROBIT_PIN_ACCEL_DATA_READY P0_28 + +/** + * I2C constants + */ +#define MMA8653_DEFAULT_ADDR 0x3A + +/** + * MMA8653 Register map (partial) + */ +#define MMA8653_STATUS 0x00 +#define MMA8653_OUT_X_MSB 0x01 +#define MMA8653_WHOAMI 0x0D +#define MMA8653_XYZ_DATA_CFG 0x0E +#define MMA8653_CTRL_REG1 0x2A +#define MMA8653_CTRL_REG2 0x2B +#define MMA8653_CTRL_REG3 0x2C +#define MMA8653_CTRL_REG4 0x2D +#define MMA8653_CTRL_REG5 0x2E + + +/** + * MMA8653 constants + */ +#define MMA8653_WHOAMI_VAL 0x5A + +/** + * Class definition for MMA8653. + * This class provides a simple wrapper between the hybrid FXOS8700 accelerometer and higher level accelerometer funcitonality. + */ +class MMA8653 : public MicroBitAccelerometer +{ + MicroBitI2C& i2c; // The I2C interface to use. + MicroBitPin& int1; // Data ready interrupt. + uint16_t address; // I2C address of this accelerometer. + + public: + + /** + * Constructor. + * Create a software abstraction of an accelerometer. + * + * @param coordinateSpace The orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id The unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER + * + */ + MMA8653(MicroBitI2C& _i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address = MMA8653_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_ACCELEROMETER); + + /** + * 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. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ + virtual int configure(); + + /** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ + virtual int requestUpdate(); + + /** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ + virtual void idleCallback(); + + + /** + * Destructor. + */ + ~MMA8653(); + +}; + +#endif + diff --git a/inc/drivers/MicroBitAccelerometer.h b/inc/drivers/MicroBitAccelerometer.h index cc92913..9e1598a 100644 --- a/inc/drivers/MicroBitAccelerometer.h +++ b/inc/drivers/MicroBitAccelerometer.h @@ -1,8 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016 British Broadcasting Corporation. -This software is provided by Lancaster University by arrangement with the BBC. +Copyright (c) 2017 Lancaster University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,61 +22,28 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef MICROBIT_ACCELEROMETER_H -#define MICROBIT_ACCELEROMETER_H +#ifndef MICROBIT_ACCELEROMTER_H +#define MICROBIT_ACCELEROMTER_H -#include "mbed.h" #include "MicroBitConfig.h" #include "MicroBitComponent.h" -#include "MicroBitCoordinateSystem.h" -#include "MicroBitI2C.h" +#include "MicroBitPin.h" +#include "CoordinateSystem.h" /** - * Relevant pin assignments - */ -#define MICROBIT_PIN_ACCEL_DATA_READY P0_28 + * Status flags + */ +#define MICROBIT_ACCELEROMETER_IMU_DATA_VALID 0x02 +#define MICROBIT_ACCEL_ADDED_TO_IDLE 0x04 /** - * Status flags - */ -#define MICROBIT_ACCEL_PITCH_ROLL_VALID 0x02 -#define MICROBIT_ACCEL_ADDED_TO_IDLE 0x04 - -/** - * I2C constants - */ -#define MMA8653_DEFAULT_ADDR 0x3A - -/** - * MMA8653 Register map (partial) - */ -#define MMA8653_STATUS 0x00 -#define MMA8653_OUT_X_MSB 0x01 -#define MMA8653_WHOAMI 0x0D -#define MMA8653_XYZ_DATA_CFG 0x0E -#define MMA8653_CTRL_REG1 0x2A -#define MMA8653_CTRL_REG2 0x2B -#define MMA8653_CTRL_REG3 0x2C -#define MMA8653_CTRL_REG4 0x2D -#define MMA8653_CTRL_REG5 0x2E - - -/** - * MMA8653 constants - */ -#define MMA8653_WHOAMI_VAL 0x5A - -#define MMA8653_SAMPLE_RANGES 3 -#define MMA8653_SAMPLE_RATES 8 - -/** - * Accelerometer events - */ + * Accelerometer events + */ #define MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE 1 /** - * Gesture events - */ + * Gesture events + */ #define MICROBIT_ACCELEROMETER_EVT_NONE 0 #define MICROBIT_ACCELEROMETER_EVT_TILT_UP 1 #define MICROBIT_ACCELEROMETER_EVT_TILT_DOWN 2 @@ -92,8 +58,8 @@ DEALINGS IN THE SOFTWARE. #define MICROBIT_ACCELEROMETER_EVT_SHAKE 11 /** - * Gesture recogniser constants - */ + * Gesture recogniser constants + */ #define MICROBIT_ACCELEROMETER_REST_TOLERANCE 200 #define MICROBIT_ACCELEROMETER_TILT_TOLERANCE 200 #define MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE 400 @@ -102,39 +68,16 @@ DEALINGS IN THE SOFTWARE. #define MICROBIT_ACCELEROMETER_6G_TOLERANCE 6144 #define MICROBIT_ACCELEROMETER_8G_TOLERANCE 8192 #define MICROBIT_ACCELEROMETER_GESTURE_DAMPING 5 -#define MICROBIT_ACCELEROMETER_SHAKE_DAMPING 10 +#define MICROBIT_ACCELEROMETER_SHAKE_DAMPING 10 #define MICROBIT_ACCELEROMETER_SHAKE_RTX 30 #define MICROBIT_ACCELEROMETER_REST_THRESHOLD (MICROBIT_ACCELEROMETER_REST_TOLERANCE * MICROBIT_ACCELEROMETER_REST_TOLERANCE) -#define MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD (MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE * MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE) -#define MICROBIT_ACCELEROMETER_3G_THRESHOLD (MICROBIT_ACCELEROMETER_3G_TOLERANCE * MICROBIT_ACCELEROMETER_3G_TOLERANCE) -#define MICROBIT_ACCELEROMETER_6G_THRESHOLD (MICROBIT_ACCELEROMETER_6G_TOLERANCE * MICROBIT_ACCELEROMETER_6G_TOLERANCE) -#define MICROBIT_ACCELEROMETER_8G_THRESHOLD (MICROBIT_ACCELEROMETER_8G_TOLERANCE * MICROBIT_ACCELEROMETER_8G_TOLERANCE) +#define MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD ((uint32_t)MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE * (uint32_t)MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE) +#define MICROBIT_ACCELEROMETER_3G_THRESHOLD ((uint32_t)MICROBIT_ACCELEROMETER_3G_TOLERANCE * (uint32_t)MICROBIT_ACCELEROMETER_3G_TOLERANCE) +#define MICROBIT_ACCELEROMETER_6G_THRESHOLD ((uint32_t)MICROBIT_ACCELEROMETER_6G_TOLERANCE * (uint32_t)MICROBIT_ACCELEROMETER_6G_TOLERANCE) +#define MICROBIT_ACCELEROMETER_8G_THRESHOLD ((uint32_t)MICROBIT_ACCELEROMETER_8G_TOLERANCE * (uint32_t)MICROBIT_ACCELEROMETER_8G_TOLERANCE) #define MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD 4 -struct MMA8653Sample -{ - int16_t x; - int16_t y; - 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[]; - struct ShakeHistory { uint16_t shaken:1, @@ -151,302 +94,257 @@ struct ShakeHistory }; /** - * Class definition for MicroBit Accelerometer. - * - * Represents an implementation of the Freescale MMA8653 3 axis accelerometer - * Also includes basic data caching and on demand activation. + * Class definition for MicroBitAccelerometer. */ class MicroBitAccelerometer : public 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. - float pitch; // Pitch of the device, in radians. - MicroBitI2C& i2c; // The I2C interface to use. - float roll; // Roll of the device, in radians. - uint8_t sigma; // the number of ticks that the instantaneous gesture has been stable. - uint8_t impulseSigma; // the number of ticks since an impulse event has been generated. - uint16_t lastGesture; // the last, stable gesture recorded. - uint16_t currentGesture; // the instantaneous, unfiltered gesture detected. - ShakeHistory shake; // State information needed to detect shake events. + protected: + + uint16_t samplePeriod; // The time between samples, in milliseconds. + uint8_t sampleRange; // The sample range of the accelerometer in g. + Sample3D sample; // The last sample read, in the coordinate system specified by the coordinateSpace variable. + Sample3D sampleENU; // The last sample read, in raw ENU format (stored in case requests are made for data in other coordinate spaces) + CoordinateSpace &coordinateSpace; // The coordinate space transform (if any) to apply to the raw data from the hardware. + + float pitch; // Pitch of the device, in radians. + float roll; // Roll of the device, in radians. + + uint8_t sigma; // the number of ticks that the instantaneous gesture has been stable. + uint8_t impulseSigma; // the number of ticks since an impulse event has been generated. + uint16_t lastGesture; // the last, stable gesture recorded. + uint16_t currentGesture; // the instantaneous, unfiltered gesture detected. + ShakeHistory shake; // State information needed to detect shake events. public: - /** - * Constructor. - * Create a software abstraction of an accelerometer. - * - * @param _i2c an instance of MicroBitI2C used to communicate with the onboard accelerometer. - * - * @param address the default I2C address of the accelerometer. Defaults to: MMA8653_DEFAULT_ADDR. - * - * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER - * - * @code - * MicroBitI2C i2c = MicroBitI2C(I2C_SDA0, I2C_SCL0); - * - * MicroBitAccelerometer accelerometer = MicroBitAccelerometer(i2c); - * @endcode - */ - MicroBitAccelerometer(MicroBitI2C &_i2c, uint16_t address = MMA8653_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_ACCELEROMETER); + /** + * Constructor. + * Create a software abstraction of an accelerometer. + * + * @param coordinateSpace the orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER + * + */ + MicroBitAccelerometer(CoordinateSpace &coordinateSpace, uint16_t id = MICROBIT_ID_ACCELEROMETER); - /** - * 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. - */ - int configure(); + /** + * Attempts to set the sample rate of the accelerometer to the specified value (in ms). + * + * @param period the requested time between samples, in milliseconds. + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. + * + * @note The requested rate may not be possible on the hardware. In this case, the + * nearest lower rate is chosen. + * + * @note This method should be overriden (if supported) by specific accelerometer device drivers. + */ + virtual int setPeriod(int period); - /** - * Reads the acceleration data from the accelerometer, and stores it in our buffer. - * This only happens if the accelerometer indicates that it has new data via int1. - * - * On first use, this member function will attempt to add this component to the - * list of fiber components in order to constantly update the values stored - * by this object. - * - * This technique is called lazy instantiation, and it means that we do not - * obtain the overhead from non-chalantly adding this component to fiber components. - * - * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails. - */ - int updateSample(); + /** + * Reads the currently configured sample rate of the accelerometer. + * + * @return The time between samples, in milliseconds. + */ + virtual int getPeriod(); - /** - * Attempts to set the sample rate of the accelerometer to the specified value (in ms). - * - * @param period the requested time between samples, in milliseconds. - * - * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. - * - * @code - * // sample rate is now 20 ms. - * accelerometer.setPeriod(20); - * @endcode - * - * @note The requested rate may not be possible on the hardware. In this case, the - * nearest lower rate is chosen. - */ - int setPeriod(int period); + /** + * Attempts to set the sample range of the accelerometer to the specified value (in g). + * + * @param range The requested sample range of samples, in g. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. + * + * @note The requested range may not be possible on the hardware. In this case, the + * nearest lower range is chosen. + * + * @note This method should be overriden (if supported) by specific accelerometer device drivers. + */ + virtual int setRange(int range); - /** - * Reads the currently configured sample rate of the accelerometer. - * - * @return The time between samples, in milliseconds. - */ - int getPeriod(); + /** + * Reads the currently configured sample range of the accelerometer. + * + * @return The sample range, in g. + */ + virtual int getRange(); - /** - * Attempts to set the sample range of the accelerometer to the specified value (in g). - * - * @param range The requested sample range of samples, in g. - * - * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. - * - * @code - * // the sample range of the accelerometer is now 8G. - * accelerometer.setRange(8); - * @endcode - * - * @note The requested range may not be possible on the hardware. In this case, the - * nearest lower range is chosen. - */ - int setRange(int range); + /** + * 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. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ + virtual int configure(); - /** - * Reads the currently configured sample range of the accelerometer. - * - * @return The sample range, in g. - */ - int getRange(); + /** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ + virtual int requestUpdate(); - /** - * Attempts to read the 8 bit ID from the accelerometer, this can be used for - * validation purposes. - * - * @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails. - * - * @code - * accelerometer.whoAmI(); - * @endcode - */ - int whoAmI(); + /** + * Stores data from the accelerometer sensor in our buffer, and perform gesture tracking. + * + * On first use, this member function will attempt to add this component to the + * list of fiber components in order to constantly update the values stored + * by this object. + * + * This lazy instantiation means that we do not + * obtain the overhead from non-chalantly adding this component to fiber components. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails. + */ + virtual int update(); - /** - * Reads the value of the X axis from the latest update retrieved from the accelerometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The force measured in the X axis, in milli-g. - * - * @code - * accelerometer.getX(); - * @endcode - */ - int getX(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN); + /** + * Reads the last accelerometer value stored, and provides it in the coordinate system requested. + * + * @param coordinateSpace The coordinate system to use. + * @return The force measured in each axis, in milli-g. + */ + Sample3D getSample(CoordinateSystem coordinateSystem); - /** - * Reads the value of the Y axis from the latest update retrieved from the accelerometer. - * - * @return The force measured in the Y axis, in milli-g. - * - * @code - * accelerometer.getY(); - * @endcode - */ - int getY(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN); + /** + * Reads the last accelerometer value stored, and in the coordinate system defined in the constructor. + * @return The force measured in each axis, in milli-g. + */ + Sample3D getSample(); - /** - * Reads the value of the Z axis from the latest update retrieved from the accelerometer. - * - * @return The force measured in the Z axis, in milli-g. - * - * @code - * accelerometer.getZ(); - * @endcode - */ - int getZ(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN); + /** + * reads the value of the x axis from the latest update retrieved from the accelerometer, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the x axis, in milli-g. + */ + int getX(); - /** - * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer. - * - * @return The pitch of the device, in degrees. - * - * @code - * accelerometer.getPitch(); - * @endcode - */ - int getPitch(); + /** + * reads the value of the y axis from the latest update retrieved from the accelerometer, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the y axis, in milli-g. + */ + int getY(); - /** - * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer. - * - * @return The pitch of the device, in radians. - * - * @code - * accelerometer.getPitchRadians(); - * @endcode - */ - float getPitchRadians(); + /** + * reads the value of the z axis from the latest update retrieved from the accelerometer, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the z axis, in milli-g. + */ + int getZ(); - /** - * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer. - * - * @return The roll of the device, in degrees. - * - * @code - * accelerometer.getRoll(); - * @endcode - */ - int getRoll(); + /** + * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer. + * + * @return The pitch of the device, in degrees. + * + * @code + * accelerometer.getPitch(); + * @endcode + */ + int getPitch(); - /** - * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer. - * - * @return The roll of the device, in radians. - * - * @code - * accelerometer.getRollRadians(); - * @endcode - */ - float getRollRadians(); + /** + * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer. + * + * @return The pitch of the device, in radians. + * + * @code + * accelerometer.getPitchRadians(); + * @endcode + */ + float getPitchRadians(); - /** - * Retrieves the last recorded gesture. - * - * @return The last gesture that was detected. - * - * Example: - * @code - * MicroBitDisplay display; - * - * if (accelerometer.getGesture() == SHAKE) - * display.scroll("SHAKE!"); - * @endcode - */ - uint16_t getGesture(); + /** + * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer. + * + * @return The roll of the device, in degrees. + * + * @code + * accelerometer.getRoll(); + * @endcode + */ + int getRoll(); - /** - * A periodic callback invoked by the fiber scheduler idle thread. - * - * Internally calls updateSample(). - */ - virtual void idleTick(); + /** + * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer. + * + * @return The roll of the device, in radians. + * + * @code + * accelerometer.getRollRadians(); + * @endcode + */ + float getRollRadians(); - /** - * Destructor for MicroBitButton, where we deregister this instance from the array of fiber components. - */ - ~MicroBitAccelerometer(); + /** + * Retrieves the last recorded gesture. + * + * @return The last gesture that was detected. + * + * Example: + * @code + * + * if (accelerometer.getGesture() == SHAKE) + * display.scroll("SHAKE!"); + * @endcode + */ + uint16_t getGesture(); + + /** + * Destructor. + */ + ~MicroBitAccelerometer(); private: - /** - * 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. - */ - int writeCommand(uint8_t reg, uint8_t value); + /** + * Recalculate roll and pitch values for the current sample. + * + * @note 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 recalculatePitchRoll(); - /** - * Issues a read command, copying data 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. - */ - int readCommand(uint8_t reg, uint8_t* buffer, int length); + /** + * Updates the basic gesture recognizer. This performs instantaneous pose recognition, and also some low pass filtering to promote + * stability. + */ + void updateGesture(); - /** - * Recalculate roll and pitch values for the current sample. - * - * @note 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 recalculatePitchRoll(); + /** + * A service function. + * It 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. + */ + uint32_t instantaneousAccelerationSquared(); - /** - * Updates the basic gesture recognizer. This performs instantaneous pose recognition, and also some low pass filtering to promote - * stability. - */ - void updateGesture(); - - /** - * A service function. - * It 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. - */ - int instantaneousAccelerationSquared(); - - /** - * Service function. - * Determines a 'best guess' posture of the device based on instantaneous data. - * - * This makes no use of historic data, and forms this input to the filter implemented in updateGesture(). - * - * @return A 'best guess' of the current posture of the device, based on instanataneous data. - */ - uint16_t instantaneousPosture(); + /** + * Service function. + * Determines a 'best guess' posture of the device based on instantaneous data. + * + * This makes no use of historic data, and forms this input to the filter implemented in updateGesture(). + * + * @return A 'best guess' of the current posture of the device, based on instanataneous data. + */ + uint16_t instantaneousPosture(); }; #endif diff --git a/inc/drivers/MicroBitCompass.h b/inc/drivers/MicroBitCompass.h index d534ce7..62e1cdc 100644 --- a/inc/drivers/MicroBitCompass.h +++ b/inc/drivers/MicroBitCompass.h @@ -1,8 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016 British Broadcasting Corporation. -This software is provided by Lancaster University by arrangement with the BBC. +Copyright (c) 2017 Lancaster University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,491 +22,266 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef MICROBIT_COMPASS_H -#define MICROBIT_COMPASS_H +#ifndef MICROBIT_COMPASS +#define MICROBIT_COMPASS -#include "mbed.h" #include "MicroBitConfig.h" #include "MicroBitComponent.h" -#include "MicroBitCoordinateSystem.h" +#include "CoordinateSystem.h" #include "MicroBitAccelerometer.h" -#include "MicroBitStorage.h" + /** - * Relevant pin assignments - */ -#define MICROBIT_PIN_COMPASS_DATA_READY P0_29 + * Status flags + */ +#define MICROBIT_COMPASS_STATUS_RUNNING 0x01 +#define MICROBIT_COMPASS_STATUS_CALIBRATED 0x02 +#define MICROBIT_COMPASS_STATUS_CALIBRATING 0x04 +#define MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE 0x08 /** - * I2C constants - */ -#define MAG3110_DEFAULT_ADDR 0x1D + * Accelerometer events + */ +#define MICROBIT_COMPASS_EVT_DATA_UPDATE 1 +#define MICROBIT_COMPASS_EVT_CONFIG_NEEDED 2 +#define MICROBIT_COMPASS_EVT_CALIBRATE 3 +#define MICROBIT_COMPASS_EVT_CALIBRATION_NEEDED 4 /** - * MAG3110 Register map - */ -#define MAG_DR_STATUS 0x00 -#define MAG_OUT_X_MSB 0x01 -#define MAG_OUT_X_LSB 0x02 -#define MAG_OUT_Y_MSB 0x03 -#define MAG_OUT_Y_LSB 0x04 -#define MAG_OUT_Z_MSB 0x05 -#define MAG_OUT_Z_LSB 0x06 -#define MAG_WHOAMI 0x07 -#define MAG_SYSMOD 0x08 -#define MAG_OFF_X_MSB 0x09 -#define MAG_OFF_X_LSB 0x0A -#define MAG_OFF_Y_MSB 0x0B -#define MAG_OFF_Y_LSB 0x0C -#define MAG_OFF_Z_MSB 0x0D -#define MAG_OFF_Z_LSB 0x0E -#define MAG_DIE_TEMP 0x0F -#define MAG_CTRL_REG1 0x10 -#define MAG_CTRL_REG2 0x11 - -/** - * Configuration options - */ -struct MAG3110SampleRateConfig -{ - uint32_t sample_period; - uint8_t ctrl_reg1; -}; - -extern const MAG3110SampleRateConfig MAG3110SampleRate[]; - -#define MAG3110_SAMPLE_RATES 11 - -/** - * Compass events - */ -#define MICROBIT_COMPASS_EVT_CAL_REQUIRED 1 // DEPRECATED -#define MICROBIT_COMPASS_EVT_CAL_START 2 // DEPRECATED -#define MICROBIT_COMPASS_EVT_CAL_END 3 // DEPRECATED - -#define MICROBIT_COMPASS_EVT_DATA_UPDATE 4 -#define MICROBIT_COMPASS_EVT_CONFIG_NEEDED 5 -#define MICROBIT_COMPASS_EVT_CALIBRATE 6 -#define MICROBIT_COMPASS_EVT_CALIBRATION_NEEDED 7 -/** - * Status Bits - */ -#define MICROBIT_COMPASS_STATUS_CALIBRATED 2 -#define MICROBIT_COMPASS_STATUS_CALIBRATING 4 -#define MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE 8 - -/** - * Term to convert sample data into SI units - */ -#define MAG3110_NORMALIZE_SAMPLE(x) (100*x) - -/** - * MAG3110 MAGIC ID value - * Returned from the MAG_WHO_AM_I register for ID purposes. - */ -#define MAG3110_WHOAMI_VAL 0xC4 - -struct CompassSample -{ - int x; - int y; - int z; - - CompassSample() - { - this->x = 0; - this->y = 0; - this->z = 0; - } - - CompassSample(int x, int y, int z) - { - this->x = x; - this->y = y; - this->z = z; - } - - bool operator==(const CompassSample& other) const - { - return x == other.x && y == other.y && z == other.z; - } - - bool operator!=(const CompassSample& other) const - { - return !(x == other.x && y == other.y && z == other.z); - } -}; - -/** - * Class definition for MicroBit Compass. - * - * Represents an implementation of the Freescale MAG3110 I2C Magnetmometer. - * Also includes basic caching, calibration and on demand activation. - */ + * Class definition for a general e-compass. + */ class MicroBitCompass : public MicroBitComponent { - uint16_t address; // I2C address of the magnetmometer. - uint16_t samplePeriod; // The time between samples, in millseconds. + protected: - CompassSample average; // Centre point of sample data. - CompassSample sample; // The latest sample data recorded. - DigitalIn int1; // Data ready interrupt. - MicroBitI2C& i2c; // The I2C interface the sensor is connected to. - MicroBitAccelerometer* accelerometer; // The accelerometer to use for tilt compensation. - MicroBitStorage* storage; // An instance of MicroBitStorage used for persistence. + uint16_t samplePeriod; // The time between samples, in milliseconds. + Sample3D average; // The zero offset of this compass (generated by calibration) + Sample3D sample; // The last sample read, in the coordinate system specified by the coordinateSpace variable. + Sample3D sampleENU; // The last sample read, in raw ENU format (stored in case requests are made for data in other coordinate spaces) + CoordinateSpace &coordinateSpace; // The coordinate space transform (if any) to apply to the raw data from the hardware. + MicroBitAccelerometer* accelerometer; // The accelerometer to use for tilt compensation. public: - /** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param _accelerometer an instance of the accelerometer, used for tilt compensation. - * - * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitAccelerometer accelerometer(i2c); - * - * MicroBitStorage storage; - * - * MicroBitCompass compass(i2c, accelerometer, storage); - * @endcode - */ - MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, MicroBitStorage& _storage, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS); + /** + * Constructor. + * Create a software abstraction of an e-compass. + * + * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_COMPASS + * @param coordinateSpace the orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * + */ + MicroBitCompass(CoordinateSpace &coordinateSpace, uint16_t id = MICROBIT_ID_COMPASS); - /** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param _accelerometer an instance of the accelerometer, used for tilt compensation. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitAccelerometer accelerometer(i2c); - * - * MicroBitCompass compass(i2c, accelerometer, storage); - * @endcode - */ - MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS); + /** + * Constructor. + * Create a software abstraction of an e-compass. + * + * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_COMPASS + * @param accel the accelerometer to use for tilt compensation + * @param coordinateSpace the orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * + */ + MicroBitCompass(MicroBitAccelerometer &accel, CoordinateSpace &coordinateSpace, uint16_t id = MICROBIT_ID_COMPASS); - /** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitStorage storage; - * - * MicroBitCompass compass(i2c, storage); - * @endcode - */ - MicroBitCompass(MicroBitI2C& _i2c, MicroBitStorage& _storage, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS); + /** + * Gets the current heading of the device, relative to magnetic north. + * + * If the compass is not calibrated, it will raise the COMPASS_EVT_CALIBRATE event. + * + * Users wishing to implement their own calibration algorithms should listen for this event, + * using MESSAGE_BUS_LISTENER_IMMEDIATE model. This ensures that calibration is complete before + * the user program continues. + * + * @return the current heading, in degrees. Or CALIBRATION_IN_PROGRESS if the compass is calibrating. + * + * @code + * compass.heading(); + * @endcode + */ + int heading(); - /** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitCompass compass(i2c); - * @endcode - */ - MicroBitCompass(MicroBitI2C& _i2c, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS); + /** + * Determines the overall magnetic field strength based on the latest update from the magnetometer. + * + * @return The magnetic force measured across all axis, in nano teslas. + * + * @code + * compass.getFieldStrength(); + * @endcode + */ + int getFieldStrength(); - /** - * Configures the compass for the 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 or MICROBIT_I2C_ERROR if the magnetometer could not be configured. - */ - int configure(); + /** + * Perform a calibration of the compass. + * + * This method will be called automatically if a user attempts to read a compass value when + * the compass is uncalibrated. It can also be called at any time by the user. + * + * The method will only return once the compass has been calibrated. + * + * @return MICROBIT_OK, MICROBIT_I2C_ERROR if the magnetometer could not be accessed, + * or MICROBIT_CALIBRATION_REQUIRED if the calibration algorithm failed to complete successfully. + * + * @note THIS MUST BE CALLED TO GAIN RELIABLE VALUES FROM THE COMPASS + */ + int calibrate(); - /** - * Attempts to set the sample rate of the compass to the specified value (in ms). - * - * @param period the requested time between samples, in milliseconds. - * - * @return MICROBIT_OK or MICROBIT_I2C_ERROR if the magnetometer could not be updated. - * - * @code - * // sample rate is now 20 ms. - * compass.setPeriod(20); - * @endcode - * - * @note The requested rate may not be possible on the hardware. In this case, the - * nearest lower rate is chosen. - */ - int setPeriod(int period); + /** + * Configure the compass to use the calibration data that is supplied to this call. + * + * Calibration data is comprised of the perceived zero offset of each axis of the compass. + * + * After calibration this should now take into account trimming errors in the magnetometer, + * and any "hard iron" offsets on the device. + * + * @param calibration A Sample3D containing the offsets for the x, y and z axis. + */ + void setCalibration(Sample3D calibration); - /** - * Reads the currently configured sample rate of the compass. - * - * @return The time between samples, in milliseconds. - */ - int getPeriod(); + /** + * Provides the calibration data currently in use by the compass. + * + * More specifically, the x, y and z zero offsets of the compass. + * + * @return A Sample3D containing the offsets for the x, y and z axis. + */ + Sample3D getCalibration(); - /** - * Gets the current heading of the device, relative to magnetic north. - * - * If the compass is not calibrated, it will raise the MICROBIT_COMPASS_EVT_CALIBRATE event. - * - * Users wishing to implement their own calibration algorithms should listen for this event, - * using MESSAGE_BUS_LISTENER_IMMEDIATE model. This ensures that calibration is complete before - * the user program continues. - * - * @return the current heading, in degrees. Or MICROBIT_CALIBRATION_IN_PROGRESS if the compass is calibrating. - * - * @code - * compass.heading(); - * @endcode - */ - int heading(); + /** + * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration. + */ + int isCalibrated(); - /** - * Attempts to read the 8 bit ID from the magnetometer, this can be used for - * validation purposes. - * - * @return the 8 bit ID returned by the magnetometer, or MICROBIT_I2C_ERROR if the request fails. - * - * @code - * compass.whoAmI(); - * @endcode - */ - int whoAmI(); + /** + * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating. + */ + int isCalibrating(); - /** - * Reads the value of the X axis from the latest update retrieved from the magnetometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The magnetic force measured in the X axis, in nano teslas. - * - * @code - * compass.getX(); - * @endcode - */ - int getX(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN); + /** + * Clears the calibration held in persistent storage, and sets the calibrated flag to zero. + */ + void clearCalibration(); - /** - * Reads the value of the Y axis from the latest update retrieved from the magnetometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The magnetic force measured in the Y axis, in nano teslas. - * - * @code - * compass.getY(); - * @endcode - */ - int getY(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN); - /** - * Reads the value of the Z axis from the latest update retrieved from the magnetometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The magnetic force measured in the Z axis, in nano teslas. - * - * @code - * compass.getZ(); - * @endcode - */ - int getZ(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN); + /** + * Configures the device for the 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 compass could not be configured. + */ + int configure(); - /** - * Determines the overall magnetic field strength based on the latest update from the magnetometer. - * - * @return The magnetic force measured across all axis, in nano teslas. - * - * @code - * compass.getFieldStrength(); - * @endcode - */ - int getFieldStrength(); + /** + * Attempts to set the sample rate of the compass to the specified period value (in ms). + * + * @param period the requested time between samples, in milliseconds. + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. + * + * @note The requested rate may not be possible on the hardware. In this case, the + * nearest lower rate is chosen. + * + * @note This method should be overriden (if supported) by specific magnetometer device drivers. + */ + virtual int setPeriod(int period); - /** - * Reads the current die temperature of the compass. - * - * @return the temperature in degrees celsius, or MICROBIT_I2C_ERROR if the temperature reading could not be retreived - * from the accelerometer. - */ - int readTemperature(); + /** + * Reads the currently configured sample rate of the compass. + * + * @return The time between samples, in milliseconds. + */ + virtual int getPeriod(); - /** - * Perform a calibration of the compass. - * - * This method will be called automatically if a user attempts to read a compass value when - * the compass is uncalibrated. It can also be called at any time by the user. - * - * The method will only return once the compass has been calibrated. - * - * @return MICROBIT_OK, MICROBIT_I2C_ERROR if the magnetometer could not be accessed, - * or MICROBIT_CALIBRATION_REQUIRED if the calibration algorithm failed to complete successfully. - * - * @note THIS MUST BE CALLED TO GAIN RELIABLE VALUES FROM THE COMPASS - */ - int calibrate(); + /** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ + virtual int requestUpdate(); - /** - * Configure the compass to use the calibration data that is supplied to this call. - * - * Calibration data is comprised of the perceived zero offset of each axis of the compass. - * - * After calibration this should now take into account trimming errors in the magnetometer, - * and any "hard iron" offsets on the device. - * - * @param calibration A CompassSample containing the offsets for the x, y and z axis. - */ - void setCalibration(CompassSample calibration); + /** + * Stores data from the compass sensor in our buffer. + * + * On first use, this member function will attempt to add this component to the + * list of fiber components in order to constantly update the values stored + * by this object. + * + * This lazy instantiation means that we do not + * obtain the overhead from non-chalantly adding this component to fiber components. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails. + */ + virtual int update(); - /** - * Provides the calibration data currently in use by the compass. - * - * More specifically, the x, y and z zero offsets of the compass. - * - * @return calibration A CompassSample containing the offsets for the x, y and z axis. - */ - CompassSample getCalibration(); + /** + * Reads the last compass value stored, and provides it in the coordinate system requested. + * + * @param coordinateSpace The coordinate system to use. + * @return The force measured in each axis, in milli-g. + */ + Sample3D getSample(CoordinateSystem coordinateSystem); - /** - * Updates the local sample, only if the compass indicates that - * data is stale. - * - * @note Can be used to trigger manual updates, if the device is running without a scheduler. - * Also called internally by all get[X,Y,Z]() member functions. - */ - int updateSample(); + /** + * Reads the last compass value stored, and in the coordinate system defined in the constructor. + * @return The force measured in each axis, in milli-g. + */ + Sample3D getSample(); - /** - * Periodic callback from MicroBit idle thread. - * - * Calls updateSample(). - */ - virtual void idleTick(); + /** + * reads the value of the x axis from the latest update retrieved from the compass, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the x axis, in milli-g. + */ + int getX(); - /** - * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration. - */ - int isCalibrated(); + /** + * reads the value of the y axis from the latest update retrieved from the compass, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the y axis, in milli-g. + */ + int getY(); - /** - * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating. - */ - int isCalibrating(); + /** + * reads the value of the z axis from the latest update retrieved from the compass, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the z axis, in milli-g. + */ + int getZ(); - /** - * Clears the calibration held in persistent storage, and sets the calibrated flag to zero. - */ - void clearCalibration(); - /** - * Destructor for MicroBitCompass, where we deregister this instance from the array of fiber components. - */ - ~MicroBitCompass(); + /** + * Destructor. + */ + ~MicroBitCompass(); private: - /** - * 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. - */ - int writeCommand(uint8_t reg, uint8_t value); + /** + * Internal helper used to de-duplicate code in the constructors + * + */ + void init(uint16_t id); - /** - * Issues a read command, copying data 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. - */ - int readCommand(uint8_t reg, uint8_t* buffer, int length); + /** + * Calculates a tilt compensated bearing of the device, using the accelerometer. + */ + int tiltCompensatedBearing(); - /** - * Issues a read of a given address, and returns the value. - * - * Blocks the calling thread until complete. - * - * @param reg The address of the 16 bit register to access. - * - * @return The register value, interpreted as a 16 but signed value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed. - */ - int read16(uint8_t reg); - - /** - * Issues a read of a given address, and returns the value. - * - * Blocks the calling thread until complete. - * - * @param reg The address of the 16 bit register to access. - * - * @return The register value, interpreted as a 8 bit unsigned value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed. - */ - int read8(uint8_t reg); - - /** - * Calculates a tilt compensated bearing of the device, using the accelerometer. - */ - int tiltCompensatedBearing(); - - /** - * Calculates a non-tilt compensated bearing of the device. - */ - int basicBearing(); - - /** - * An initialisation member function used by the many constructors of MicroBitCompass. - * - * @param id the unique identifier for this compass instance. - * - * @param address the base address of the magnetometer on the i2c bus. - */ - void init(uint16_t id, uint16_t address); + /** + * Calculates a non-tilt compensated bearing of the device. + */ + int basicBearing(); }; #endif diff --git a/inc/drivers/MicroBitI2C.h b/inc/drivers/MicroBitI2C.h index 6621d3c..563c09b 100644 --- a/inc/drivers/MicroBitI2C.h +++ b/inc/drivers/MicroBitI2C.h @@ -102,6 +102,35 @@ class MicroBitI2C : public I2C * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if an unresolved write failure is detected. */ int write(int address, const char *data, int length, bool repeated = false); + + /** + * Issues a standard, 2 byte I2C command write. + * + * Blocks the calling thread until complete. + * + * @param address The address of the I2C device to write to. + * @param reg The address of the register in the device to write. + * @param value The value to write. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed. + */ + int writeRegister(uint8_t address, uint8_t reg, uint8_t value); + + /** + * Issues a read command, copying data 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. + */ + int readRegister(uint8_t address, uint8_t reg, uint8_t* buffer, int length); + }; #endif diff --git a/inc/drivers/MicroBitIO.h b/inc/drivers/MicroBitIO.h index 788d215..c94e927 100644 --- a/inc/drivers/MicroBitIO.h +++ b/inc/drivers/MicroBitIO.h @@ -60,6 +60,8 @@ class MicroBitIO MicroBitPin P16; MicroBitPin P19; MicroBitPin P20; + MicroBitPin int1; + MicroBitPin int2; /** * Constructor. @@ -75,7 +77,7 @@ class MicroBitIO int ID_P9, int ID_P10,int ID_P11, int ID_P12,int ID_P13,int ID_P14, int ID_P15,int ID_P16,int ID_P19, - int ID_P20); + int ID_P20, int ID_INT1, int ID_INT2); }; #endif diff --git a/inc/types/CoordinateSystem.h b/inc/types/CoordinateSystem.h new file mode 100644 index 0000000..ebfca32 --- /dev/null +++ b/inc/types/CoordinateSystem.h @@ -0,0 +1,166 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef COORDINATE_SYSTEM_H +#define COORDINATE_SYSTEM_H +#include "MicroBitConfig.h" + +/** + * Co-ordinate systems that can be used. + * RAW: Unaltered data. Data will be returned directly from the accelerometer. + * + * SIMPLE_CARTESIAN: Data will be returned based on an easy to understand alignment, consistent with the cartesian system taught in schools. + * When held upright, facing the user: + * + * / + * +--------------------+ z + * | | + * | ..... | + * | * ..... * | + * ^ | ..... | + * | | | + * y +--------------------+ x--> + * + * + * NORTH_EAST_DOWN: Data will be returned based on the industry convention of the North East Down (NED) system. + * When held upright, facing the user: + * + * z + * +--------------------+ / + * | | + * | ..... | + * | * ..... * | + * ^ | ..... | + * | | | + * x +--------------------+ y--> + * + */ + +#define COORDINATE_SPACE_ROTATED_0 0 +#define COORDINATE_SPACE_ROTATED_90 1 +#define COORDINATE_SPACE_ROTATED_180 2 +#define COORDINATE_SPACE_ROTATED_270 3 + +enum CoordinateSystem +{ + RAW, + SIMPLE_CARTESIAN, + NORTH_EAST_DOWN, + EAST_NORTH_UP +}; + +struct Sample3D +{ + int x; + int y; + int z; + + Sample3D() + { + this->x = 0; + this->y = 0; + this->z = 0; + } + + Sample3D(int x, int y, int z) + { + this->x = x; + this->y = y; + this->z = z; + } + + Sample3D operator-(const Sample3D& other) const + { + Sample3D result; + + result.x = x - other.x; + result.y = y - other.y; + result.z = z - other.z; + + return result; + } + + Sample3D operator+(const Sample3D& other) const + { + Sample3D result; + + result.x = x + other.x; + result.y = y + other.y; + result.z = z + other.z; + + return result; + } + + bool operator==(const Sample3D& other) const + { + return x == other.x && y == other.y && z == other.z; + } + + bool operator!=(const Sample3D& other) const + { + return !(x == other.x && y == other.y && z == other.z); + } +}; + + +class CoordinateSpace +{ + public: + + CoordinateSystem system; + bool upsidedown; + int rotated; + + /** + * Constructor. + * + * Creates a new coordinatespace transformation object. + * + * @param system the CoordinateSystem to generated as output. + * @param upsidedown set if the sensor is mounted inverted (upside down) on the device board. + * @param rotated defines the rotation of the sensor on the PCB, with respect to pin 1 being at the top left corner + * when viewing the device from its "natural" (user defined) orientation. n.b. if the sensor is upside down, the rotation + * should be defined w.r.t. lookign at the side of the device where the sensor is mounted. + */ + CoordinateSpace(CoordinateSystem system, bool upsidedown = false, int rotated = COORDINATE_SPACE_ROTATED_0); + + /** + * Transforms a given 3D x,y,z tuple from ENU format into that format defined in this instance. + * + * @param a the sample point to convert, in ENU format. + * @return the equivalent location of 's' in the coordinate space specified in the constructor. + */ + Sample3D transform(Sample3D s); + + /** + * Transforms a given 3D x,y,z tuple from ENU format into that format defined in this instance. + * + * @param a the sample point to convert, in ENU format. + * @param system The coordinate system to use in the result. + * @return the equivalent location of 's' in the coordinate space specified in the constructor, and coordinate system supplied. + */ + Sample3D transform(Sample3D s, CoordinateSystem system); + +}; +#endif diff --git a/inc/types/MicroBitCoordinateSystem.h b/inc/types/MicroBitCoordinateSystem.h deleted file mode 100644 index 350355b..0000000 --- a/inc/types/MicroBitCoordinateSystem.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2016 British Broadcasting Corporation. -This software is provided by Lancaster University by arrangement with the BBC. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -#ifndef MICROBIT_COORDINATE_SYSTEM_H -#define MICROBIT_COORDINATE_SYSTEM_H -#include "MicroBitConfig.h" - -/** - * Co-ordinate systems that can be used. - * RAW: Unaltered data. Data will be returned directly from the accelerometer. - * - * SIMPLE_CARTESIAN: Data will be returned based on an easy to understand alignment, consistent with the cartesian system taught in schools. - * When held upright, facing the user: - * - * / - * +--------------------+ z - * | | - * | ..... | - * | * ..... * | - * ^ | ..... | - * | | | - * y +--------------------+ x--> - * - * - * NORTH_EAST_DOWN: Data will be returned based on the industry convention of the North East Down (NED) system. - * When held upright, facing the user: - * - * z - * +--------------------+ / - * | | - * | ..... | - * | * ..... * | - * ^ | ..... | - * | | | - * x +--------------------+ y--> - * - */ -enum MicroBitCoordinateSystem -{ - RAW, - SIMPLE_CARTESIAN, - NORTH_EAST_DOWN -}; - -#endif diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index da5e93e..3aecde3 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -14,7 +14,9 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "core/MicroBitHeapAllocator.cpp" "core/MicroBitListener.cpp" "core/MicroBitSystemTimer.cpp" + "core/MicroBitUtil.cpp" + "types/CoordinateSystem.cpp" "types/ManagedString.cpp" "types/Matrix4.cpp" "types/MicroBitEvent.cpp" @@ -45,6 +47,11 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "drivers/MicroBitFlash.cpp" "drivers/MicroBitFile.cpp" "drivers/MicroBitFileSystem.cpp" + "drivers/FXOS8700.cpp" + "drivers/MMA8653.cpp" + "drivers/MAG3110.cpp" + "drivers/LSM303Accelerometer.cpp" + "drivers/LSM303Magnetometer.cpp" "bluetooth/MicroBitAccelerometerService.cpp" "bluetooth/MicroBitBLEManager.cpp" diff --git a/source/asm/CortexContextSwitch.s b/source/asm/CortexContextSwitch.s deleted file mode 100644 index bd7deec..0000000 --- a/source/asm/CortexContextSwitch.s +++ /dev/null @@ -1,293 +0,0 @@ -; The MIT License (MIT) - -; Copyright (c) 2016 British Broadcasting Corporation. -; This software is provided by Lancaster University by arrangement with the BBC. - -; Permission is hereby granted, free of charge, to any person obtaining a -; copy of this software and associated documentation files (the "Software"), -; to deal in the Software without restriction, including without limitation -; the rights to use, copy, modify, merge, publish, distribute, sublicense, -; and/or sell copies of the Software, and to permit persons to whom the -; Software is furnished to do so, subject to the following conditions: - -; The above copyright notice and this permission notice shall be included in -; all copies or substantial portions of the Software. - -; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -; DEALINGS IN THE SOFTWARE. - - AREA asm_func, CODE, READONLY - -; Export our context switching subroutine as a C function for use in mbed - EXPORT swap_context - EXPORT save_context - EXPORT save_register_context - EXPORT restore_register_context - - ALIGN - -; R0 Contains a pointer to the TCB of the fibre being scheduled out. -; R1 Contains a pointer to the TCB of the fibre being scheduled in. -; R2 Contains a pointer to the base of the stack of the fibre being scheduled out. -; R3 Contains a pointer to the base of the stack of the fibre being scheduled in. - -swap_context - - ; Write our core registers into the TCB - ; First, store the general registers - - ; Skip this is we're given a NULL parameter for the TCB - CMP R0, #0 - BEQ store_context_complete - - STR R0, [R0,#0] - STR R1, [R0,#4] - STR R2, [R0,#8] - STR R3, [R0,#12] - STR R4, [R0,#16] - STR R5, [R0,#20] - STR R6, [R0,#24] - STR R7, [R0,#28] - - ; Now the high general purpose registers - MOV R4, R8 - STR R4, [R0,#32] - MOV R4, R9 - STR R4, [R0,#36] - MOV R4, R10 - STR R4, [R0,#40] - MOV R4, R11 - STR R4, [R0,#44] - MOV R4, R12 - STR R4, [R0,#48] - - ; Now the Stack and Link Register. - ; As this context is only intended for use with a fiber scheduler, - ; we don't need the PC. - MOV R6, SP - STR R6, [R0,#52] - MOV R4, LR - STR R4, [R0,#56] - -store_context_complete - ; Finally, Copy the stack. We do this to reduce RAM footprint, as stack is usually very small at the point - ; of scheduling, but we need a lot of capacity for interrupt handling and other functions. - - ; Skip this is we're given a NULL parameter for the stack. - CMP R2, #0 - BEQ store_stack_complete - - LDR R4, [R0,#60] ; Load R4 with the fiber's defined stack_base. -store_stack - SUBS R4, #4 - SUBS R2, #4 - - LDR R5, [R4] - STR R5, [R2] - - CMP R4, R6 - BNE store_stack - -store_stack_complete - - ; - ; Now page in the new context. - ; Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler. - ; - LDR R4, [R1, #56] - MOV LR, R4 - LDR R6, [R1, #52] - MOV SP, R6 - - ; Copy the stack in. - ; n.b. we do this after setting the SP to make comparisons easier. - - ; Skip this is we're given a NULL parameter for the stack. - CMP R3, #0 - BEQ restore_stack_complete - - LDR R4, [R1,#60] ; Load R4 with the fiber's defined stack_base. - -restore_stack - SUBS R4, #4 - SUBS R3, #4 - - LDR R5, [R3] - STR R5, [R4] - - CMP R4, R6 - BNE restore_stack - -restore_stack_complete - LDR R4, [R1, #48] - MOV R12, R4 - LDR R4, [R1, #44] - MOV R11, R4 - LDR R4, [R1, #40] - MOV R10, R4 - LDR R4, [R1, #36] - MOV R9, R4 - LDR R4, [R1, #32] - MOV R8, R4 - - LDR R7, [R1, #28] - LDR R6, [R1, #24] - LDR R5, [R1, #20] - LDR R4, [R1, #16] - LDR R3, [R1, #12] - LDR R2, [R1, #8] - LDR R0, [R1, #0] - LDR R1, [R1, #4] - - ; Return to caller (scheduler). - BX LR - - -; R0 Contains a pointer to the TCB of the fibre to snapshot -; R1 Contains a pointer to the base of the stack of the fibre being snapshotted - -save_context - - ; Write our core registers into the TCB - ; First, store the general registers - - STR R0, [R0,#0] - STR R1, [R0,#4] - STR R2, [R0,#8] - STR R3, [R0,#12] - STR R4, [R0,#16] - STR R5, [R0,#20] - STR R6, [R0,#24] - STR R7, [R0,#28] - - ; Now the high general purpose registers - MOV R4, R8 - STR R4, [R0,#32] - MOV R4, R9 - STR R4, [R0,#36] - MOV R4, R10 - STR R4, [R0,#40] - MOV R4, R11 - STR R4, [R0,#44] - MOV R4, R12 - STR R4, [R0,#48] - - ; Now the Stack and Link Register. - ; As this context is only intended for use with a fiber scheduler, - ; we don't need the PC. - MOV R6, SP - STR R6, [R0,#52] - MOV R4, LR - STR R4, [R0,#56] - - ; Finally, Copy the stack. We do this to reduce RAM footprint, as stackis usually very small at the point - ; of sceduling, but we need a lot of capacity for interrupt handling and other functions. - - LDR R4, [R0,#60] ; Load R4 with the fiber's defined stack_base. - -store_stack1 - SUBS R4, #4 - SUBS R1, #4 - - LDR R5, [R4] - STR R5, [R1] - - CMP R4, R6 - BNE store_stack1 - - ; Restore scratch registers. - - LDR R7, [R0, #28] - LDR R6, [R0, #24] - LDR R5, [R0, #20] - LDR R4, [R0, #16] - - ; Return to caller (scheduler). - BX LR - - -; R0 Contains a pointer to the TCB of the fiber to snapshot -save_register_context - - ; Write our core registers into the TCB - ; First, store the general registers - - STR R0, [R0,#0] - STR R1, [R0,#4] - STR R2, [R0,#8] - STR R3, [R0,#12] - STR R4, [R0,#16] - STR R5, [R0,#20] - STR R6, [R0,#24] - STR R7, [R0,#28] - - ; Now the high general purpose registers - MOV R4, R8 - STR R4, [R0,#32] - MOV R4, R9 - STR R4, [R0,#36] - MOV R4, R10 - STR R4, [R0,#40] - MOV R4, R11 - STR R4, [R0,#44] - MOV R4, R12 - STR R4, [R0,#48] - - ; Now the Stack Pointer and Link Register. - ; As this context is only intended for use with a fiber scheduler, - ; we don't need the PC. - MOV R4, SP - STR R4, [R0,#52] - MOV R4, LR - STR R4, [R0,#56] - - ; Restore scratch registers. - LDR R4, [R0, #16] - - ; Return to caller (scheduler). - BX LR - - -restore_register_context - - ; - ; Now page in the new context. - ; Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler. - ; - LDR R4, [R0, #56] - MOV LR, R4 - LDR R4, [R0, #52] - MOV SP, R4 - - ; High registers... - LDR R4, [R0, #48] - MOV R12, R4 - LDR R4, [R0, #44] - MOV R11, R4 - LDR R4, [R0, #40] - MOV R10, R4 - LDR R4, [R0, #36] - MOV R9, R4 - LDR R4, [R0, #32] - MOV R8, R4 - - ; Low registers... - LDR R7, [R0, #28] - LDR R6, [R0, #24] - LDR R5, [R0, #20] - LDR R4, [R0, #16] - LDR R3, [R0, #12] - LDR R2, [R0, #8] - LDR R0, [R0, #0] - LDR R1, [R0, #4] - - ; Return to caller (normally the scheduler). - BX LR - - ALIGN - END diff --git a/source/core/MicroBitUtil.cpp b/source/core/MicroBitUtil.cpp new file mode 100644 index 0000000..2353d44 --- /dev/null +++ b/source/core/MicroBitUtil.cpp @@ -0,0 +1,65 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * This file contains functions used to maintain compatability and portability. + * It also contains constants that are used elsewhere in the DAL. + */ +#include "MicroBitUtil.h" + +KeyValueTableEntry* KeyValueTable::find(const uint32_t key) const +{ + // Now find the nearest sample range to that specified. + KeyValueTableEntry *p = (KeyValueTableEntry *)data + (length - 1); + KeyValueTableEntry *result = p; + + while (p >= (KeyValueTableEntry *)data) + { + if (p->key < key) + break; + + result = p; + p--; + } + + return result; +} + + +uint32_t KeyValueTable::get(const uint32_t key) const +{ + return find(key)->value; +} + +uint32_t KeyValueTable::getKey(const uint32_t key) const +{ + return find(key)->key; +} + +bool KeyValueTable::hasKey(const uint32_t key) const +{ + return (find(key)->key == key); +} + + diff --git a/source/drivers/FXOS8700.cpp b/source/drivers/FXOS8700.cpp new file mode 100644 index 0000000..3d37ce8 --- /dev/null +++ b/source/drivers/FXOS8700.cpp @@ -0,0 +1,285 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * Class definition for an FXS8700 3 axis accelerometer. + * + * Represents an implementation of the Freescale FXS8700 3 axis accelerometer + * Also includes basic data caching and on demand activation. + */ +#include "FXOS8700.h" +#include "FXOS8700Accelerometer.h" +#include "FXOS8700Magnetometer.h" +#include "ErrorNo.h" +#include "MicroBitEvent.h" +#include "MicroBitCompat.h" +#include "MicroBitFiber.h" +#include "MicroBitAccelerometer.h" + +// +// Configuration table for available g force ranges. +// Maps g -> XYZ_DATA_CFG bit [0..1] +// +static const KeyValueTableEntry accelerometerRangeData[] = { + {2,0}, + {4,1}, + {8,2} +}; +CREATE_KEY_VALUE_TABLE(accelerometerRange, accelerometerRangeData); + +// +// Configuration table for available data update frequency. +// maps microsecond period -> CTRL_REG1 data rate selection bits [3..5] +// +static const KeyValueTableEntry accelerometerPeriodData[] = { + {2500,0x00}, + {5000,0x08}, + {10000,0x10}, + {20000,0x18}, + {80000,0x20}, + {160000,0x28}, + {320000,0x30}, + {1280000,0x38} +}; +CREATE_KEY_VALUE_TABLE(accelerometerPeriod, accelerometerPeriodData); + + +/** + * 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. + */ +int FXOS8700::configure() +{ + int result; + uint8_t value; + + // First find the nearest sample rate to that specified. + MicroBitAccelerometer::samplePeriod = accelerometerPeriod.getKey(MicroBitAccelerometer::samplePeriod * 2000) / 1000; + MicroBitAccelerometer::sampleRange = accelerometerRange.getKey(MicroBitAccelerometer::sampleRange); + MicroBitCompass::samplePeriod = MicroBitAccelerometer::samplePeriod; + + // Now configure the accelerometer accordingly. + // Firstly, disable the module (as some registers cannot be changed while its running). + value = 0x00; + result = i2c.writeRegister(address, FXOS8700_CTRL_REG1, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Enter hybrid mode (interleave accelerometer and magnetometer samples). + // Also, select full oversampling on the magnetometer + // TODO: Determine power / accuracy tradeoff here. + value = 0x1F; + result = i2c.writeRegister(address, FXOS8700_M_CTRL_REG1, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Select the auto incremement mode, which allows a contiguous I2C block + // read of both acceleromter and magnetometer data despite them being non-contguous + // in memory... funky! + value = 0x20; + result = i2c.writeRegister(address, FXOS8700_M_CTRL_REG2, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Configure PushPull Active LOW interrupt mode. + // n.b. This may need to be reconfigured if the interrupt line is shared. + value = 0x00; + result = i2c.writeRegister(address, FXOS8700_CTRL_REG3, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Enable a data ready interrupt. + // TODO: This is currently PUSHPULL mode. This may nede to be reconfigured + // to OPEN_DRAIN if the interrupt line is shared. + value = 0x01; + result = i2c.writeRegister(address, FXOS8700_CTRL_REG4, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Route the data ready interrupt to INT1 pin. + value = 0x01; + result = i2c.writeRegister(address, FXOS8700_CTRL_REG5, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Configure acceleromter g range. + value = accelerometerRange.get(MicroBitAccelerometer::sampleRange); + result = i2c.writeRegister(address, FXOS8700_XYZ_DATA_CFG, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Configure sample rate and re-enable the sensor. + value = accelerometerPeriod.get(MicroBitAccelerometer::samplePeriod * 1000) | 0x01; + result = i2c.writeRegister(address, FXOS8700_CTRL_REG1, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + return MICROBIT_OK; +} + +/** + * Constructor. + * Create a software abstraction of an FXSO8700 combined accelerometer/magnetometer + * + * @param _i2c an instance of I2C used to communicate with the device. + * + * @param address the default I2C address of the accelerometer. Defaults to: FXS8700_DEFAULT_ADDR. + * + */ +FXOS8700::FXOS8700(MicroBitI2C &_i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address, uint16_t aid, uint16_t cid) : + MicroBitAccelerometer(coordinateSpace, aid), + MicroBitCompass(coordinateSpace, cid), + i2c(_i2c), int1(_int1) +{ + // Store our identifiers. + this->address = address; + + // Configure and enable the accelerometer. + configure(); +} + +/** + * Attempts to read the 8 bit ID from the accelerometer, this can be used for + * validation purposes. + * + * @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails. + * + * @code + * accelerometer.whoAmI(); + * @endcode + */ +int FXOS8700::whoAmI() +{ + uint8_t data; + int result; + + result = i2c.readRegister(address, FXOS8700_WHO_AM_I, &data, 1); + if (result !=0) + return MICROBIT_I2C_ERROR; + + return (int)data; +} + + /** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int FXOS8700::requestUpdate() +{ + // Ensure we're scheduled to update the data periodically + if(!(MicroBitAccelerometer::status & MICROBIT_ACCEL_ADDED_TO_IDLE)) + { + fiber_add_idle_component((MicroBitAccelerometer *)this); + MicroBitAccelerometer::status |= MICROBIT_ACCEL_ADDED_TO_IDLE; + } + + // Poll interrupt line from device (ACTIVE LOW) + if(int1.getDigitalValue() == 0) + { + uint8_t data[12]; + int16_t s; + uint8_t *lsb = (uint8_t *) &s; + uint8_t *msb = lsb + 1; + Sample3D accelerometerSample; + Sample3D compassSample; + int result; + + // Read the combined accelerometer and magnetometer data. + result = i2c.readRegister(address, FXOS8700_OUT_X_MSB, data, 12); + + if (result !=0) + return MICROBIT_I2C_ERROR; + + + // read sensor data (and translate into signed little endian) + *msb = data[0]; + *lsb = data[1]; + accelerometerSample.x = s; + + *msb = data[2]; + *lsb = data[3]; + accelerometerSample.y = s; + + *msb = data[4]; + *lsb = data[5]; + accelerometerSample.z = s; + + *msb = data[6]; + *lsb = data[7]; + compassSample.x = s; + + *msb = data[8]; + *lsb = data[9]; + compassSample.y = s; + + *msb = data[10]; + *lsb = data[11]; + compassSample.z = s; + + // scale the 14 bit accelerometer data (packed into 16 bits) into SI units (milli-g), and translate to ENU coordinate system + MicroBitAccelerometer::sampleENU.x = (-accelerometerSample.y * MicroBitAccelerometer::sampleRange) / 32; + MicroBitAccelerometer::sampleENU.y = (accelerometerSample.x * MicroBitAccelerometer::sampleRange) / 32; + MicroBitAccelerometer::sampleENU.z = (accelerometerSample.z * MicroBitAccelerometer::sampleRange) / 32; + + // translate magnetometer data into ENU coordinate system + MicroBitCompass::sampleENU.x = -compassSample.y; + MicroBitCompass::sampleENU.y = compassSample.x; + MicroBitCompass::sampleENU.z = compassSample.z; + + MicroBitAccelerometer::update(); + MicroBitCompass::update(); + } + + return MICROBIT_OK; +} + + +/** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ +void FXOS8700::idleCallback() +{ + requestUpdate(); +} + +/** + * Destructor for FXS8700, where we deregister from the array of fiber components. + */ +FXOS8700::~FXOS8700() +{ +} + diff --git a/source/drivers/LSM303Accelerometer.cpp b/source/drivers/LSM303Accelerometer.cpp new file mode 100644 index 0000000..6890914 --- /dev/null +++ b/source/drivers/LSM303Accelerometer.cpp @@ -0,0 +1,193 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * Class definition for an LSM303 3 axis accelerometer. + * + * Represents an implementation of the LSM303 3 axis accelerometer + * Also includes basic data caching and on demand activation. + */ +#include "MicroBitConfig.h" +#include "LSM303Accelerometer.h" +#include "ErrorNo.h" +#include "MicroBitEvent.h" +#include "MicroBitCompat.h" +#include "MicroBitFiber.h" + +// +// Configuration table for available g force ranges. +// Maps g -> CTRL_REG4 full scale selection bits [4..5] +// +static const KeyValueTableEntry accelerometerRangeData[] = { + {2, 0}, + {4, 1}, + {8, 2}, + {16, 3} +}; +CREATE_KEY_VALUE_TABLE(accelerometerRange, accelerometerRangeData); + +// +// Configuration table for available data update frequency. +// maps microsecond period -> CTRL_REG1 data rate selection bits [4..7] +// +static const KeyValueTableEntry accelerometerPeriodData[] = { + {1235, 0x80}, + {1488, 0x90}, + {5000, 0x70}, + {10000, 0x60}, + {20000, 0x50}, + {40000, 0x40}, + {80000, 0x30}, + {200000, 0x20}, + {2000000, 0x10} +}; +CREATE_KEY_VALUE_TABLE(accelerometerPeriod, accelerometerPeriodData); + + +/** + * Constructor. + * Create a software abstraction of an accelerometer. + * + * @param coordinateSpace The orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id The unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER + * + */ +LSM303Accelerometer::LSM303Accelerometer(MicroBitI2C& _i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address, uint16_t id) : MicroBitAccelerometer(coordinateSpace, id), i2c(_i2c), int1(_int1) +{ + // Store our identifiers. + this->status = 0; + this->address = address; + + // Configure and enable the accelerometer. + //configure(); +} + +/** + * 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. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int LSM303Accelerometer::configure() +{ + int result; + + // First find the nearest sample rate to that specified. + samplePeriod = accelerometerPeriod.getKey(samplePeriod * 1000) / 1000; + sampleRange = accelerometerRange.getKey(sampleRange); + + // Now configure the accelerometer accordingly. + + // Place the device into normal (10 bit) mode, with all axes enabled at the nearest supported data rate to that requested. + result = i2c.writeRegister(address, LSM303_CTRL_REG1_A, samplePeriod | 0x07); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Enable the DRDY1 interrupt on INT1 pin. + result = i2c.writeRegister(address, LSM303_CTRL_REG3_A, 0x10); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Select the g range to that requested, using little endian data format and disable self-test and high rate functions. + result = i2c.writeRegister(address, LSM303_CTRL_REG3_A, sampleRange); + if (result != 0) + return MICROBIT_I2C_ERROR; + + return MICROBIT_OK; +} + +/** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int LSM303Accelerometer::requestUpdate() +{ +#ifdef POOP + // Ensure we're scheduled to update the data periodically + if(!(status & MICROBIT_ACCEL_ADDED_TO_IDLE)) + { + fiber_add_idle_component(this); + status |= MICROBIT_ACCEL_ADDED_TO_IDLE; + } + + // Poll interrupt line from device (ACTIVE LO) + if(!int1.getDigitalValue()) + { + int8_t data[6]; + int result; + Sample3D s; + + uint16_t *x = (uint16_t *) &data[0]; + uint16_t *y = (uint16_t *) &data[2]; + uint16_t *z = (uint16_t *) &data[4]; + + // Read the combined accelerometer and magnetometer data. + result = i2c.readRegister(address, LSM303_OUT_X_L_A, (uint8_t *)data, 6); + + if (result !=0) + return MICROBIT_I2C_ERROR; + + // Scale into millig (approx) and align to ENU coordinate system + sampleENU.x = -(int)(*y * sampleRange); + sampleENU.y = (int)(*x * sampleRange); + sampleENU.z = (int)(*z * sampleRange); + + // indicate that new data is available. + update(); + } + +#endif + return MICROBIT_OK; +} + +/** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ +void LSM303Accelerometer::idleCallback() +{ + requestUpdate(); +} + + +/** + * Destructor. + */ +LSM303Accelerometer::~LSM303Accelerometer() +{ +} + diff --git a/source/drivers/LSM303Magnetometer.cpp b/source/drivers/LSM303Magnetometer.cpp new file mode 100644 index 0000000..95d19fd --- /dev/null +++ b/source/drivers/LSM303Magnetometer.cpp @@ -0,0 +1,165 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * Class definition for a LSM303 3 axis magnetometer. + * + * Represents an implementation of the ST LSM303 3 axis magnetometer + */ +#include "LSM303Magnetometer.h" +#include "ErrorNo.h" +#include "MicroBitEvent.h" +#include "MicroBitCompat.h" +#include "MicroBitFiber.h" + +// +// Configuration table for available data update frequency. +// maps microsecond period -> LSM303_CFG_REG_A_M data rate selection bits [2..3] +// +static const KeyValueTableEntry magnetometerPeriodData[] = { + {100000, 0x0}, // 10 Hz + {50000, 0x4}, // 20 Hz + {20000, 0x8}, // 50 Hz + {10000, 0xC} // 100 Hz +}; +CREATE_KEY_VALUE_TABLE(magnetometerPeriod, magnetometerPeriodData); + + +/** + * Configures the compass for the 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 compass could not be configured. + */ +int LSM303Magnetometer::configure() +{ + int result; + + // First find the nearest sample rate to that specified. + samplePeriod = magnetometerPeriod.getKey(samplePeriod * 1000) / 1000; + + // Now configure the magnetometer accordingly. + // Enable automatic reset after each sample; + result = i2c.writeRegister(address, LSM303_CFG_REG_A_M, magnetometerPeriod.get(samplePeriod)); + if (result != MICROBIT_OK) + return MICROBIT_I2C_ERROR; + + // Enable Data Ready interrupt, with buffering of data to avoid race conditions. + result = i2c.writeRegister(address, LSM303_CFG_REG_C_M, 0x51); + if (result != MICROBIT_OK) + return MICROBIT_I2C_ERROR; + + return MICROBIT_OK; +} + +/** + * Constructor. + * Create a software abstraction of an FXSO8700 combined magnetometer/magnetometer + * + * @param _i2c an instance of I2C used to communicate with the device. + * + * @param address the default I2C address of the magnetometer. Defaults to: FXS8700_DEFAULT_ADDR. + * + */ +LSM303Magnetometer::LSM303Magnetometer(MicroBitI2C &_i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address, uint16_t id) : MicroBitCompass(coordinateSpace, id), i2c(_i2c), int1(_int1) +{ + // Store our identifiers. + this->address = address; + + // Configure and enable the magnetometer. + //configure(); +} + + +/** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int LSM303Magnetometer::requestUpdate() +{ +#ifdef POOP + // Ensure we're scheduled to update the data periodically + if(!(status & MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE)) + { + fiber_add_idle_component(this); + status |= MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE; + } + + // Poll interrupt line from device (ACTIVE LO) + if(!int1.getDigitalValue()) + { + int8_t data[6]; + int result; + Sample3D s; + + uint16_t *x = (uint16_t *) &data[0]; + uint16_t *y = (uint16_t *) &data[2]; + uint16_t *z = (uint16_t *) &data[4]; + + // Read the combined accelerometer and magnetometer data. + result = i2c.readRegister(address, LSM303_OUTX_L_REG_M, (uint8_t *)data, 6); + + if (result !=0) + return MICROBIT_I2C_ERROR; + + // Scale into millig (approx) and align to ENU coordinate system + sampleENU.x = -((int)(*y)); + sampleENU.y = (int)*x; + sampleENU.z = (int)*z; + + // indicate that new data is available. + update(); + } + +#endif + return MICROBIT_OK; +} + + +/** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ +void LSM303Magnetometer::idleCallback() +{ + requestUpdate(); +} + +/** + * Destructor for FXS8700, where we deregister from the array of fiber components. + */ +LSM303Magnetometer::~LSM303Magnetometer() +{ +} + diff --git a/source/drivers/MAG3110.cpp b/source/drivers/MAG3110.cpp new file mode 100644 index 0000000..292ebe1 --- /dev/null +++ b/source/drivers/MAG3110.cpp @@ -0,0 +1,199 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * Class definition for a MAG3110 3 axis magnetometer. + * + * Represents an implementation of the Freescale MAG3110 3 axis magnetometer + */ +#include "MAG3110.h" +#include "ErrorNo.h" +#include "MicroBitEvent.h" +#include "MicroBitCompat.h" +#include "MicroBitFiber.h" + +// +// Configuration table for available data update frequency. +// maps microsecond period -> CTRL_REG1 data rate selection bits [3..5] +// +static const KeyValueTableEntry magnetometerPeriodData[] = { + {12500, 0x00}, // 80 Hz + {25000, 0x20}, // 40 Hz + {50000, 0x40}, // 20 Hz + {100000, 0x60}, // 10 hz + {200000, 0x80}, // 5 hz + {400000, 0x88}, // 2.5 hz + {800000, 0x90}, // 1.25 hz + {1600000, 0xb0}, // 0.63 hz + {3200000, 0xd0}, // 0.31 hz + {6400000, 0xf0}, // 0.16 hz + {12800000, 0xf8} // 0.08 hz +}; +CREATE_KEY_VALUE_TABLE(magnetometerPeriod, magnetometerPeriodData); + + +/** + * Configures the compass for the 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 compass could not be configured. + */ +int MAG3110::configure() +{ + int result; + uint8_t value; + + // First, take the device offline, so it can be configured. + result = i2c.writeRegister(address, MAG_CTRL_REG1, 0x00); + if (result != MICROBIT_OK) + return MICROBIT_I2C_ERROR; + + // Wait for the part to enter standby mode... + while(1) + { + // Read the status of the part... + // If we can't communicate with it over I2C, pass on the error. + result = i2c.readRegister(address,MAG_SYSMOD, &value, 1); + if (result == MICROBIT_I2C_ERROR) + return MICROBIT_I2C_ERROR; + + // if the part in in standby, we're good to carry on. + if((value & 0x03) == 0) + break; + + // Perform a power efficient sleep... + fiber_sleep(100); + } + + // First find the nearest sample rate to that specified. + samplePeriod = magnetometerPeriod.getKey(samplePeriod * 1000) / 1000; + + // Now configure the magnetometer accordingly. + // Enable automatic reset after each sample; + result = i2c.writeRegister(address, MAG_CTRL_REG2, 0xA0); + if (result != MICROBIT_OK) + return MICROBIT_I2C_ERROR; + + + // Bring the device online, with the requested sample frequency. + result = i2c.writeRegister(address, MAG_CTRL_REG1, magnetometerPeriod.get(samplePeriod) | 0x01); + if (result != MICROBIT_OK) + return MICROBIT_I2C_ERROR; + + return MICROBIT_OK; +} + +/** + * Constructor. + * Create a software abstraction of an FXSO8700 combined magnetometer/magnetometer + * + * @param _i2c an instance of I2C used to communicate with the device. + * + * @param address the default I2C address of the magnetometer. Defaults to: FXS8700_DEFAULT_ADDR. + * + */ +MAG3110::MAG3110(MicroBitI2C &_i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address, uint16_t id) : MicroBitCompass(coordinateSpace, id), i2c(_i2c), int1(_int1) +{ + // Store our identifiers. + this->address = address; + + // Configure and enable the magnetometer. + configure(); +} + + +/** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int MAG3110::requestUpdate() +{ + // Ensure we're scheduled to update the data periodically + if(!(status & MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE)) + { + fiber_add_idle_component(this); + status |= MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE; + } + + // Poll interrupt line from device (ACTIVE HI) + if(int1.getDigitalValue()) + { + uint8_t data[6]; + int16_t s; + uint8_t *lsb = (uint8_t *) &s; + uint8_t *msb = lsb + 1; + int result; + + // Read the combined magnetometer and magnetometer data. + result = i2c.readRegister(address, MAG_OUT_X_MSB, data, 6); + + if (result !=0) + return MICROBIT_I2C_ERROR; + // Scale the 14 bit data (packed into 16 bits) into SI units (milli-g) and translate into signed little endian, and align to ENU coordinate system + *msb = data[0]; + *lsb = data[1]; + sampleENU.y = MAG3110_NORMALIZE_SAMPLE(s); + + *msb = data[2]; + *lsb = data[3]; + sampleENU.x = -MAG3110_NORMALIZE_SAMPLE(s); + + *msb = data[4]; + *lsb = data[5]; + sampleENU.z = -MAG3110_NORMALIZE_SAMPLE(s); + + // Inform the higher level driver that raw data has been updated. + update(); + } + + return MICROBIT_OK; +} + + +/** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ +void MAG3110::idleCallback() +{ + requestUpdate(); +} + +/** + * Destructor for FXS8700, where we deregister from the array of fiber components. + */ +MAG3110::~MAG3110() +{ +} + diff --git a/source/drivers/MMA8653.cpp b/source/drivers/MMA8653.cpp new file mode 100644 index 0000000..88af7fa --- /dev/null +++ b/source/drivers/MMA8653.cpp @@ -0,0 +1,215 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * Class definition for an MMA8653 3 axis accelerometer. + * + * Represents an implementation of the MMA8653 3 axis accelerometer + * Also includes basic data caching and on demand activation. + */ +#include "MicroBitConfig.h" +#include "MMA8653.h" +#include "ErrorNo.h" +#include "MicroBitEvent.h" +#include "MicroBitCompat.h" +#include "MicroBitFiber.h" + +// +// Configuration table for available g force ranges. +// Maps g -> XYZ_DATA_CFG bit [0..1] +// +static const KeyValueTableEntry accelerometerRangeData[] = { + {2, 0}, + {4, 1}, + {8, 2} +}; +CREATE_KEY_VALUE_TABLE(accelerometerRange, accelerometerRangeData); + +// +// Configuration table for available data update frequency. +// maps microsecond period -> CTRL_REG1 data rate selection bits [3..5] +// +static const KeyValueTableEntry accelerometerPeriodData[] = { + {1250, 0x00}, + {2500, 0x08}, + {5000, 0x10}, + {10000, 0x18}, + {20000, 0x20}, + {80000, 0x28}, + {160000, 0x30}, + {640000, 0x38} +}; +CREATE_KEY_VALUE_TABLE(accelerometerPeriod, accelerometerPeriodData); + + +/** + * Constructor. + * Create a software abstraction of an accelerometer. + * + * @param coordinateSpace The orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id The unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER + * + */ +MMA8653::MMA8653(MicroBitI2C& _i2c, MicroBitPin &_int1, CoordinateSpace &coordinateSpace, uint16_t address, uint16_t id) : MicroBitAccelerometer(coordinateSpace, id), i2c(_i2c), int1(_int1) +{ + // Store our identifiers. + this->status = 0; + this->address = address; + + // Configure and enable the accelerometer. + configure(); +} + +/** + * 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. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int MMA8653::configure() +{ + int result; + uint8_t value; + + // First find the nearest sample rate to that specified. + samplePeriod = accelerometerPeriod.getKey(samplePeriod * 1000) / 1000; + sampleRange = accelerometerRange.getKey(sampleRange); + + // Now configure the accelerometer accordingly. + + // First place the device into standby mode, so it can be configured. + result = i2c.writeRegister(address, MMA8653_CTRL_REG1, 0x00); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Enable high precisiosn mode. This consumes a bit more power, but still only 184 uA! + result = i2c.writeRegister(address, MMA8653_CTRL_REG2, 0x10); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Enable the INT1 interrupt pin. + result = i2c.writeRegister(address, MMA8653_CTRL_REG4, 0x01); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Select the DATA_READY event source to be routed to INT1 + result = i2c.writeRegister(address, MMA8653_CTRL_REG5, 0x01); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Configure for the selected g range. + value = accelerometerRange.get(sampleRange); + result = i2c.writeRegister(address, MMA8653_XYZ_DATA_CFG, value); + if (result != 0) + return MICROBIT_I2C_ERROR; + + // Bring the device back online, with 10bit wide samples at the requested frequency. + value = accelerometerPeriod.get(samplePeriod); + result = i2c.writeRegister(address, MMA8653_CTRL_REG1, value | 0x01); + if (result != 0) + return MICROBIT_I2C_ERROR; + + return MICROBIT_OK; +} + +/** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int MMA8653::requestUpdate() +{ + // Ensure we're scheduled to update the data periodically + if(!(status & MICROBIT_ACCEL_ADDED_TO_IDLE)) + { + fiber_add_idle_component(this); + status |= MICROBIT_ACCEL_ADDED_TO_IDLE; + } + + // Poll interrupt line from device (ACTIVE LO) + if(!int1.getDigitalValue()) + { + int8_t data[6]; + int result; + Sample3D s; + + // Read the combined accelerometer and magnetometer data. + result = i2c.readRegister(address, MMA8653_OUT_X_MSB, (uint8_t *)data, 6); + + if (result !=0) + return MICROBIT_I2C_ERROR; + + // read MSB values and normalize the data in the -1024...1024 range + s.x = 8 * data[0]; + s.y = 8 * data[2]; + s.z = 8 * data[4]; + +#if CONFIG_ENABLED(USE_ACCEL_LSB) + // Add in LSB values. + s.x += (data[1] / 64); + s.y += (data[3] / 64); + s.z += (data[5] / 64); +#endif + + // Scale into millig (approx) and align to ENU coordinate system + sampleENU.x = -s.y * sampleRange; + sampleENU.y = s.x * sampleRange; + sampleENU.z = s.z * sampleRange; + + // indicate that new data is available. + update(); + } + + return MICROBIT_OK; +} + +/** + * A periodic callback invoked by the fiber scheduler idle thread. + * + * Internally calls updateSample(). + */ +void MMA8653::idleCallback() +{ + requestUpdate(); +} + + +/** + * Destructor. + */ +MMA8653::~MMA8653() +{ +} + diff --git a/source/drivers/MicroBitAccelerometer.cpp b/source/drivers/MicroBitAccelerometer.cpp index 32a43ba..1406da2 100644 --- a/source/drivers/MicroBitAccelerometer.cpp +++ b/source/drivers/MicroBitAccelerometer.cpp @@ -1,8 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016 British Broadcasting Corporation. -This software is provided by Lancaster University by arrangement with the BBC. +Copyright (c) 2017 Lancaster University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,167 +22,29 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * 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 "MicroBitConfig.h" #include "MicroBitAccelerometer.h" #include "ErrorNo.h" -#include "MicroBitConfig.h" #include "MicroBitEvent.h" #include "MicroBitCompat.h" #include "MicroBitFiber.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. - * - * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the accelerometer could not be configured. - */ -int MicroBitAccelerometer::configure() -{ - const MMA8653SampleRangeConfig *actualSampleRange; - const MMA8653SampleRateConfig *actualSampleRate; - int result; - - // 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. - result = writeCommand(MMA8653_CTRL_REG1, 0x00); - if (result != 0) - return MICROBIT_I2C_ERROR; - - // Enable high precisiosn mode. This consumes a bit more power, but still only 184 uA! - result = writeCommand(MMA8653_CTRL_REG2, 0x10); - if (result != 0) - return MICROBIT_I2C_ERROR; - - // Enable the INT1 interrupt pin. - result = writeCommand(MMA8653_CTRL_REG4, 0x01); - if (result != 0) - return MICROBIT_I2C_ERROR; - - // Select the DATA_READY event source to be routed to INT1 - result = writeCommand(MMA8653_CTRL_REG5, 0x01); - if (result != 0) - return MICROBIT_I2C_ERROR; - - // Configure for the selected g range. - result = writeCommand(MMA8653_XYZ_DATA_CFG, actualSampleRange->xyz_data_cfg); - if (result != 0) - return MICROBIT_I2C_ERROR; - - // Bring the device back online, with 10bit wide samples at the requested frequency. - result = writeCommand(MMA8653_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01); - if (result != 0) - return MICROBIT_I2C_ERROR; - - return MICROBIT_OK; -} - -/** - * 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. - */ -int MicroBitAccelerometer::writeCommand(uint8_t reg, uint8_t value) -{ - uint8_t command[2]; - command[0] = reg; - command[1] = value; - - return i2c.write(address, (const char *)command, 2); -} - -/** - * Issues a read command, copying data 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. - */ -int MicroBitAccelerometer::readCommand(uint8_t reg, uint8_t* buffer, int length) -{ - int result; - - if (buffer == NULL || length <= 0 ) - return MICROBIT_INVALID_PARAMETER; - - result = i2c.write(address, (const char *)®, 1, true); - if (result !=0) - return MICROBIT_I2C_ERROR; - - result = i2c.read(address, (char *)buffer, length); - if (result !=0) - return MICROBIT_I2C_ERROR; - - return MICROBIT_OK; -} /** * Constructor. - * Create a software abstraction of an accelerometer. + * Create a software abstraction of an FXSO8700 combined accelerometer/magnetometer * - * @param _i2c an instance of MicroBitI2C used to communicate with the onboard accelerometer. + * @param _i2c an instance of I2C used to communicate with the device. * - * @param address the default I2C address of the accelerometer. Defaults to: MMA8653_DEFAULT_ADDR. + * @param address the default I2C address of the accelerometer. Defaults to: FXS8700_DEFAULT_ADDR. * - * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER - * - * @code - * MicroBitI2C i2c = MicroBitI2C(I2C_SDA0, I2C_SCL0); - * - * MicroBitAccelerometer accelerometer = MicroBitAccelerometer(i2c); - * @endcode */ -MicroBitAccelerometer::MicroBitAccelerometer(MicroBitI2C& _i2c, uint16_t address, uint16_t id) : sample(), int1(MICROBIT_PIN_ACCEL_DATA_READY), i2c(_i2c) +MicroBitAccelerometer::MicroBitAccelerometer(CoordinateSpace &cspace, uint16_t id) : sample(), sampleENU(), coordinateSpace(cspace) { // Store our identifiers. this->id = id; this->status = 0; - this->address = address; - // Update our internal state for 50Hz at +/- 2g (50Hz has a period af 20ms). + // Set a default rate of 50Hz and a +/-2g range. this->samplePeriod = 20; this->sampleRange = 2; @@ -200,97 +61,33 @@ MicroBitAccelerometer::MicroBitAccelerometer(MicroBitI2C& _i2c, uint16_t address this->shake.impulse_3 = 1; this->shake.impulse_6 = 1; this->shake.impulse_8 = 1; - - // Configure and enable the accelerometer. - if (this->configure() == MICROBIT_OK) - status |= MICROBIT_COMPONENT_RUNNING; } /** - * Attempts to read the 8 bit ID from the accelerometer, this can be used for - * validation purposes. - * - * @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails. - * - * @code - * accelerometer.whoAmI(); - * @endcode - */ -int MicroBitAccelerometer::whoAmI() -{ - uint8_t data; - int result; - - result = readCommand(MMA8653_WHOAMI, &data, 1); - if (result !=0) - return MICROBIT_I2C_ERROR; - - return (int)data; -} - -/** - * Reads the acceleration data from the accelerometer, and stores it in our buffer. - * This only happens if the accelerometer indicates that it has new data via int1. + * Stores data from the accelerometer sensor in our buffer, and perform gesture tracking. * * On first use, this member function will attempt to add this component to the * list of fiber components in order to constantly update the values stored * by this object. * - * This technique is called lazy instantiation, and it means that we do not + * This lazy instantiation means that we do not * obtain the overhead from non-chalantly adding this component to fiber components. * * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails. */ -int MicroBitAccelerometer::updateSample() +int MicroBitAccelerometer::update() { - if(!(status & MICROBIT_ACCEL_ADDED_TO_IDLE)) - { - fiber_add_idle_component(this); - status |= MICROBIT_ACCEL_ADDED_TO_IDLE; - } + // Store the new data, after performing any necessary coordinate transformations. + sample = coordinateSpace.transform(sampleENU); - // Poll interrupt line from accelerometer. - // n.b. Default is Active LO. Interrupt is cleared in data read. - if(!int1) - { - int8_t data[6]; - int result; + // Indicate that pitch and roll data is now stale, and needs to be recalculated if needed. + status &= ~MICROBIT_ACCELEROMETER_IMU_DATA_VALID; - result = readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6); - if (result !=0) - return MICROBIT_I2C_ERROR; + // Update gesture tracking + updateGesture(); - // read MSB values... - sample.x = data[0]; - sample.y = data[2]; - sample.z = data[4]; - - // Normalize the data in the 0..1024 range. - sample.x *= 8; - sample.y *= 8; - sample.z *= 8; - -#if CONFIG_ENABLED(USE_ACCEL_LSB) - // Add in LSB values. - sample.x += (data[1] / 64); - sample.y += (data[3] / 64); - sample.z += (data[5] / 64); -#endif - - // Scale into millig (approx!) - sample.x *= this->sampleRange; - sample.y *= this->sampleRange; - sample.z *= this->sampleRange; - - // Indicate that pitch and roll data is now stale, and needs to be recalculated if needed. - status &= ~MICROBIT_ACCEL_PITCH_ROLL_VALID; - - // Update gesture tracking - updateGesture(); - - // Indicate that a new sample is available - MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE); - } + // Indicate that a new sample is available + MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE); return MICROBIT_OK; }; @@ -304,12 +101,12 @@ int MicroBitAccelerometer::updateSample() * * @return the sum of the square of the acceleration of the device across all axes. */ -int MicroBitAccelerometer::instantaneousAccelerationSquared() +uint32_t MicroBitAccelerometer::instantaneousAccelerationSquared() { - updateSample(); + requestUpdate(); // 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; + return (uint32_t)sample.x*(uint32_t)sample.x + (uint32_t)sample.y*(uint32_t)sample.y + (uint32_t)sample.z*(uint32_t)sample.z; } /** @@ -330,19 +127,19 @@ uint16_t MicroBitAccelerometer::instantaneousPosture() // // If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device // has been shaken. - if ((getX() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (getX() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x)) + if ((sample.x < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (sample.x > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x)) { shakeDetected = true; shake.x = !shake.x; } - if ((getY() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (getY() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y)) + if ((sample.y < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (sample.y > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y)) { shakeDetected = true; shake.y = !shake.y; } - if ((getZ() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (getZ() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z)) + if ((sample.z < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (sample.z > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z)) { shakeDetected = true; shake.z = !shake.z; @@ -352,7 +149,7 @@ uint16_t MicroBitAccelerometer::instantaneousPosture() if (shakeDetected && shake.count < MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD) { shake.count++; - + if (shake.count == 1) shake.timer = 0; @@ -386,26 +183,27 @@ uint16_t MicroBitAccelerometer::instantaneousPosture() } } - if (instantaneousAccelerationSquared() < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD) + uint32_t force = instantaneousAccelerationSquared(); + if (force < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD) return MICROBIT_ACCELEROMETER_EVT_FREEFALL; // Determine our posture. - if (getX() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) + if (sample.x < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_LEFT; - if (getX() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) + if (sample.x > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT; - if (getY() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) + if (sample.y < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_DOWN; - if (getY() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) + if (sample.y > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_UP; - if (getZ() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) + if (sample.z < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_FACE_UP; - if (getZ() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) + if (sample.z > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_FACE_DOWN; return MICROBIT_ACCELEROMETER_EVT_NONE; @@ -420,7 +218,7 @@ void MicroBitAccelerometer::updateGesture() // Check for High/Low G force events - typically impulses, impacts etc. // Again, during such spikes, these event take priority of the posture of the device. // For these events, we don't perform any low pass filtering. - int force = instantaneousAccelerationSquared(); + uint32_t force = instantaneousAccelerationSquared(); if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD) { @@ -496,8 +294,14 @@ void MicroBitAccelerometer::updateGesture() */ int MicroBitAccelerometer::setPeriod(int period) { - this->samplePeriod = period; - return this->configure(); + int result; + + samplePeriod = period; + result = configure(); + + samplePeriod = getPeriod(); + return result; + } /** @@ -527,8 +331,13 @@ int MicroBitAccelerometer::getPeriod() */ int MicroBitAccelerometer::setRange(int range) { - this->sampleRange = range; - return this->configure(); + int result; + + sampleRange = range; + result = configure(); + + sampleRange = getRange(); + return result; } /** @@ -542,84 +351,93 @@ int MicroBitAccelerometer::getRange() } /** - * Reads the value of the X axis from the latest update retrieved from the accelerometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The force measured in the X axis, in milli-g. - * - * @code - * accelerometer.getX(); - * @endcode - */ -int MicroBitAccelerometer::getX(MicroBitCoordinateSystem system) + * 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. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int MicroBitAccelerometer::configure() { - updateSample(); - - switch (system) - { - case SIMPLE_CARTESIAN: - return -sample.x; - - case NORTH_EAST_DOWN: - return sample.y; - - case RAW: - default: - return sample.x; - } + return MICROBIT_NOT_SUPPORTED; } /** - * Reads the value of the Y axis from the latest update retrieved from the accelerometer. - * - * @return The force measured in the Y axis, in milli-g. - * - * @code - * accelerometer.getY(); - * @endcode - */ -int MicroBitAccelerometer::getY(MicroBitCoordinateSystem system) + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int MicroBitAccelerometer::requestUpdate() { - updateSample(); - - switch (system) - { - case SIMPLE_CARTESIAN: - return -sample.y; - - case NORTH_EAST_DOWN: - return -sample.x; - - case RAW: - default: - return sample.y; - } + return MICROBIT_NOT_SUPPORTED; } /** - * Reads the value of the Z axis from the latest update retrieved from the accelerometer. - * - * @return The force measured in the Z axis, in milli-g. - * - * @code - * accelerometer.getZ(); - * @endcode - */ -int MicroBitAccelerometer::getZ(MicroBitCoordinateSystem system) + * Reads the last accelerometer value stored, and provides it in the coordinate system requested. + * + * @param coordinateSpace The coordinate system to use. + * @return The force measured in each axis, in milli-g. + */ +Sample3D MicroBitAccelerometer::getSample(CoordinateSystem coordinateSystem) { - updateSample(); + requestUpdate(); + return coordinateSpace.transform(sampleENU, coordinateSystem); +} - switch (system) - { - case NORTH_EAST_DOWN: - return -sample.z; +/** + * Reads the last accelerometer value stored, and in the coordinate system defined in the constructor. + * @return The force measured in each axis, in milli-g. + */ +Sample3D MicroBitAccelerometer::getSample() +{ + requestUpdate(); + return sample; +} - case SIMPLE_CARTESIAN: - case RAW: - default: - return sample.z; - } +/** + * reads the value of the x axis from the latest update retrieved from the accelerometer, + * usingthe default coordinate system as specified in the constructor. + * + * @return the force measured in the x axis, in milli-g. + */ +int MicroBitAccelerometer::getX() +{ + requestUpdate(); + return sample.x; +} + +/** + * reads the value of the y axis from the latest update retrieved from the accelerometer, + * usingthe default coordinate system as specified in the constructor. + * + * @return the force measured in the y axis, in milli-g. + */ +int MicroBitAccelerometer::getY() +{ + requestUpdate(); + return sample.y; +} + +/** + * reads the value of the z axis from the latest update retrieved from the accelerometer, + * usingthe default coordinate system as specified in the constructor. + * + * @return the force measured in the z axis, in milli-g. + */ +int MicroBitAccelerometer::getZ() +{ + requestUpdate(); + return sample.z; } /** @@ -647,7 +465,8 @@ int MicroBitAccelerometer::getPitch() */ float MicroBitAccelerometer::getPitchRadians() { - if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID)) + requestUpdate(); + if (!(status & MICROBIT_ACCELEROMETER_IMU_DATA_VALID)) recalculatePitchRoll(); return pitch; @@ -678,7 +497,8 @@ int MicroBitAccelerometer::getRoll() */ float MicroBitAccelerometer::getRollRadians() { - if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID)) + requestUpdate(); + if (!(status & MICROBIT_ACCELEROMETER_IMU_DATA_VALID)) recalculatePitchRoll(); return roll; @@ -692,14 +512,14 @@ float MicroBitAccelerometer::getRollRadians() */ void MicroBitAccelerometer::recalculatePitchRoll() { - double x = (double) getX(NORTH_EAST_DOWN); - double y = (double) getY(NORTH_EAST_DOWN); - double z = (double) getZ(NORTH_EAST_DOWN); + double x = (double) sample.x; + double y = (double) sample.y; + double z = (double) sample.z; roll = atan2(y, z); pitch = atan(-x / (y*sin(roll) + z*cos(roll))); - status |= MICROBIT_ACCEL_PITCH_ROLL_VALID; + status |= MICROBIT_ACCELEROMETER_IMU_DATA_VALID; } /** @@ -709,7 +529,6 @@ void MicroBitAccelerometer::recalculatePitchRoll() * * Example: * @code - * MicroBitDisplay display; * * if (accelerometer.getGesture() == SHAKE) * display.scroll("SHAKE!"); @@ -721,36 +540,9 @@ uint16_t MicroBitAccelerometer::getGesture() } /** - * A periodic callback invoked by the fiber scheduler idle thread. - * - * Internally calls updateSample(). - */ -void MicroBitAccelerometer::idleTick() -{ - updateSample(); -} - -/** - * Destructor for MicroBitAccelerometer, where we deregister from the array of fiber components. + * Destructor for FXS8700, where we deregister from the array of fiber components. */ MicroBitAccelerometer::~MicroBitAccelerometer() { - fiber_remove_idle_component(this); } -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} -}; diff --git a/source/drivers/MicroBitCompass.cpp b/source/drivers/MicroBitCompass.cpp index 95655d3..72d7b69 100644 --- a/source/drivers/MicroBitCompass.cpp +++ b/source/drivers/MicroBitCompass.cpp @@ -1,8 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016 British Broadcasting Corporation. -This software is provided by Lancaster University by arrangement with the BBC. +Copyright (c) 2017 Lancaster University. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,340 +22,81 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Class definition for MicroBit Compass. - * - * Represents an implementation of the Freescale MAG3110 I2C Magnetmometer. - * Also includes basic caching, calibration and on demand activation. - */ -#include "MicroBitConfig.h" #include "MicroBitCompass.h" -#include "MicroBitFiber.h" #include "ErrorNo.h" +#include "MicroBitEvent.h" +#include "MicroBitCompat.h" +#include "MicroBitFiber.h" + /** - * An initialisation member function used by the many constructors of MicroBitCompass. - * - * @param id the unique identifier for this compass instance. - * - * @param address the base address of the magnetometer on the i2c bus. - */ -void MicroBitCompass::init(uint16_t id, uint16_t address) + * Constructor. + * Create a software abstraction of an e-compass. + * + * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_COMPASS + * @param coordinateSpace the orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * + */ +MicroBitCompass::MicroBitCompass(CoordinateSpace &cspace, uint16_t id) : sample(), sampleENU(), coordinateSpace(cspace) { - this->id = id; - this->address = address; + accelerometer = NULL; + init(id); +} - // Select 10Hz update rate, with oversampling, and enable the device. - this->samplePeriod = 100; +/** + * Constructor. + * Create a software abstraction of an e-compass. + * + * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_COMPASS + * @param accel the accelerometer to use for tilt compensation + * @param coordinateSpace the orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * + */ +MicroBitCompass::MicroBitCompass(MicroBitAccelerometer &accel, CoordinateSpace &cspace, uint16_t id) : sample(), sampleENU(), coordinateSpace(cspace) +{ + accelerometer = &accel; + init(id); +} + +/** + * Internal helper used to de-duplicate code in the constructors + * @param coordinateSpace the orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_COMPASS + * + */ +void MicroBitCompass::init(uint16_t id) +{ + // Store our identifiers. + this->id = id; + this->status = 0; + + // Set a default rate of 50Hz. + this->samplePeriod = 20; this->configure(); // Assume that we have no calibration information. status &= ~MICROBIT_COMPASS_STATUS_CALIBRATED; - if(this->storage != NULL) - { - KeyValuePair *calibrationData = storage->get("compassCal"); - - if(calibrationData != NULL) - { - CompassSample storedSample = CompassSample(); - - memcpy(&storedSample, calibrationData->value, sizeof(CompassSample)); - - setCalibration(storedSample); - - delete calibrationData; - } - } - // Indicate that we're up and running. status |= MICROBIT_COMPONENT_RUNNING; } -/** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param _accelerometer an instance of the accelerometer, used for tilt compensation. - * - * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitAccelerometer accelerometer(i2c); - * - * MicroBitStorage storage; - * - * MicroBitCompass compass(i2c, accelerometer, storage); - * @endcode - */ -MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, MicroBitStorage& _storage, uint16_t address, uint16_t id) : - average(), - sample(), - int1(MICROBIT_PIN_COMPASS_DATA_READY), - i2c(_i2c), - accelerometer(&_accelerometer), - storage(&_storage) -{ - init(id, address); -} /** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param _accelerometer an instance of the accelerometer, used for tilt compensation. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitAccelerometer accelerometer(i2c); - * - * MicroBitCompass compass(i2c, accelerometer, storage); - * @endcode - */ -MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, uint16_t address, uint16_t id) : - average(), - sample(), - int1(MICROBIT_PIN_COMPASS_DATA_READY), - i2c(_i2c), - accelerometer(&_accelerometer), - storage(NULL) -{ - init(id, address); -} - -/** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitStorage storage; - * - * MicroBitCompass compass(i2c, storage); - * @endcode - */ -MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, MicroBitStorage& _storage, uint16_t address, uint16_t id) : - average(), - sample(), - int1(MICROBIT_PIN_COMPASS_DATA_READY), - i2c(_i2c), - accelerometer(NULL), - storage(&_storage) -{ - init(id, address); -} - -/** - * Constructor. - * Create a software representation of an e-compass. - * - * @param _i2c an instance of i2c, which the compass is accessible from. - * - * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR. - * - * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR. - * - * @code - * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); - * - * MicroBitCompass compass(i2c); - * @endcode - */ -MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, uint16_t address, uint16_t id) : - average(), - sample(), - int1(MICROBIT_PIN_COMPASS_DATA_READY), - i2c(_i2c), - accelerometer(NULL), - storage(NULL) -{ - init(id, address); -} - -/** - * 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. - */ -int MicroBitCompass::writeCommand(uint8_t reg, uint8_t value) -{ - uint8_t command[2]; - command[0] = reg; - command[1] = value; - - return i2c.write(address, (const char *)command, 2); -} - -/** - * Issues a read command, copying data 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. - */ -int MicroBitCompass::readCommand(uint8_t reg, uint8_t* buffer, int length) -{ - int result; - - if (buffer == NULL || length <= 0) - return MICROBIT_INVALID_PARAMETER; - - result = i2c.write(address, (const char *)®, 1, true); - if (result !=0) - return MICROBIT_I2C_ERROR; - - result = i2c.read(address, (char *)buffer, length); - if (result !=0) - return MICROBIT_I2C_ERROR; - - return MICROBIT_OK; -} - - -/** - * Issues a read of a given address, and returns the value. - * - * Blocks the calling thread until complete. - * - * @param reg The address of the 16 bit register to access. - * - * @return The register value, interpreted as a 16 but signed value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed. - */ -int MicroBitCompass::read16(uint8_t reg) -{ - uint8_t cmd[2]; - int result; - - cmd[0] = reg; - result = i2c.write(address, (const char *)cmd, 1); - if (result !=0) - return MICROBIT_I2C_ERROR; - - cmd[0] = 0x00; - cmd[1] = 0x00; - - result = i2c.read(address, (char *)cmd, 2); - if (result !=0) - return MICROBIT_I2C_ERROR; - - return (int16_t) ((cmd[1] | (cmd[0] << 8))); //concatenate the MSB and LSB -} - -/** - * Issues a read of a given address, and returns the value. - * - * Blocks the calling thread until complete. - * - * @param reg The address of the 16 bit register to access. - * - * @return The register value, interpreted as a 8 bit unsigned value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed. - */ -int MicroBitCompass::read8(uint8_t reg) -{ - uint8_t data; - int result; - - data = 0; - result = readCommand(reg, (uint8_t*) &data, 1); - if (result != MICROBIT_OK) - return MICROBIT_I2C_ERROR; - - return data; -} - -/** - * Calculates a tilt compensated bearing of the device, using the accelerometer. - */ -int MicroBitCompass::tiltCompensatedBearing() -{ - // Precompute the tilt compensation parameters to improve readability. - float phi = accelerometer->getRollRadians(); - float theta = accelerometer->getPitchRadians(); - - float x = (float) getX(NORTH_EAST_DOWN); - float y = (float) getY(NORTH_EAST_DOWN); - float z = (float) getZ(NORTH_EAST_DOWN); - - // Precompute cos and sin of pitch and roll angles to make the calculation a little more efficient. - float sinPhi = sin(phi); - float cosPhi = cos(phi); - float sinTheta = sin(theta); - float cosTheta = cos(theta); - - float bearing = (360*atan2(z*sinPhi - y*cosPhi, x*cosTheta + y*sinTheta*sinPhi + z*sinTheta*cosPhi)) / (2*PI); - - if (bearing < 0) - bearing += 360.0; - - return (int) bearing; -} - -/** - * Calculates a non-tilt compensated bearing of the device. - */ -int MicroBitCompass::basicBearing() -{ - updateSample(); - - float bearing = (atan2((double)(sample.y - average.y),(double)(sample.x - average.x)))*180/PI; - - if (bearing < 0) - bearing += 360.0; - - return (int)(360.0 - bearing); -} - -/** - * Gets the current heading of the device, relative to magnetic north. - * - * If the compass is not calibrated, it will raise the MICROBIT_COMPASS_EVT_CALIBRATE event. - * - * Users wishing to implement their own calibration algorithms should listen for this event, - * using MESSAGE_BUS_LISTENER_IMMEDIATE model. This ensures that calibration is complete before - * the user program continues. - * - * @return the current heading, in degrees. Or MICROBIT_CALIBRATION_IN_PROGRESS if the compass is calibrating. - * - * @code - * compass.heading(); - * @endcode - */ + * Gets the current heading of the device, relative to magnetic north. + * + * If the compass is not calibrated, it will raise the MICROBIT_COMPASS_EVT_CALIBRATE event. + * + * Users wishing to implement their own calibration algorithms should listen for this event, + * using MESSAGE_BUS_LISTENER_IMMEDIATE model. This ensures that calibration is complete before + * the user program continues. + * + * @return the current heading, in degrees. Or CALIBRATION_IN_PROGRESS if the compass is calibrating. + * + * @code + * compass.heading(); + * @endcode + */ int MicroBitCompass::heading() { if(status & MICROBIT_COMPASS_STATUS_CALIBRATING) @@ -372,304 +112,45 @@ int MicroBitCompass::heading() } /** - * Updates the local sample, only if the compass indicates that - * data is stale. - * - * @note Can be used to trigger manual updates, if the device is running without a scheduler. - * Also called internally by all get[X,Y,Z]() member functions. - */ -int MicroBitCompass::updateSample() -{ - /** - * Adds the compass to idle, if it hasn't been added already. - * This is an optimisation so that the compass is only added on first 'use'. - */ - if(!(status & MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE)) - { - fiber_add_idle_component(this); - status |= MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE; - } - - // Poll interrupt line from compass (Active HI). - // Interrupt is cleared on data read of MAG_OUT_X_MSB. - if(int1) - { - sample.x = MAG3110_NORMALIZE_SAMPLE((int) read16(MAG_OUT_X_MSB)); - sample.y = MAG3110_NORMALIZE_SAMPLE((int) read16(MAG_OUT_Y_MSB)); - sample.z = MAG3110_NORMALIZE_SAMPLE((int) read16(MAG_OUT_Z_MSB)); - - // Indicate that a new sample is available - MicroBitEvent e(id, MICROBIT_COMPASS_EVT_DATA_UPDATE); - } - - return MICROBIT_OK; -} - -/** - * Periodic callback from MicroBit idle thread. - * - * Calls updateSample(). - */ -void MicroBitCompass::idleTick() -{ - updateSample(); -} - -/** - * Reads the value of the X axis from the latest update retrieved from the magnetometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The magnetic force measured in the X axis, in nano teslas. - * - * @code - * compass.getX(); - * @endcode - */ -int MicroBitCompass::getX(MicroBitCoordinateSystem system) -{ - updateSample(); - - switch (system) - { - case SIMPLE_CARTESIAN: - return sample.x - average.x; - - case NORTH_EAST_DOWN: - return -(sample.y - average.y); - - case RAW: - default: - return sample.x; - } -} - -/** - * Reads the value of the Y axis from the latest update retrieved from the magnetometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The magnetic force measured in the Y axis, in nano teslas. - * - * @code - * compass.getY(); - * @endcode - */ -int MicroBitCompass::getY(MicroBitCoordinateSystem system) -{ - updateSample(); - - switch (system) - { - case SIMPLE_CARTESIAN: - return -(sample.y - average.y); - - case NORTH_EAST_DOWN: - return (sample.x - average.x); - - case RAW: - default: - return sample.y; - } -} - -/** - * Reads the value of the Z axis from the latest update retrieved from the magnetometer. - * - * @param system The coordinate system to use. By default, a simple cartesian system is provided. - * - * @return The magnetic force measured in the Z axis, in nano teslas. - * - * @code - * compass.getZ(); - * @endcode - */ -int MicroBitCompass::getZ(MicroBitCoordinateSystem system) -{ - updateSample(); - - switch (system) - { - case SIMPLE_CARTESIAN: - case NORTH_EAST_DOWN: - return -(sample.z - average.z); - - case RAW: - default: - return sample.z; - } -} - -/** - * Determines the overall magnetic field strength based on the latest update from the magnetometer. - * - * @return The magnetic force measured across all axis, in nano teslas. - * - * @code - * compass.getFieldStrength(); - * @endcode - */ + * Determines the overall magnetic field strength based on the latest update from the magnetometer. + * + * @return The magnetic force measured across all axis, in nano teslas. + * + * @code + * compass.getFieldStrength(); + * @endcode + */ int MicroBitCompass::getFieldStrength() { - double x = getX(); - double y = getY(); - double z = getZ(); + Sample3D s = getSample(); + + double x = s.x; + double y = s.y; + double z = s.z; return (int) sqrt(x*x + y*y + z*z); } /** - * Configures the compass for the 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 or MICROBIT_I2C_ERROR if the magnetometer could not be configured. - */ -int MicroBitCompass::configure() -{ - const MAG3110SampleRateConfig *actualSampleRate; - int result; - - // First, take the device offline, so it can be configured. - result = writeCommand(MAG_CTRL_REG1, 0x00); - if (result != MICROBIT_OK) - return MICROBIT_I2C_ERROR; - - // Wait for the part to enter standby mode... - while(1) - { - // Read the status of the part... - // If we can't communicate with it over I2C, pass on the error. - result = this->read8(MAG_SYSMOD); - if (result == MICROBIT_I2C_ERROR) - return MICROBIT_I2C_ERROR; - - // if the part in in standby, we're good to carry on. - if((result & 0x03) == 0) - break; - - // Perform a power efficient sleep... - fiber_sleep(100); - } - - // Find the nearest sample rate to that specified. - actualSampleRate = &MAG3110SampleRate[MAG3110_SAMPLE_RATES-1]; - for (int i=MAG3110_SAMPLE_RATES-1; i>=0; i--) - { - if(MAG3110SampleRate[i].sample_period < this->samplePeriod * 1000) - break; - - actualSampleRate = &MAG3110SampleRate[i]; - } - - // OK, we have the correct data. Update our local state. - this->samplePeriod = actualSampleRate->sample_period / 1000; - - // Enable automatic reset after each sample; - result = writeCommand(MAG_CTRL_REG2, 0xA0); - if (result != MICROBIT_OK) - return MICROBIT_I2C_ERROR; - - - // Bring the device online, with the requested sample frequency. - result = writeCommand(MAG_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01); - if (result != MICROBIT_OK) - return MICROBIT_I2C_ERROR; - - return MICROBIT_OK; -} - -/** - * Attempts to set the sample rate of the compass to the specified value (in ms). - * - * @param period the requested time between samples, in milliseconds. - * - * @return MICROBIT_OK or MICROBIT_I2C_ERROR if the magnetometer could not be updated. - * - * @code - * // sample rate is now 20 ms. - * compass.setPeriod(20); - * @endcode - * - * @note The requested rate may not be possible on the hardware. In this case, the - * nearest lower rate is chosen. - */ -int MicroBitCompass::setPeriod(int period) -{ - this->samplePeriod = period; - return this->configure(); -} - -/** - * Reads the currently configured sample rate of the compass. - * - * @return The time between samples, in milliseconds. - */ -int MicroBitCompass::getPeriod() -{ - return (int)samplePeriod; -} - -/** - * Attempts to read the 8 bit ID from the magnetometer, this can be used for - * validation purposes. - * - * @return the 8 bit ID returned by the magnetometer, or MICROBIT_I2C_ERROR if the request fails. - * - * @code - * compass.whoAmI(); - * @endcode - */ -int MicroBitCompass::whoAmI() -{ - uint8_t data; - int result; - - result = readCommand(MAG_WHOAMI, &data, 1); - if (result != MICROBIT_OK) - return MICROBIT_I2C_ERROR; - - return (int)data; -} - -/** - * Reads the current die temperature of the compass. - * - * @return the temperature in degrees celsius, or MICROBIT_I2C_ERROR if the temperature reading could not be retreived - * from the accelerometer. - */ -int MicroBitCompass::readTemperature() -{ - int8_t temperature; - int result; - - result = readCommand(MAG_DIE_TEMP, (uint8_t *)&temperature, 1); - if (result != MICROBIT_OK) - return MICROBIT_I2C_ERROR; - - return temperature; -} - -/** - * Perform a calibration of the compass. - * - * This method will be called automatically if a user attempts to read a compass value when - * the compass is uncalibrated. It can also be called at any time by the user. - * - * The method will only return once the compass has been calibrated. - * - * @return MICROBIT_OK, MICROBIT_I2C_ERROR if the magnetometer could not be accessed, - * or MICROBIT_CALIBRATION_REQUIRED if the calibration algorithm failed to complete successfully. - * - * @note THIS MUST BE CALLED TO GAIN RELIABLE VALUES FROM THE COMPASS - */ + * Perform a calibration of the compass. + * + * This method will be called automatically if a user attempts to read a compass value when + * the compass is uncalibrated. It can also be called at any time by the user. + * + * The method will only return once the compass has been calibrated. + * + * @return MICROBIT_OK, MICROBIT_I2C_ERROR if the magnetometer could not be accessed, + * or MICROBIT_CALIBRATION_REQUIRED if the calibration algorithm failed to complete successfully. + * + * @note THIS MUST BE CALLED TO GAIN RELIABLE VALUES FROM THE COMPASS + */ int MicroBitCompass::calibrate() { // Only perform one calibration process at a time. if(isCalibrating()) return MICROBIT_CALIBRATION_IN_PROGRESS; - updateSample(); + requestUpdate(); // Delete old calibration data clearCalibration(); @@ -691,78 +172,248 @@ int MicroBitCompass::calibrate() } /** - * Configure the compass to use the calibration data that is supplied to this call. - * - * Calibration data is comprised of the perceived zero offset of each axis of the compass. - * - * After calibration this should now take into account trimming errors in the magnetometer, - * and any "hard iron" offsets on the device. - * - * @param calibration A CompassSample containing the offsets for the x, y and z axis. - */ -void MicroBitCompass::setCalibration(CompassSample calibration) + * Configure the compass to use the calibration data that is supplied to this call. + * + * Calibration data is comprised of the perceived zero offset of each axis of the compass. + * + * After calibration this should now take into account trimming errors in the magnetometer, + * and any "hard iron" offsets on the device. + * + * @param calibration A Sample3D containing the offsets for the x, y and z axis. + */ +void MicroBitCompass::setCalibration(Sample3D calibration) { - if(this->storage != NULL) - this->storage->put(ManagedString("compassCal"), (uint8_t *)&calibration, sizeof(CompassSample)); - average = calibration; status |= MICROBIT_COMPASS_STATUS_CALIBRATED; } /** - * Provides the calibration data currently in use by the compass. - * - * More specifically, the x, y and z zero offsets of the compass. - * - * @return calibration A CompassSample containing the offsets for the x, y and z axis. - */ -CompassSample MicroBitCompass::getCalibration() + * Provides the calibration data currently in use by the compass. + * + * More specifically, the x, y and z zero offsets of the compass. + * + * @return A Sample3D containing the offsets for the x, y and z axis. + */ +Sample3D MicroBitCompass::getCalibration() { return average; } /** - * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration. - */ + * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration. + */ int MicroBitCompass::isCalibrated() { return status & MICROBIT_COMPASS_STATUS_CALIBRATED; } /** - * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating. - */ + * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating. + */ int MicroBitCompass::isCalibrating() { return status & MICROBIT_COMPASS_STATUS_CALIBRATING; } /** - * Clears the calibration held in persistent storage, and sets the calibrated flag to zero. - */ + * Clears the calibration held in memory storage, and sets the calibrated flag to zero. + */ void MicroBitCompass::clearCalibration() { + average = Sample3D(); status &= ~MICROBIT_COMPASS_STATUS_CALIBRATED; } /** - * Destructor for MicroBitCompass, where we deregister this instance from the array of fiber components. + * Configures the device for the 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 compass could not be configured. + */ +int MicroBitCompass::configure() +{ + return MICROBIT_NOT_SUPPORTED; +} + +/** + * Attempts to set the sample rate of the compass to the specified period value (in ms). + * + * @param period the requested time between samples, in milliseconds. + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails. + * + * @note The requested rate may not be possible on the hardware. In this case, the + * nearest lower rate is chosen. + * + * @note This method should be overriden (if supported) by specific magnetometer device drivers. + */ +int MicroBitCompass::setPeriod(int period) +{ + int result; + + samplePeriod = period; + result = configure(); + + samplePeriod = getPeriod(); + return result; + +} + +/** + * Reads the currently configured sample rate of the compass. + * + * @return The time between samples, in milliseconds. + */ +int MicroBitCompass::getPeriod() +{ + return (int)samplePeriod; +} + +/** + * Poll to see if new data is available from the hardware. If so, update it. + * n.b. it is not necessary to explicitly call this funciton to update data + * (it normally happens in the background when the scheduler is idle), but a check is performed + * if the user explicitly requests up to date data. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the update fails. + * + * @note This method should be overidden by the hardware driver to implement the requested + * changes in hardware. + */ +int MicroBitCompass::requestUpdate() +{ + return MICROBIT_NOT_SUPPORTED; +} + +/** + * Stores data from the compass sensor in our buffer. + * + * On first use, this member function will attempt to add this component to the + * list of fiber components in order to constantly update the values stored + * by this object. + * + * This lazy instantiation means that we do not + * obtain the overhead from non-chalantly adding this component to fiber components. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails. + */ +int MicroBitCompass::update() +{ + // Store the new data, after performing any necessary coordinate transformations. + sample = coordinateSpace.transform(sampleENU - average); + + // Indicate that a new sample is available + MicroBitEvent e(id, MICROBIT_COMPASS_EVT_DATA_UPDATE); + + return MICROBIT_OK; +}; + +/** + * Reads the last compass value stored, and provides it in the coordinate system requested. + * + * @param coordinateSpace The coordinate system to use. + * @return The force measured in each axis, in milli-g. + */ +Sample3D MicroBitCompass::getSample(CoordinateSystem coordinateSystem) +{ + requestUpdate(); + return coordinateSpace.transform(sampleENU - average, coordinateSystem); +} + +/** + * Reads the last compass value stored, and in the coordinate system defined in the constructor. + * @return The force measured in each axis, in milli-g. + */ +Sample3D MicroBitCompass::getSample() +{ + requestUpdate(); + return sample; +} + +/** + * reads the value of the x axis from the latest update retrieved from the compass, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the x axis, in milli-g. + */ +int MicroBitCompass::getX() +{ + requestUpdate(); + return sample.x; +} + +/** + * reads the value of the y axis from the latest update retrieved from the compass, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the y axis, in milli-g. + */ +int MicroBitCompass::getY() +{ + requestUpdate(); + return sample.y; +} + +/** + * reads the value of the z axis from the latest update retrieved from the compass, + * using the default coordinate system as specified in the constructor. + * + * @return the force measured in the z axis, in milli-g. + */ +int MicroBitCompass::getZ() +{ + requestUpdate(); + return sample.z; +} + +/** + * Calculates a tilt compensated bearing of the device, using the accelerometer. + */ +int MicroBitCompass::tiltCompensatedBearing() +{ + // Precompute the tilt compensation parameters to improve readability. + float phi = accelerometer->getRollRadians(); + float theta = accelerometer->getPitchRadians(); + + Sample3D s = getSample(NORTH_EAST_DOWN); + + float x = (float) s.x; + float y = (float) s.y; + float z = (float) s.z; + + // Precompute cos and sin of pitch and roll angles to make the calculation a little more efficient. + float sinPhi = sin(phi); + float cosPhi = cos(phi); + float sinTheta = sin(theta); + float cosTheta = cos(theta); + + float bearing = (360*atan2(z*sinPhi - y*cosPhi, x*cosTheta + y*sinTheta*sinPhi + z*sinTheta*cosPhi)) / (2*PI); + + if (bearing < 0) + bearing += 360.0; + + return (int) bearing; +} + +/** + * Calculates a non-tilt compensated bearing of the device. + */ +int MicroBitCompass::basicBearing() +{ + float bearing = (atan2((double)(sample.y - average.y),(double)(sample.x - average.x)))*180/PI; + + if (bearing < 0) + bearing += 360.0; + + return (int)(360.0 - bearing); +} + +/** + * Destructor. */ MicroBitCompass::~MicroBitCompass() { - fiber_remove_idle_component(this); } -const MAG3110SampleRateConfig MAG3110SampleRate[MAG3110_SAMPLE_RATES] = { - {12500, 0x00}, // 80 Hz - {25000, 0x20}, // 40 Hz - {50000, 0x40}, // 20 Hz - {100000, 0x60}, // 10 hz - {200000, 0x80}, // 5 hz - {400000, 0x88}, // 2.5 hz - {800000, 0x90}, // 1.25 hz - {1600000, 0xb0}, // 0.63 hz - {3200000, 0xd0}, // 0.31 hz - {6400000, 0xf0}, // 0.16 hz - {12800000, 0xf8} // 0.08 hz -}; + diff --git a/source/drivers/MicroBitCompassCalibrator.cpp b/source/drivers/MicroBitCompassCalibrator.cpp index fb4e68e..75ed0c9 100644 --- a/source/drivers/MicroBitCompassCalibrator.cpp +++ b/source/drivers/MicroBitCompassCalibrator.cpp @@ -145,11 +145,16 @@ void MicroBitCompassCalibrator::calibrate(MicroBitEvent) if (cursor.x == perimeter[i].x && cursor.y == perimeter[i].y && !perimeter[i].on) { // Record the sample data for later processing... - X.set(samples, 0, compass.getX(RAW)); - X.set(samples, 1, compass.getY(RAW)); - X.set(samples, 2, compass.getZ(RAW)); + Sample3D s = compass.getSample(RAW); + X.set(samples, 0, s.x); + X.set(samples, 1, s.y); + X.set(samples, 2, s.z); X.set(samples, 3, 1); + //X.set(samples, 0, compass.getX(RAW)); + //X.set(samples, 1, compass.getY(RAW)); + //X.set(samples, 2, compass.getZ(RAW)); + // Record that this pixel has been visited. perimeter[i].on = 1; samples++; @@ -177,7 +182,7 @@ void MicroBitCompassCalibrator::calibrate(MicroBitEvent) // The result contains the approximate zero point of each axis, but doubled. // Halve each sample, and record this as the compass calibration data. - CompassSample cal ((int)(Beta.get(0,0) / 2), (int)(Beta.get(1,0) / 2), (int)(Beta.get(2,0) / 2)); + Sample3D cal ((int)(Beta.get(0,0) / 2), (int)(Beta.get(1,0) / 2), (int)(Beta.get(2,0) / 2)); compass.setCalibration(cal); // Show a smiley to indicate that we're done, and continue on with the user program. diff --git a/source/drivers/MicroBitI2C.cpp b/source/drivers/MicroBitI2C.cpp index fae2727..46a5c89 100644 --- a/source/drivers/MicroBitI2C.cpp +++ b/source/drivers/MicroBitI2C.cpp @@ -73,6 +73,7 @@ int MicroBitI2C::read(int address, char *data, int length, bool repeated) { int result = I2C::read(address,data,length,repeated); +#ifdef MICROBIT_I2C_RESET_IN_FAIL //0 indicates a success, presume failure while(result != 0 && retries < MICROBIT_I2C_MAX_RETRIES) { @@ -86,6 +87,7 @@ int MicroBitI2C::read(int address, char *data, int length, bool repeated) result = I2C::read(address,data,length,repeated); retries++; } +#endif if(result != 0) return MICROBIT_I2C_ERROR; @@ -112,6 +114,7 @@ int MicroBitI2C::write(int address, const char *data, int length, bool repeated) int result = I2C::write(address,data,length,repeated); //0 indicates a success, presume failure +#ifdef MICROBIT_I2C_RESET_IN_FAIL while(result != 0 && retries < MICROBIT_I2C_MAX_RETRIES) { _i2c.i2c->EVENTS_ERROR = 0; @@ -125,6 +128,7 @@ int MicroBitI2C::write(int address, const char *data, int length, bool repeated) result = I2C::write(address,data,length,repeated); retries++; } +#endif if(result != 0) return MICROBIT_I2C_ERROR; @@ -132,3 +136,54 @@ int MicroBitI2C::write(int address, const char *data, int length, bool repeated) retries = 0; return MICROBIT_OK; } + +/** + * Issues a standard, 2 byte I2C command write. + * + * Blocks the calling thread until complete. + * + * @param address The address of the I2C device to write to. + * @param reg The address of the register in the device to write. + * @param value The value to write. + * + * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed. + */ +int MicroBitI2C::writeRegister(uint8_t address, uint8_t reg, uint8_t value) +{ + uint8_t command[2]; + command[0] = reg; + command[1] = value; + + return write(address, (const char *)command, 2); +} + +/** + * Issues a read command, copying data 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. + */ +int MicroBitI2C::readRegister(uint8_t address, uint8_t reg, uint8_t* buffer, int length) +{ + int result; + + if (buffer == NULL || length <= 0 ) + return MICROBIT_INVALID_PARAMETER; + + result = write(address, (const char *)®, 1, true); + if (result !=0) + return MICROBIT_I2C_ERROR; + + result = read(address, (char *)buffer, length); + if (result !=0) + return MICROBIT_I2C_ERROR; + + return MICROBIT_OK; +} diff --git a/source/drivers/MicroBitIO.cpp b/source/drivers/MicroBitIO.cpp index 5a1aff0..f6a701b 100644 --- a/source/drivers/MicroBitIO.cpp +++ b/source/drivers/MicroBitIO.cpp @@ -46,7 +46,7 @@ MicroBitIO::MicroBitIO(int ID_P0, int ID_P1, int ID_P2, int ID_P9, int ID_P10,int ID_P11, int ID_P12,int ID_P13,int ID_P14, int ID_P15,int ID_P16,int ID_P19, - int ID_P20) : + int ID_P20, int ID_INT1, int ID_INT2) : P0 (ID_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL), //P0 is the left most pad (ANALOG/DIGITAL/TOUCH) P1 (ID_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL), //P1 is the middle pad (ANALOG/DIGITAL/TOUCH) P2 (ID_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL), //P2 is the right most pad (ANALOG/DIGITAL/TOUCH) @@ -65,6 +65,8 @@ MicroBitIO::MicroBitIO(int ID_P0, int ID_P1, int ID_P2, P15(ID_P15,MICROBIT_PIN_P15,PIN_CAPABILITY_STANDARD), //MOSI P16(ID_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_STANDARD), //PIN 16 P19(ID_P19,MICROBIT_PIN_P19,PIN_CAPABILITY_STANDARD), //SCL - P20(ID_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_STANDARD) //SDA + P20(ID_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_STANDARD), //SDA + int1(ID_INT1,P0_28,PIN_CAPABILITY_STANDARD), //INT1 + int2(ID_INT2,P0_29,PIN_CAPABILITY_STANDARD) //INT2 { } diff --git a/source/types/CoordinateSystem.cpp b/source/types/CoordinateSystem.cpp new file mode 100644 index 0000000..45a0a86 --- /dev/null +++ b/source/types/CoordinateSystem.cpp @@ -0,0 +1,120 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Lancaster University. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#include "CoordinateSystem.h" + +/** + * Constructor. + * + * Creates a new coordinatespace transformation object. + * + * @param system the CoordinateSystem to generated as output. + * @param upsidedown set if the sensor is mounted inverted (upside down) on the device board. + * @param rotated defines the rotation of the sensor on the PCB, with respect to pin 1 being at the top left corner + * when viewing the device from its "natural" (user defined) orientation. n.b. if the sensor is upside down, the rotation + * should be defined w.r.t. lookign at the side of the device where the sensor is mounted. + */ +CoordinateSpace::CoordinateSpace(CoordinateSystem system, bool upsidedown, int rotated) +{ + this->system = system; + this->upsidedown = upsidedown; + this->rotated = rotated; +} + +/** + * Transforms a given 3D x,y,z tuple from ENU format into that format defined in this instance. + * + * @param a the sample point to convert, in ENU format. + * @return the equivalent location of 's' in the coordinate space specified in the constructor. + */ +Sample3D CoordinateSpace::transform(Sample3D s) +{ + return transform(s, system); +} + +/** + * Transforms a given 3D x,y,z tuple from ENU format into that format defined in this instance. + * + * @param a the sample point to convert, in ENU format. + * @param system The coordinate system to use in the result. + * @return the equivalent location of 's' in the coordinate space specified in the constructor, and coordinate system supplied. + */ +Sample3D CoordinateSpace::transform(Sample3D s, CoordinateSystem system) +{ + Sample3D result = s; + int temp; + + // Firstly, handle any inversions. + // As we know the input is in ENU format, this means we flip the polarity of the X and Z axes. + if(upsidedown) + { + result.y = -result.y; + result.z = -result.z; + } + + // Now, handle any rotations. + switch (rotated) + { + case COORDINATE_SPACE_ROTATED_90: + temp = -result.x; + result.x = result.y; + result.y = temp; + break; + + case COORDINATE_SPACE_ROTATED_180: + result.x = -result.x; + result.y = -result.y; + break; + + case COORDINATE_SPACE_ROTATED_270: + temp = result.x; + result.x = -result.y; + result.y = temp; + break; + } + + // Finally, apply coordinate system transformation. + switch (system) + { + case NORTH_EAST_DOWN: + result.y = -result.y; + result.z = -result.z; + break; + + case SIMPLE_CARTESIAN: + temp = result.x; + result.x = result.y; + result.y = temp; + result.z = -result.z; + break; + + case EAST_NORTH_UP: + case RAW: + break; + } + + return result; + +} +