diff --git a/inc/MicroBit.h b/inc/MicroBit.h index fd9a51e..31a8dbc 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -47,13 +47,22 @@ /** * Class definition for a MicroBit device. * - * Represents the device as a whole, and includes member variables to that reflect the components of the system. + * Represents the device as a whole, and includes member variables that represent various device drivers + * used to control aspects of the micro:bit. */ class MicroBit { private: + /** + * A listener to perform actions as a result of Message Bus reflection. + * + * In some cases we want to perform lazy instantiation of components, such as + * the compass and the accelerometer, where we only want to add them to the idle + * fiber when someone has the intention of using these components. + */ void onListenerRegisteredEvent(MicroBitEvent evt); + uint8_t status; public: @@ -93,54 +102,54 @@ class MicroBit /** * Constructor. - * Create a representation of a MicroBit device as a global singleton. - * @param messageBus callback function to receive MicroBitMessageBus events. * - * Exposed objects: - * @code - * uBit.messageBus; //The message bus where events are fired. - * uBit.display; //The display object for the LED matrix. - * uBit.buttonA; //The buttonA object for button a. - * uBit.buttonB; //The buttonB object for button b. - * uBit.resetButton; //The resetButton used for soft resets. - * uBit.accelerometer; //The object that represents the inbuilt accelerometer - * uBit.compass; //The object that represents the inbuilt compass(magnetometer) - * uBit.io.P*; //Where P* is P0 to P16, P19 & P20 on the edge connector - * @endcode + * Create a representation of a MicroBit device, which includes member variables + * that represent various device drivers used to control aspects of the micro:bit. */ MicroBit(); /** * Post constructor initialisation method. - * After *MUCH* pain, it's noted that the BLE stack can't be brought up in a - * static context, so we bring it up here rather than in the constructor. - * n.b. This method *must* be called in main() or later, not before. * - * Example: + * This call will initialised the scheduler, memory allocator and Bluetooth stack. + * + * This is required as the Bluetooth stack can't be brought up in a + * static context i.e. in a constructor. + * * @code * uBit.init(); * @endcode + * + * @note This method must be called before user code utilises any functionality + * contained by uBit. */ void init(); /** - * Return the friendly name for this device. - * - * @return A string representing the friendly name of this device. - */ + * Return the friendly name for this device. + * + * @return A ManagedString representing the friendly name of this device. + * + * @code + * ManagedString name = uBit.getName(); + * @endcode + */ static ManagedString getName(); /** - * Return the serial number of this device. - * - * @return A string representing the serial number of this device. - */ + * Return the serial number of this device. + * + * @return A ManagedString representing the serial number of this device. + * + * @code + * ManagedString serialNumber = uBit.getSerial(); + * @endcode + */ static ManagedString getSerial(); /** * Will reset the micro:bit when called. * - * Example: * @code * uBit.reset(); * @endcode @@ -148,28 +157,34 @@ class MicroBit void reset(); /** - * Delay for the given amount of time. + * Delay execution for the given amount of time. + * * If the scheduler is running, this will deschedule the current fiber and perform * a power efficent, concurrent sleep operation. + * * If the scheduler is disabled or we're running in an interrupt context, this * will revert to a busy wait. * - * @note Values of 6 and below tend to lose resolution - do you really need to sleep for this short amount of time? + * Alternatively: wait, wait_ms, wait_us can be used which will perform a blocking sleep + * operation. * * @param milliseconds the amount of time, in ms, to wait for. This number cannot be negative. + * * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER milliseconds is less than zero. * - * Example: * @code - * MicroBit::sleep(20); //sleep for 20ms + * uBit.sleep(20); //sleep for 20ms * @endcode + * + * @note This operation is currently limited by the rate of the system timer, therefore + * the granularity of the sleep operation is limited to 6 ms unless the rate of + * the system timer is modified. */ void sleep(uint32_t milliseconds); /** * Seed the pseudo random number generator using the hardware generator. * - * Example: * @code * uBit.seedRandom(); * @endcode @@ -181,22 +196,27 @@ class MicroBit * * @param seed The 32-bit value to seed the generator with. * - * Example: * @code - * uBit.seedRandom(0x12345678); + * uBit.seedRandom(0xBB5EED); * @endcode */ void seedRandom(uint32_t seed); + /** * Generate a random number in the given range. - * We use the NRF51822 in built random number generator here - * TODO: Determine if we want to, given its relatively high power consumption! + * We use a simple Galois LFSR random number generator here, + * as a Galois LFSR is sufficient for our applications, and much more lightweight + * than the hardware random number generator built int the processor, which takes + * a long time and uses a lot of energy. * - * @param max the upper range to generate a number for. This number cannot be negative - * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_PARAMETER if max is <= 0. + * KIDS: You shouldn't use this is the real world to generte cryptographic keys though... + * have a think why not. :-) + * + * @param max the upper range to generate a number for. This number cannot be negative. + * + * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0. * - * Example: * @code * uBit.random(200); //a number between 0 and 199 * @endcode @@ -206,63 +226,135 @@ class MicroBit /** * Determine the time since this MicroBit was last reset. * - * @return The time since the last reset, in milliseconds. This will result in overflow after 1.6 months. - * TODO: handle overflow case. + * @return The time since the last reset, in milliseconds. + * + * @note This will value overflow after 1.6 months. */ + //TODO: handle overflow case. unsigned long systemTime(); /** * Determine the version of the micro:bit runtime currently in use. * - * @return A textual description of the currentlt executing micro:bit runtime. - * TODO: handle overflow case. + * @return A textual description of the version of the micro:bit runtime that + * is currently running on this device. */ const char *systemVersion(); /** - * Triggers a microbit panic where an infinite loop will occur swapping between the panicFace and statusCode if provided. + * Triggers a microbit panic where an loop will display a panic face + * and the status code, if provided. * - * @param statusCode the status code of the associated error. Status codes must be in the range 0-255. + * This loop will continue for panic_timeout iterations, defaults to 0 (infinite). + * + * panic_timeout can be configured via a call to microbit_panic_timeout. + * + * @param statusCode the status code of the associated error. + * + * @code + * microbit_panic_timeout(4); + * + * // will display loop for 4 iterations. + * uBit.panic(10); + * @endcode */ void panic(int statusCode = 0); - /** - * add a component to the array of components which invocate the systemTick member function during a systemTick - * @param component The component to add. - * @return MICROBIT_OK on success. MICROBIT_NO_RESOURCES is returned if further components cannot be supported. - * @note This interface is now deprecated. See fiber_add_system_component(). - */ + /** + * Add a component to the array of system components. This component will then receive + * periodic callbacks, once every tick period in interrupt context. + * + * @param component The component to add. + * + * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the component array is full. + * + * @code + * // heap allocated - otherwise it will be paged out! + * MicroBitDisplay* display = new MicroBitDisplay(); + * + * uBit.addSystemComponent(display); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use system_timer_add_component(). + */ int addSystemComponent(MicroBitComponent *component); - /** - * remove a component from the array of components - * @param component The component to remove. - * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMTER is returned if the given component has not been previous added. - * @note This interface is now deprecated. See fiber_remove_system_component(). - */ + /** + * Remove a component from the array of system components. This component will no longer receive + * periodic callbacks. + * + * @param component The component to remove. + * + * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added. + * + * @code + * // heap allocated - otherwise it will be paged out! + * MicroBitDisplay* display = new MicroBitDisplay(); + * + * uBit.addSystemComponent(display); + * + * uBit.removeSystemComponent(display); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use system_timer_remove_component(). + */ int removeSystemComponent(MicroBitComponent *component); - /** - * add a component to the array of components which invocate the systemTick member function during a systemTick - * @param component The component to add. - * @return MICROBIT_OK on success. MICROBIT_NO_RESOURCES is returned if further components cannot be supported. - * @note This interface is now deprecated. See fiber_add_idle_component(). - */ + /** + * Adds a component to the array of idle thread components, which are processed + * when the run queue is empty. + * + * The system timer will poll isIdleCallbackNeeded on each component to determine + * if the scheduler should schedule the idle_task imminently. + * + * @param component The component to add to the array. + * + * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the fiber components array is full. + * + * @code + * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); + * + * // heap allocated - otherwise it will be paged out! + * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c); + * + * fiber_add_idle_component(accelerometer); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use fiber_add_idle_component(). + */ int addIdleComponent(MicroBitComponent *component); - /** - * remove a component from the array of components - * @param component The component to remove. - * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMTER is returned if the given component has not been previous added. - * @note This interface is now deprecated. See fiber_remove_idle_component(). - */ + /** + * Remove a component from the array of idle thread components + * + * @param component The component to remove from the idle component array. + * + * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added. + * + * @code + * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); + * + * // heap allocated - otherwise it will be paged out! + * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c); + * + * uBit.addIdleComponent(accelerometer); + * + * uBit.removeIdleComponent(accelerometer); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use fiber_remove_idle_component(). + */ int removeIdleComponent(MicroBitComponent *component); }; /** * Return the friendly name for this device. * - * @return A string representing the friendly name of this device. + * @return A ManagedString representing the friendly name of this device. + * + * @code + * ManagedString name = uBit.getName(); + * @endcode */ inline ManagedString MicroBit::getName() { @@ -270,10 +362,14 @@ inline ManagedString MicroBit::getName() } /** - * Return the serial number of this device. - * - * @return A string representing the serial number of this device. - */ + * Return the serial number of this device. + * + * @return A ManagedString representing the serial number of this device. + * + * @code + * ManagedString serialNumber = uBit.getSerial(); + * @endcode + */ inline ManagedString MicroBit::getSerial() { // We take to 16 bit numbers here, as we want the full range of ID bits, but don't want negative numbers... @@ -290,7 +386,6 @@ inline ManagedString MicroBit::getSerial() /** * Will reset the micro:bit when called. * - * Example: * @code * uBit.reset(); * @endcode @@ -310,28 +405,34 @@ inline void MicroBit::reset() } /** - * Delay for the given amount of time. + * Delay execution for the given amount of time. + * * If the scheduler is running, this will deschedule the current fiber and perform * a power efficent, concurrent sleep operation. + * * If the scheduler is disabled or we're running in an interrupt context, this * will revert to a busy wait. * - * @note Values of below below the scheduling period (typical 6ms) tend to lose resolution. + * Alternatively: wait, wait_ms, wait_us can be used which will perform a blocking sleep + * operation. * * @param milliseconds the amount of time, in ms, to wait for. This number cannot be negative. + * * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER milliseconds is less than zero. * - * Example: * @code * uBit.sleep(20); //sleep for 20ms * @endcode + * + * @note This operation is currently limited by the rate of the system timer, therefore + * the granularity of the sleep operation is limited to 6 ms unless the rate of + * the system timer is modified. */ inline void MicroBit::sleep(uint32_t milliseconds) { fiber_sleep(milliseconds); } - /** * Generate a random number in the given range. * We use a simple Galois LFSR random number generator here, @@ -342,10 +443,10 @@ inline void MicroBit::sleep(uint32_t milliseconds) * KIDS: You shouldn't use this is the real world to generte cryptographic keys though... * have a think why not. :-) * - * @param max the upper range to generate a number for. This number cannot be negative - * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE (defined in ErrorNo.h) if max is <= 0. + * @param max the upper range to generate a number for. This number cannot be negative. + * + * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0. * - * Example: * @code * uBit.random(200); //a number between 0 and 199 * @endcode @@ -356,11 +457,11 @@ inline int MicroBit::random(int max) } /** - * Seed our a random number generator (RNG). - * We use the NRF51822 in built cryptographic random number generator to seed a Galois LFSR. - * We do this as the hardware RNG is relatively high power, and use the the BLE stack internally, - * with a less than optimal application interface. A Galois LFSR is sufficient for our - * applications, and much more lightweight. + * Seed the pseudo random number generator using the hardware random number generator. + * + * @code + * uBit.seedRandom(); + * @endcode */ inline void MicroBit::seedRandom() { @@ -369,7 +470,13 @@ inline void MicroBit::seedRandom() /** - * Seed our pseudo random number generator (PRNG) using the given 32-bit value. + * Seed the pseudo random number generator using the given value. + * + * @param seed The 32-bit value to seed the generator with. + * + * @code + * uBit.seedRandom(0xBB5EED); + * @endcode */ inline void MicroBit::seedRandom(uint32_t seed) { @@ -378,10 +485,21 @@ inline void MicroBit::seedRandom(uint32_t seed) /** - * add a component to the array of components which invocate the systemTick member function during a systemTick + * Add a component to the array of system components. This component will then receive + * periodic callbacks, once every tick period in interrupt context. + * * @param component The component to add. - * @return MICROBIT_OK on success. MICROBIT_NO_RESOURCES is returned if further components cannot be supported. - * @note this will be converted into a dynamic list of components + * + * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the component array is full. + * + * @code + * // heap allocated - otherwise it will be paged out! + * MicroBitDisplay* display = new MicroBitDisplay(); + * + * uBit.addSystemComponent(display); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use system_timer_add_component(). */ inline int MicroBit::addSystemComponent(MicroBitComponent *component) { @@ -389,10 +507,23 @@ inline int MicroBit::addSystemComponent(MicroBitComponent *component) } /** - * remove a component from the array of components + * Remove a component from the array of system components. This component will no longer receive + * periodic callbacks. + * * @param component The component to remove. - * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMTER is returned if the given component has not been previous added. - * @note this will be converted into a dynamic list of components + * + * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added. + * + * @code + * // heap allocated - otherwise it will be paged out! + * MicroBitDisplay* display = new MicroBitDisplay(); + * + * uBit.addSystemComponent(display); + * + * uBit.removeSystemComponent(display); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use system_timer_remove_component(). */ inline int MicroBit::removeSystemComponent(MicroBitComponent *component) { @@ -400,10 +531,26 @@ inline int MicroBit::removeSystemComponent(MicroBitComponent *component) } /** - * add a component to the array of components which invocate the systemTick member function during a systemTick - * @param component The component to add. - * @return MICROBIT_OK on success. MICROBIT_NO_RESOURCES is returned if further components cannot be supported. - * @note this will be converted into a dynamic list of components + * Adds a component to the array of idle thread components, which are processed + * when the run queue is empty. + * + * The system timer will poll isIdleCallbackNeeded on each component to determine + * if the scheduler should schedule the idle_task imminently. + * + * @param component The component to add to the array. + * + * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the fiber components array is full. + * + * @code + * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); + * + * // heap allocated - otherwise it will be paged out! + * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c); + * + * fiber_add_idle_component(accelerometer); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use fiber_add_idle_component(). */ inline int MicroBit::addIdleComponent(MicroBitComponent *component) { @@ -411,10 +558,24 @@ inline int MicroBit::addIdleComponent(MicroBitComponent *component) } /** - * remove a component from the array of components - * @param component The component to remove. - * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMTER is returned if the given component has not been previous added. - * @note this will be converted into a dynamic list of components + * Remove a component from the array of idle thread components + * + * @param component The component to remove from the idle component array. + * + * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added. + * + * @code + * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0); + * + * // heap allocated - otherwise it will be paged out! + * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c); + * + * uBit.addIdleComponent(accelerometer); + * + * uBit.removeIdleComponent(accelerometer); + * @endcode + * + * @note This interface is now deprecated, and will be removed in the next major release. Please use fiber_remove_idle_component(). */ inline int MicroBit::removeIdleComponent(MicroBitComponent *component) { @@ -425,8 +586,9 @@ inline int MicroBit::removeIdleComponent(MicroBitComponent *component) /** * Determine the time since this MicroBit was last reset. * - * @return The time since the last reset, in milliseconds. This will result in overflow after 1.6 months. - * TODO: handle overflow case. + * @return The time since the last reset, in milliseconds. + * + * @note This will value overflow after 1.6 months. */ inline unsigned long MicroBit::systemTime() { @@ -435,19 +597,32 @@ inline unsigned long MicroBit::systemTime() /** - * Determine the version of the micro:bit runtime currently in use. - * - * @return A textual description of the currentlt executing micro:bit runtime. - * TODO: handle overflow case. - */ + * Determine the version of the micro:bit runtime currently in use. + * + * @return A textual description of the version of the micro:bit runtime that + * is currently running on this device. + */ inline const char *MicroBit::systemVersion() { return microbit_dal_version(); } /** - * Triggers a microbit panic. All functionality will cease, and a sad face displayed along with an error code. - * @param statusCode the status code of the associated error. Status codes must be in the range 0-255. + * Triggers a microbit panic where an loop will display a panic face + * and the status code, if provided. + * + * This loop will continue for panic_timeout iterations, defaults to 0 (infinite). + * + * panic_timeout can be configured via a call to microbit_panic_timeout. + * + * @param statusCode the status code of the associated error. + * + * @code + * microbit_panic_timeout(4); + * + * // will display loop for 4 iterations. + * uBit.panic(10); + * @endcode */ inline void MicroBit::panic(int statusCode) { diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index c981f23..5a5104a 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -29,22 +29,9 @@ RawSerial* SERIAL_DEBUG = NULL; /** * Constructor. - * Create a representation of a MicroBit device as a global singleton. - * @param messageBus callback function to receive MicroBitMessageBus events. * - * Exposed objects: - * @code - * uBit.systemTicker; //the Ticker callback that performs routines like updating the display. - * uBit.messageBus; //The message bus where events are fired. - * uBit.display; //The display object for the LED matrix. - * uBit.buttonA; //The buttonA object for button a. - * uBit.buttonB; //The buttonB object for button b. - * uBit.buttonAB; //The buttonAB object for button a+b multi press. - * uBit.resetButton; //The resetButton used for soft resets. - * uBit.accelerometer; //The object that represents the inbuilt accelerometer - * uBit.compass; //The object that represents the inbuilt compass(magnetometer) - * uBit.io.P*; //Where P* is P0 to P16, P19 & P20 on the edge connector - * @endcode + * Create a representation of a MicroBit device, which includes member variables + * that represent various device drivers used to control aspects of the micro:bit. */ MicroBit::MicroBit() : serial(USBTX, USBRX), @@ -81,14 +68,18 @@ MicroBit::MicroBit() : /** * Post constructor initialisation method. - * After *MUCH* pain, it's noted that the BLE stack can't be brought up in a - * static context, so we bring it up here rather than in the constructor. - * n.b. This method *must* be called in main() or later, not before. * - * Example: + * This call will initialised the scheduler, memory allocator and Bluetooth stack. + * + * This is required as the Bluetooth stack can't be brought up in a + * static context i.e. in a constructor. + * * @code * uBit.init(); * @endcode + * + * @note This method must be called before user code utilises any functionality + * contained by uBit. */ void MicroBit::init() { @@ -107,7 +98,7 @@ void MicroBit::init() seedRandom(); // Create an event handler to trap any handlers being created for I2C services. - // We do this to enable initialisation of those services only when they're used, + // We do this to enable initialisation of those services only when they're used, // which saves processor time, memeory and battery life. messageBus.listen(MICROBIT_ID_MESSAGE_BUS_LISTENER, MICROBIT_EVT_ANY, this, &MicroBit::onListenerRegisteredEvent); @@ -204,5 +195,3 @@ void MicroBit::onListenerRegisteredEvent(MicroBitEvent evt) break; } } - -