diff --git a/inc/MicroBitAccelerometer.h b/inc/MicroBitAccelerometer.h index f1041f63..511950cc 100644 --- a/inc/MicroBitAccelerometer.h +++ b/inc/MicroBitAccelerometer.h @@ -27,84 +27,53 @@ DEALINGS IN THE SOFTWARE. #include "CodalConfig.h" #include "MicroBitCompat.h" -#include "codal-core/inc/core/CodalComponent.h" #include "codal-core/inc/driver-models/Accelerometer.h" -#include "codal-core/inc/driver-models/Compass.h" -#include "codal-core/inc/driver-models/I2C.h" -#include "codal-core/inc/driver-models/Pin.h" #include "codal-core/inc/types/CoordinateSystem.h" +#include "MicroBitI2C.h" -/** - * Class definition for MicroBitAccelerometer. - */ -class MicroBitAccelerometer : public Accelerometer +namespace codal { - public: + /** + * Class definition for MicroBitAccelerometer. + */ + class MicroBitAccelerometer : public Accelerometer + { + public: static Accelerometer* driver; // The instance of an Accelerometer driver. - static NRF52Pin irq1; // IRQ pin for detected acceleromters to use - static CoordinateSpace coordinateSpace; // Default coordinate space /** * Constructor. - * Create a software abstraction of an accelerometer. + * Create an instance of an accelerometer that detects which on-board + * sensor is present and initialises the respective driver. * - * @param coordinateSpace the orientation of the sensor. Defaults to: SIMPLE_CARTESIAN + * @param coordinateSpace the orientation of the sensor. * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER * */ - MicroBitAccelerometer(MicroBitI2C &i2c, uint16_t id = MICROBIT_ID_ACCELEROMETER); + MicroBitAccelerometer(MicroBitI2C &i2c, CoordinateSpace &coordinateSpace, uint16_t id = MICROBIT_ID_ACCELEROMETER); /** - * Device autodetection. Scans the given I2C bus for supported accelerometer devices. - * if found, constructs an appropriate driver and returns it. - * - * @param i2c the bus to scan. - * - */ - static Accelerometer& autoDetect(MicroBitI2C &i2c); - - /** - * 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. + * Constructor for an empty driver. * - * @note The requested rate may not be possible on the hardware. In this case, the - * nearest lower rate is chosen. + * This constructor should only be called when the on-board sensor is not + * detected, and we need an instance that's able to panic on first usage. * - * @note This method should be overriden (if supported) by specific accelerometer device drivers. - */ - int setPeriod(int period); - - /** - * Reads the currently configured sample rate of the accelerometer. + * @param coordinateSpace the orientation of the sensor. + * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER * - * @return The time between samples, in milliseconds. */ - int getPeriod(); + MicroBitAccelerometer(CoordinateSpace &coordinateSpace, uint16_t id = MICROBIT_ID_ACCELEROMETER); /** - * 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. + * Device autodetection. Scans the given I2C bus for supported accelerometer devices. + * if found, constructs an appropriate driver and returns it. * - * @note The requested range may not be possible on the hardware. In this case, the - * nearest lower range is chosen. + * @param i2c the bus to scan. * - * @note This method should be overriden (if supported) by specific accelerometer device drivers. */ - int setRange(int range); - - /** - * Reads the currently configured sample range of the accelerometer. - * - * @return The sample range, in g. - */ - int getRange(); + static Accelerometer& autoDetect(MicroBitI2C &i2c); /** * Configures the accelerometer for G range and sample rate defined @@ -113,125 +82,24 @@ class MicroBitAccelerometer : public Accelerometer * 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 configure(); + virtual int configure() override; /** * 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 + * 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. - * - * @note This method should be overidden by the hardware driver to implement the requested - * changes in hardware. - */ - int requestUpdate(); - - /** - * 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 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 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(); - - /** - * 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(); - - /** - * 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 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 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(); - - /** - * 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 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(); - - /** - * 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(); + virtual int requestUpdate() override; /** * Destructor. */ ~MicroBitAccelerometer(); -}; + }; +} #endif diff --git a/inc/MicroBitCompass.h b/inc/MicroBitCompass.h index e7e169de..5d57dfcb 100644 --- a/inc/MicroBitCompass.h +++ b/inc/MicroBitCompass.h @@ -30,113 +30,36 @@ DEALINGS IN THE SOFTWARE. #include "CoordinateSystem.h" #include "MicroBitAccelerometer.h" -/** - * Class definition for a general e-compass. - */ -class MicroBitCompass : public Compass +namespace codal { - public: - static Compass* driver; // The instance of a MicroBitAcelerometer driver. + /** + * Class definition for a general e-compass. + */ + class MicroBitCompass : public Compass + { + public: + static Compass* driver; // The instance of a Compass driver. MicroBitAccelerometer* accelerometer; // The accelerometer to use for tilt compensation. /** * Constructor. * Create a software abstraction of an e-compass. * + * @param coordinateSpace the orientation of the sensor. * @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(MicroBitI2C &i2c, uint16_t id = MICROBIT_ID_COMPASS); + MicroBitCompass(MicroBitI2C &i2c, CoordinateSpace &coordinateSpace, uint16_t id = MICROBIT_ID_COMPASS); + MicroBitCompass(CoordinateSpace &coordinateSpace, uint16_t id = MICROBIT_ID_COMPASS); /** * Device autodetection. Scans the given I2C bus for supported compass devices. * if found, constructs an appropriate driver and returns it. * - * @param i2c the bus to scan. + * @param i2c the bus to scan. * */ - static Compass& autoDetect(MicroBitI2C &i2c); - - /** - * 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(); - - /** - * 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(); - - /** - * 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(); - - /** - * 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(CompassCalibration calibration); - - /** - * 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. - */ - CompassCalibration getCalibration(); - - /** - * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration. - */ - int isCalibrated(); - - /** - * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating. - */ - int isCalibrating(); - - /** - * Clears the calibration held in persistent storage, and sets the calibrated flag to zero. - */ - void clearCalibration(); - + static Compass& autoDetect(MicroBitI2C &i2c); /** * Configures the device for the sample rate defined @@ -146,118 +69,29 @@ class MicroBitCompass : public Compass * * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the compass could not be configured. */ - virtual int configure(); - - /** - * - * Defines the accelerometer to be used for tilt compensation. - * - * @param acceleromter Reference to the accelerometer to use. - */ - void setAccelerometer(MicroBitAccelerometer &accelerometer); - - /** - * 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 setPeriod(int period); - - /** - * Reads the currently configured sample rate of the compass. - * - * @return The time between samples, in milliseconds. - */ - int getPeriod(); + virtual int configure() override; /** * 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 + * 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. - * - * @note This method should be overidden by the hardware driver to implement the requested - * changes in hardware. - */ - int requestUpdate(); - - /** - * 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 update(); - - /** - * 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); - - /** - * 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(); - - /** - * 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(); - - /** - * 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(); - - /** - * 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(); - - /** - * updateSample() method maintained here as an inline method purely for backward compatibility. - */ - void updateSample() - { - getSample(); - } + virtual int requestUpdate() override; /** * Destructor. */ ~MicroBitCompass(); -}; + }; + + // + // Backward Compatibility + // + typedef Sample3D CompassSample; -// -// Backward Compatibility -// -typedef Sample3D CompassSample; +} #endif diff --git a/source/MicroBitAccelerometer.cpp b/source/MicroBitAccelerometer.cpp index b9fe747d..8af5a512 100644 --- a/source/MicroBitAccelerometer.cpp +++ b/source/MicroBitAccelerometer.cpp @@ -26,7 +26,6 @@ DEALINGS IN THE SOFTWARE. #include "MicroBitIO.h" #include "MicroBitEvent.h" #include "MicroBitCompat.h" -#include "MicroBitFiber.h" #include "MicroBitDevice.h" #include "MicroBitI2C.h" #include "MicroBitCompass.h" @@ -35,321 +34,84 @@ DEALINGS IN THE SOFTWARE. #include "LSM303Magnetometer.h" -Accelerometer* MicroBitAccelerometer::driver; +using namespace codal; -MicroBitAccelerometer::MicroBitAccelerometer(MicroBitI2C &i2c, uint16_t id) : Accelerometer(coordinateSpace) +Accelerometer* MicroBitAccelerometer::driver = NULL; + + +MicroBitAccelerometer::MicroBitAccelerometer(MicroBitI2C &i2c, CoordinateSpace &coordinateSpace, uint16_t id) : Accelerometer(coordinateSpace, id) { autoDetect(i2c); } -/** - * Device autodetection. Scans the given I2C bus for supported accelerometer devices. - * if found, constructs an appropriate driver and returns it. - * - * @param i2c the bus to scan. - * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER - * - */ +MicroBitAccelerometer::MicroBitAccelerometer(CoordinateSpace &coordinateSpace, uint16_t id) : Accelerometer(coordinateSpace, id) +{ +} + Accelerometer& MicroBitAccelerometer::autoDetect(MicroBitI2C &i2c) { static bool autoDetectCompleted = false; static CoordinateSpace coordinateSpace(SIMPLE_CARTESIAN, true, COORDINATE_SPACE_ROTATED_0); static NRF52Pin irq1(ID_PIN_IRQ1, P0_25, PIN_CAPABILITY_AD); - /* - * In essence, the LSM needs at least 6.4ms from power-up before we can use it. - * https://github.com/microbit-foundation/codal-microbit/issues/33 - */ - target_wait(10); - - // Add pullup resisitor to IRQ line (it's floating ACTIVE LO) - irq1.getDigitalValue(); - irq1.setPull(PullMode::Up); - irq1.setActiveLo(); - if (!autoDetectCompleted) { - MicroBitAccelerometer::driver = NULL; - MicroBitCompass::driver = NULL; + /* + * In essence, the LSM needs at least 6.4ms from power-up before we can use it. + * https://github.com/microbit-foundation/codal-microbit/issues/33 + */ + target_wait(7); + + // Add pullup resistor to IRQ line (it's floating ACTIVE LO) + irq1.getDigitalValue(); + irq1.setPull(PullMode::Up); + irq1.setActiveLo(); // Now, probe for the LSM303, and if it doesn't reply, panic - if ( LSM303Accelerometer::isDetected(i2c, 0x32) ) + if ( LSM303Accelerometer::isDetected(i2c, LSM303_A_DEFAULT_ADDR) ) { - MicroBitAccelerometer::driver = new LSM303Accelerometer( i2c, irq1, coordinateSpace, 0x32 ); - MicroBitCompass::driver = new LSM303Magnetometer( i2c, irq1, coordinateSpace, 0x3C ); - MicroBitCompass::driver->setAccelerometer( *MicroBitAccelerometer::driver ); + MicroBitAccelerometer::driver = new LSM303Accelerometer( i2c, irq1, coordinateSpace, LSM303_A_DEFAULT_ADDR ); + MicroBitCompass::driver = new LSM303Magnetometer( i2c, irq1, coordinateSpace, LSM303_M_DEFAULT_ADDR ); } + else + { + MicroBitAccelerometer::driver = new MicroBitAccelerometer( coordinateSpace ); + MicroBitCompass::driver = new MicroBitCompass( coordinateSpace ); + } + MicroBitCompass::driver->setAccelerometer( *MicroBitAccelerometer::driver ); autoDetectCompleted = true; } - - return *driver; -} - -/** - * 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 MicroBitAccelerometer::setPeriod(int period) -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->setPeriod(period); -} - -/** - * Reads the currently configured sample rate of the accelerometer. - * - * @return The time between samples, in milliseconds. - */ -int MicroBitAccelerometer::getPeriod() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getPeriod(); -} - -/** - * 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 MicroBitAccelerometer::setRange(int range) -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->setRange( range ); -} -/** - * Reads the currently configured sample range of the accelerometer. - * - * @return The sample range, in g. - */ -int MicroBitAccelerometer::getRange() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getRange(); + return *driver; } -/** - * 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() { - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->configure(); + if ( MicroBitAccelerometer::driver == this ) + { + microbit_panic( MicroBitPanic::ACCELEROMETER_ERROR ); + return DEVICE_NOT_SUPPORTED; + } + else + { + // Using an else helps the compiler to compile out this bit if possible + return driver->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 MicroBitAccelerometer::requestUpdate() { - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->requestUpdate(); -} - -/** - * 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) -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getSample( coordinateSystem ); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getSample(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getX(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getY(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getZ(); -} - -/** - * 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 MicroBitAccelerometer::getPitch() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getPitch(); -} - -/** - * 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 MicroBitAccelerometer::getPitchRadians() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getPitchRadians(); -} - -/** - * 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 MicroBitAccelerometer::getRoll() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getRoll(); -} - -/** - * 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 MicroBitAccelerometer::getRollRadians() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getRollRadians(); -} - -/** - * Retrieves the last recorded gesture. - * - * @return The last gesture that was detected. - * - * Example: - * @code - * - * if (accelerometer.getGesture() == SHAKE) - * display.scroll("SHAKE!"); - * @endcode - */ -uint16_t MicroBitAccelerometer::getGesture() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::ACCELEROMETER_ERROR ); - - return driver->getGesture(); + if ( MicroBitAccelerometer::driver == this ) + { + microbit_panic( MicroBitPanic::ACCELEROMETER_ERROR ); + return DEVICE_NOT_SUPPORTED; + } + else + { + // Using an else helps the compiler to compile out this bit if possible + return driver->requestUpdate(); + } } MicroBitAccelerometer::~MicroBitAccelerometer() diff --git a/source/MicroBitCompass.cpp b/source/MicroBitCompass.cpp index 17683834..ca388dda 100644 --- a/source/MicroBitCompass.cpp +++ b/source/MicroBitCompass.cpp @@ -31,320 +31,55 @@ DEALINGS IN THE SOFTWARE. #include "MicroBitError.h" #include "LSM303Magnetometer.h" -Compass* MicroBitCompass::driver; +using namespace codal; -/** - * 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(MicroBitI2C &i2c, uint16_t id) : Compass(MicroBitAccelerometer::coordinateSpace) -{ - autoDetect(i2c); -} - -/** - * Device autodetection. Scans the given I2C bus for supported accelerometer devices. - * if found, constructs an appropriate driver and returns it. - * - * @param i2c the bus to scan. - * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER - * - */ -Compass& MicroBitCompass::autoDetect(MicroBitI2C &i2c) -{ - /* - * In essence, the LSM needs at least 6.4ms from power-up before we can use it. - * https://github.com/microbit-foundation/codal-microbit/issues/33 - */ - target_wait(10); - - // We only have combined sensors, so rely on the accelerometer detection code to also detect the magnetomter. - MicroBitAccelerometer::autoDetect(i2c); - - return *MicroBitCompass::driver; -} - -/** - * 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( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->heading(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->getFieldStrength(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->calibrate(); -} +Compass* MicroBitCompass::driver = NULL; -/** - * 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(CompassCalibration calibration) +MicroBitCompass::MicroBitCompass(MicroBitI2C &i2c, CoordinateSpace &coordinateSpace, uint16_t id) : Compass(coordinateSpace, id) { - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->setCalibration( calibration ); -} - -/** - * 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. - */ -CompassCalibration MicroBitCompass::getCalibration() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->getCalibration(); + autoDetect(i2c); } -/** - * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration. - */ -int MicroBitCompass::isCalibrated() +MicroBitCompass::MicroBitCompass(CoordinateSpace &coordinateSpace, uint16_t id) : Compass(coordinateSpace, id) { - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->isCalibrated(); } -/** - * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating. - */ -int MicroBitCompass::isCalibrating() +Compass& MicroBitCompass::autoDetect(MicroBitI2C &i2c) { - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->isCalibrating(); -} + // We only have combined sensors, so rely on the accelerometer detection code to also detect the magnetometer. + MicroBitAccelerometer::autoDetect(i2c); -/** - * Clears the calibration held in memory storage, and sets the calibrated flag to zero. - */ -void MicroBitCompass::clearCalibration() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->clearCalibration(); + return *MicroBitCompass::driver; } -/** - * 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() { - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->configure(); -} - -/** - * - * Defines the accelerometer to be used for tilt compensation. - * - * @param acceleromter Reference to the accelerometer to use. - */ -void MicroBitCompass::setAccelerometer(MicroBitAccelerometer &accelerometer) -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->setAccelerometer( accelerometer ); -} - -/** - * 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) -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->setPeriod( period ); -} - -/** - * Reads the currently configured sample rate of the compass. - * - * @return The time between samples, in milliseconds. - */ -int MicroBitCompass::getPeriod() -{ - return driver->getPeriod(); + if ( MicroBitCompass::driver == this ) + { + microbit_panic( MicroBitPanic::COMPASS_ERROR ); + return DEVICE_NOT_SUPPORTED; + } + else + { + // Using an else helps the compiler to compile out this bit if possible + return driver->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 MicroBitCompass::requestUpdate() { - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->requestUpdate(); -} - -/** - * 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) -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->getSample( coordinateSystem ); + if ( MicroBitCompass::driver == this ) + { + microbit_panic( MicroBitPanic::COMPASS_ERROR ); + return DEVICE_NOT_SUPPORTED; + } + else + { + // Using an else helps the compiler to compile out this bit if possible + return driver->requestUpdate(); + } } -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->getSample(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->getX(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->getY(); -} - -/** - * 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() -{ - if( driver == NULL ) - target_panic( MicroBitPanic::COMPASS_ERROR ); - - return driver->getZ(); -} - -/** - * Destructor. - */ MicroBitCompass::~MicroBitCompass() { } - diff --git a/source/MicroBitDevice.cpp b/source/MicroBitDevice.cpp index fea6f4d5..b3fbacd4 100644 --- a/source/MicroBitDevice.cpp +++ b/source/MicroBitDevice.cpp @@ -26,6 +26,7 @@ DEALINGS IN THE SOFTWARE. #include "MicroBitDevice.h" #include "nrf.h" #include "hal/nrf_gpio.h" +#include "cmsis_compiler.h" #ifdef SOFTDEVICE_PRESENT #include "nrf_sdm.h" @@ -182,10 +183,14 @@ bool ble_running() /** * Perform a hard reset of the micro:bit. */ -void -microbit_reset() +__NO_RETURN void microbit_reset() { NVIC_SystemReset(); + + // __NO_RETURN added to NVIC_SystemReset() in CMSIS V5.0.5 + // Currently using V5.0.3, so this can be removed if updated in the future + // Looks like it gets compiled out anyway + for (;;); } /** @@ -386,7 +391,7 @@ void microbit_panic_timeout(int iterations) panic_timeout = iterations; } -void microbit_panic( int statusCode) +__NO_RETURN void microbit_panic( int statusCode) { const microbit_LEDMapStr &mm = microbit_LEDMap; uint8_t chr;