From dcd61f4da14ee551cb9afe89bc842b4fc1a4fab7 Mon Sep 17 00:00:00 2001 From: Juri Date: Fri, 11 Sep 2020 14:26:16 +0200 Subject: [PATCH] pxt 6.x updates --- .gitignore | 5 +- .yotta.json | 6 - inc/core/MicroBitComponent.h | 91 +++--- inc/core/MicroBitConfig.h | 33 ++- inc/core/MicroBitFiber.h | 19 +- inc/core/MicroBitListener.h | 4 +- inc/core/MicroBitLock.h | 66 +++++ inc/drivers/CalliopeMicrophone.h | 123 ++++++++ inc/drivers/MicroBitAccelerometer.h | 4 +- inc/drivers/MicroBitCompass.h | 6 +- inc/drivers/MicroBitDisplay.h | 44 ++- inc/drivers/MicroBitMessageBus.h | 6 +- inc/drivers/MicroBitPin.h | 6 +- inc/drivers/MicroBitRadio.h | 4 +- inc/platform/yotta_cfg_mappings.h | 15 +- .../MicroBitAccelerometerService.cpp | 22 +- source/bluetooth/MicroBitBLEManager.cpp | 2 +- source/core/MicroBitFiber.cpp | 270 ++++++++++++------ source/core/MicroBitHeapAllocator.cpp | 2 +- source/drivers/CalliopeMicrophone.cpp | 151 ++++++++++ source/drivers/MicroBitAccelerometer.cpp | 4 +- source/drivers/MicroBitCompass.cpp | 2 +- source/drivers/MicroBitCompassCalibrator.cpp | 9 +- source/drivers/MicroBitDisplay.cpp | 84 ++++-- source/drivers/MicroBitMessageBus.cpp | 31 +- source/drivers/MicroBitSerial.cpp | 2 +- 26 files changed, 798 insertions(+), 213 deletions(-) delete mode 100644 .yotta.json create mode 100644 inc/core/MicroBitLock.h create mode 100644 inc/drivers/CalliopeMicrophone.h create mode 100644 source/drivers/CalliopeMicrophone.cpp diff --git a/.gitignore b/.gitignore index decc3876..f0f4fe1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ build +.yotta.json +.vscode yotta_modules yotta_targets *.swp *~ Makefile -.idea -*.iml +.vscode diff --git a/.yotta.json b/.yotta.json deleted file mode 100644 index 097b7ea1..00000000 --- a/.yotta.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "build": { - "target": "calliope-mini-classic-gcc,*", - "targetSetExplicitly": true - } -} diff --git a/inc/core/MicroBitComponent.h b/inc/core/MicroBitComponent.h index 25d23c22..535e4e2d 100644 --- a/inc/core/MicroBitComponent.h +++ b/inc/core/MicroBitComponent.h @@ -36,54 +36,47 @@ und Björn Eberhardt GbR by arrangement with Calliope GbR. // Enumeration of core components. #define MICROBIT_ID_BUTTON_A 1 #define MICROBIT_ID_BUTTON_B 2 -#define MICROBIT_ID_BUTTON_RESET 3 -#define MICROBIT_ID_ACCELEROMETER 4 -#define MICROBIT_ID_COMPASS 5 -#define MICROBIT_ID_DISPLAY 6 - -//EDGE connector events -#ifdef TARGET_NRF51_CALLIOPE -#define MICROBIT_IO_PINS 21 -#else -#define MICROBIT_IO_PINS 20 -#endif - -#define MICROBIT_ID_IO_P0 7 //P0 is the left most pad (ANALOG/DIGITAL) (CM: P1) -#define MICROBIT_ID_IO_P1 8 //P1 is the middle pad (ANALOG/DIGITAL) (CM: P2) -#define MICROBIT_ID_IO_P2 9 //P2 is the right most pad (ANALOG/DIGITAL) (CM: analog/tx) -#define MICROBIT_ID_IO_P3 10 //COL1 (ANALOG/DIGITAL) -#define MICROBIT_ID_IO_P4 11 //BTN_A -#define MICROBIT_ID_IO_P5 12 //COL2 (ANALOG/DIGITAL) -#define MICROBIT_ID_IO_P6 13 //ROW2 -#define MICROBIT_ID_IO_P7 14 //ROW1 -#define MICROBIT_ID_IO_P8 15 //PIN 18 (CM: analog/tx) -#define MICROBIT_ID_IO_P9 16 //ROW3 -#define MICROBIT_ID_IO_P10 17 //COL3 (ANALOG/DIGITAL) -#define MICROBIT_ID_IO_P11 18 //BTN_B -#define MICROBIT_ID_IO_P12 19 //PIN 20 (CM: P0) -#define MICROBIT_ID_IO_P13 20 //SCK -#define MICROBIT_ID_IO_P14 21 //MISO -#define MICROBIT_ID_IO_P15 22 //MOSI -#define MICROBIT_ID_IO_P16 23 //PIN 16 (CM: P3) -#define MICROBIT_ID_IO_P19 24 //SCL -#define MICROBIT_ID_IO_P20 25 //SDA +#define MICROBIT_ID_BUTTON_AB 3 // Button A+B multibutton +#define MICROBIT_ID_BUTTON_RESET 4 +#define MICROBIT_ID_ACCELEROMETER 5 +#define MICROBIT_ID_COMPASS 6 +#define MICROBIT_ID_DISPLAY 7 +#define MICROBIT_ID_THERMOMETER 8 +#define MICROBIT_ID_RADIO 9 +#define MICROBIT_ID_RADIO_DATA_READY 10 +#define MICROBIT_ID_MULTIBUTTON_ATTACH 11 +#define MICROBIT_ID_SERIAL 12 +#define MICROBIT_ID_GESTURE 13 // Gesture events + +#define MICROBIT_ID_IO_P0 100 //P0 is the left most pad (ANALOG/DIGITAL) (CM: P1) +#define MICROBIT_ID_IO_P1 101 //P1 is the middle pad (ANALOG/DIGITAL) (CM: P2) +#define MICROBIT_ID_IO_P2 102 //P2 is the right most pad (ANALOG/DIGITAL) (CM: analog/tx) +#define MICROBIT_ID_IO_P3 103 //COL1 (ANALOG/DIGITAL) +#define MICROBIT_ID_IO_P4 104 //BTN_A +#define MICROBIT_ID_IO_P5 105 //COL2 (ANALOG/DIGITAL) +#define MICROBIT_ID_IO_P6 106 //ROW2 +#define MICROBIT_ID_IO_P7 107 //ROW1 +#define MICROBIT_ID_IO_P8 108 //PIN 18 (CM: analog/tx) +#define MICROBIT_ID_IO_P9 109 //ROW3 +#define MICROBIT_ID_IO_P10 110 //COL3 (ANALOG/DIGITAL) +#define MICROBIT_ID_IO_P11 111 //BTN_B +#define MICROBIT_ID_IO_P12 112 //PIN 20 (CM: P0) +#define MICROBIT_ID_IO_P13 113 //SCK +#define MICROBIT_ID_IO_P14 114 //MISO +#define MICROBIT_ID_IO_P15 115 //MOSI +#define MICROBIT_ID_IO_P16 116 //PIN 16 (CM: P3) +#define MICROBIT_ID_IO_P19 119 //SCL +#define MICROBIT_ID_IO_P20 120 //SDA #ifdef TARGET_NRF51_CALLIOPE -#define MICROBIT_ID_IO_P21 50 // CM: analog microphone +#define MICROBIT_ID_IO_P21 121 // CM: analog microphone #endif -#define MICROBIT_ID_BUTTON_AB 26 // Button A+B multibutton -#define MICROBIT_ID_GESTURE 27 // Gesture events - -#define MICROBIT_ID_THERMOMETER 28 -#define MICROBIT_ID_RADIO 29 -#define MICROBIT_ID_RADIO_DATA_READY 30 -#define MICROBIT_ID_MULTIBUTTON_ATTACH 31 -#define MICROBIT_ID_SERIAL 32 +#define MICROBIT_ID_IO_INT1 130 //INT1 +#define MICROBIT_ID_IO_INT2 131 //INT2 +#define MICROBIT_ID_IO_INT3 132 //INT3 -#define MICROBIT_ID_IO_INT1 33 //INT1 -#define MICROBIT_ID_IO_INT2 34 //INT2 -#define MICROBIT_ID_IO_INT3 35 //INT3 -#define MICROBIT_ID_PARTIAL_FLASHING 36 +// System Softwarre components +#define MICROBIT_ID_PARTIAL_FLASHING 200 #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 @@ -98,17 +91,17 @@ und Björn Eberhardt GbR by arrangement with Calliope GbR. * * All components should inherit from this class. * - * If a component requires regular updates, then that component can be added to the + * If a component requires regular updates, then that component can be added to the * to the systemTick and/or idleTick queues. This provides a simple, extensible mechanism * for code that requires periodic/occasional background processing but does not warrant - * the complexity of maintaining its own thread. + * the complexity of maintaining its own thread. * - * Two levels of support are available. + * Two levels of support are available. * * systemTick() provides a periodic callback during the * micro:bit's system timer interrupt. This provides a guaranteed periodic callback, but in interrupt context * and is suitable for code with lightweight processing requirements, but strict time constraints. - * + * * idleTick() provides a periodic callback whenever the scheduler is idle. This provides occasional, callbacks * in the main thread context, but with no guarantees of frequency. This is suitable for non-urgent background tasks. * @@ -145,7 +138,7 @@ class MicroBitComponent /** * The idle thread will call this member function once the component has been added to the array - * of idle components using fiber_add_idle_component. + * of idle components using fiber_add_idle_component. */ virtual void idleTick() { diff --git a/inc/core/MicroBitConfig.h b/inc/core/MicroBitConfig.h index 1dc1fcc0..83c66694 100644 --- a/inc/core/MicroBitConfig.h +++ b/inc/core/MicroBitConfig.h @@ -87,10 +87,10 @@ DEALINGS IN THE SOFTWARE. // Defines where in memory persistent data is stored. #ifndef KEY_VALUE_STORE_PAGE -#define KEY_VALUE_STORE_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 17)) +#define KEY_VALUE_STORE_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 17)) #endif -#ifndef BLE_BOND_DATA_PAGE +#ifndef BLE_BOND_DATA_PAGE #define BLE_BOND_DATA_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 18)) #endif @@ -154,6 +154,18 @@ extern uint32_t __etext; #define MICROBIT_FIBER_USER_DATA 0 #endif +// Indicate get_fiber_list() API is supported +#ifndef MICROBIT_GET_FIBER_LIST_SUPPORTED +#define MICROBIT_GET_FIBER_LIST_SUPPORTED 1 +#endif + +// Maximum size of the FiberPool +// Defines the size that the pool of unused Fiber contexts is permitted to grow to. After this point, memory +// from unused Fiber contexts will be restored to the Heap Allocator. +#ifndef MICROBIT_FIBER_MAXIMUM_FIBER_POOL_SIZE +#define MICROBIT_FIBER_MAXIMUM_FIBER_POOL_SIZE 3 +#endif + // // Message Bus: // Default behaviour for event handlers, if not specified in the listen() call @@ -176,6 +188,21 @@ extern uint32_t __etext; #define MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH 10 #endif +// +// Define MESSAGE_BUS concurrency behaviour. +// Set to MESSAGE_BUS_CONCURRENT_LISTENERS to fire event handler +// concurrently when a given event is raised, and process events sequentially as they arrive (default micro:bit semantics). +// Set to MESSAGE_BUS_CONCURRENT_EVENTS to to fire event handlers sequentially for any given event, while still allowing +// concurrent processing of events. +// +// +// Permissable values are: +// 0: MESSAGE_BUS_CONCURRENT_LISTENERS +// 1: MESSAGE_BUS_CONCURRENT_EVENTS +// +#ifndef MESSAGE_BUS_CONCURRENCY_MODE +#define MESSAGE_BUS_CONCURRENCY_MODE MESSAGE_BUS_CONCURRENT_LISTENERS +#endif // // Core micro:bit services // @@ -436,7 +463,7 @@ extern uint32_t __etext; // Should be <= MBFS_BLOCK_SIZE. // #ifndef MBFS_CACHE_SIZE -#define MBFS_CACHE_SIZE 0 +#define MBFS_CACHE_SIZE 0 #endif // diff --git a/inc/core/MicroBitFiber.h b/inc/core/MicroBitFiber.h index f01519aa..8404910b 100644 --- a/inc/core/MicroBitFiber.h +++ b/inc/core/MicroBitFiber.h @@ -51,6 +51,12 @@ DEALINGS IN THE SOFTWARE. #define MICROBIT_FIBER_FLAG_CHILD 0x04 #define MICROBIT_FIBER_FLAG_DO_NOT_PAGE 0x08 +#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) +#define HAS_THREAD_USER_DATA (currentFiber->user_data != NULL) +#else +#define HAS_THREAD_USER_DATA false +#endif + /** * Thread Context for an ARM Cortex M0 core. * @@ -88,9 +94,11 @@ struct Fiber uint32_t context; // Context specific information. uint32_t flags; // Information about this fiber. Fiber **queue; // The queue this fiber is stored on. - Fiber *next, *prev; // Position of this Fiber on the run queue. + Fiber *qnext; // Position of this Fiber on its queue. + Fiber *next; // Position of this Fiber in the global list of fibers. + #if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) - void *user_data; + void *user_data; // Optional pointer to user defined data block. #endif }; @@ -114,6 +122,13 @@ void scheduler_init(EventModel &_messageBus); */ int fiber_scheduler_running(); +/** + * Provides a list of all active fibers. + * + * @return A pointer to the head of the list of all active fibers. + */ +Fiber* get_fiber_list(); + /** * Exit point for all fibers. * diff --git a/inc/core/MicroBitListener.h b/inc/core/MicroBitListener.h index 692ea767..a3f7802b 100644 --- a/inc/core/MicroBitListener.h +++ b/inc/core/MicroBitListener.h @@ -28,9 +28,9 @@ DEALINGS IN THE SOFTWARE. #include "mbed.h" #include "MicroBitConfig.h" +#include "MicroBitLock.h" #include "MicroBitEvent.h" #include "MemberFunctionCallback.h" -#include "MicroBitConfig.h" // MicroBitListener flags... #define MESSAGE_BUS_LISTENER_PARAMETERISED 0x0001 @@ -67,7 +67,7 @@ struct MicroBitListener MicroBitEvent evt; MicroBitEventQueueItem *evt_queue; - + MicroBitLock lock; MicroBitListener *next; /** diff --git a/inc/core/MicroBitLock.h b/inc/core/MicroBitLock.h new file mode 100644 index 00000000..0d66a960 --- /dev/null +++ b/inc/core/MicroBitLock.h @@ -0,0 +1,66 @@ +/* +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. +*/ + +/** + * A simple lock, mostly used for mutual exclusion. + */ + +#ifndef MICROBIT_LOCK_H +#define MICROBIT_LOCK_H + +#include "MicroBitConfig.h" + +class Fiber; + +class MicroBitLock +{ + private: + bool locked; + Fiber *queue; + + public: + + /** + * Create a new lock that can be used for mutual exclusion and condition synchronisation. + */ + MicroBitLock(); + + /** + * Block the calling fiber until the lock is available + **/ + void wait(); + + /** + * Release the lock, and signal to one waiting fiber to continue + */ + void notify(); + + /** + * Release the lock, and signal to all waiting fibers to continue + */ + void notifyAll(); +}; + +#endif diff --git a/inc/drivers/CalliopeMicrophone.h b/inc/drivers/CalliopeMicrophone.h new file mode 100644 index 00000000..071a0772 --- /dev/null +++ b/inc/drivers/CalliopeMicrophone.h @@ -0,0 +1,123 @@ +/* +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 CALLIOPE_MICROPHONE +#define CALLIOPE_MICROPHONE + +#include "mbed.h" +#include "MicroBitComponent.h" +#include "MicroBitPin.h" +#include "EventModel.h" + + +#define CALLIOPE_MICROPHONE_MAX_VALUE 1023 +#define CALLIOPE_MICROPHONE_MAX_VALUE 1023 +#define CALLIOPE_MICROPHONE_MIN_VALUE 0 + +/** + * Class definition for CalliopeMicrophone. + * + * This is an object that interleaves sound sensing with Microphone. + */ +class MicroBitLightSensor +{ + + //contains the results from each section of the display + int results[MICROBIT_LIGHT_SENSOR_CHAN_NUM]; + + //holds the current channel (also used to index the results array) + uint8_t chan; + + //a Timeout which triggers our analogReady() call + Timeout analogTrigger; + + //a pointer the currently sensed pin, represented as an AnalogIn + AnalogIn* sensePin; + + const MatrixMap &matrixMap; + + /** + * After the startSensing method has been called, this method will be called + * MICROBIT_LIGHT_SENSOR_AN_SET_TIME after. + * + * It will then read from the currently selected channel using the AnalogIn + * that was configured in the startSensing method. + */ + void analogReady(); + + /** + * Forcibly disables the AnalogIn, otherwise it will remain in possession + * of the GPIO channel it is using, meaning that the display will not be + * able to use a channel (COL). + * + * This is required as per PAN 3, details of which can be found here: + * + * https://www.nordicsemi.com/eng/nordic/download_resource/24634/5/88440387 + */ + void analogDisable(); + + public: + + /** + * Constructor. + * + * Create a representation of the light sensor. + * + * @param map The mapping information that relates pin inputs/outputs to physical screen coordinates. + * Defaults to microbitMatrixMap, defined in MicroBitMatrixMaps.h. + */ + MicroBitLightSensor(const MatrixMap &map); + + /** + * This method returns a summed average of the three sections of the display. + * + * A section is defined as: + * + * Where each number represents a different section on the 5 x 5 matrix display. + * + * @return returns a value in the range 0 - 255 where 0 is dark, and 255 + * is very bright + */ + int read(); + + /** + * The method that is invoked by sending MICROBIT_DISPLAY_EVT_LIGHT_SENSE + * using the id MICROBIT_ID_DISPLAY. + * + * @note this can be manually driven by calling this member function, with + * a MicroBitEvent using the CREATE_ONLY option of the MicroBitEvent + * constructor. + */ + void startSensing(MicroBitEvent); + + /** + * A destructor for MicroBitLightSensor. + * + * The destructor removes the listener, used by MicroBitLightSensor from the default EventModel. + */ + ~MicroBitLightSensor(); +}; + +#endif diff --git a/inc/drivers/MicroBitAccelerometer.h b/inc/drivers/MicroBitAccelerometer.h index 653ff156..c7b9f3f6 100644 --- a/inc/drivers/MicroBitAccelerometer.h +++ b/inc/drivers/MicroBitAccelerometer.h @@ -134,10 +134,10 @@ class MicroBitAccelerometer : public MicroBitComponent * 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 i2c the bus to scan. * */ - static MicroBitAccelerometer& autoDetect(MicroBitI2C &i2c); + static MicroBitAccelerometer& autoDetect(MicroBitI2C &i2c); /** * Attempts to set the sample rate of the accelerometer to the specified value (in ms). diff --git a/inc/drivers/MicroBitCompass.h b/inc/drivers/MicroBitCompass.h index a4fdaa68..0f4527ec 100644 --- a/inc/drivers/MicroBitCompass.h +++ b/inc/drivers/MicroBitCompass.h @@ -67,7 +67,7 @@ class MicroBitCompass : public MicroBitComponent protected: uint16_t samplePeriod; // The time between samples, in milliseconds. - CompassCalibration calibration; // The calibration data of this compass + CompassCalibration calibration; // The calibration data of this compass 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. @@ -102,10 +102,10 @@ class MicroBitCompass : public MicroBitComponent * 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 MicroBitCompass& autoDetect(MicroBitI2C &i2c); + static MicroBitCompass& autoDetect(MicroBitI2C &i2c); /** diff --git a/inc/drivers/MicroBitDisplay.h b/inc/drivers/MicroBitDisplay.h index cd6c6282..d1e7d390 100644 --- a/inc/drivers/MicroBitDisplay.h +++ b/inc/drivers/MicroBitDisplay.h @@ -288,7 +288,6 @@ class MicroBitDisplay : public MicroBitComponent * @param s The string to display. * * @param delay The time to delay between characters, in milliseconds. Must be > 0. - * Defaults to: MICROBIT_DEFAULT_PRINT_SPEED. * * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. * @@ -296,7 +295,26 @@ class MicroBitDisplay : public MicroBitComponent * display.printAsync("abc123",400); * @endcode */ - int printAsync(ManagedString s, int delay = MICROBIT_DEFAULT_PRINT_SPEED); + int printAsync(ManagedString s, int delay); + + /** + * Prints the given ManagedString to the display, one character at a time. + * Returns immediately, and executes the animation asynchronously. + * + * If the string is greater than one charcter in length, the screen + * will be cleared after MICROBIT_DEFAULT_PRINT_SPEED milliseconds. + * Otherwise, that character will be left on the screen indefinitely. + * + * @param s The string to display. + * + * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. + * + * @code + * display.printAsync("abc123"); + * @endcode + */ + int printAsync(ManagedString s); + /** * Prints the given image to the display, if the display is not in use. @@ -352,7 +370,27 @@ class MicroBitDisplay : public MicroBitComponent * display.print("abc123",400); * @endcode */ - int print(ManagedString s, int delay = MICROBIT_DEFAULT_PRINT_SPEED); + int print(ManagedString s, int delay); + + /** + * Prints the given string to the display, one character at a time. + * + * Blocks the calling thread until all the text has been displayed. + * + * If the string is greater than one charcter in length, the screen + * will be cleared after MICROBIT_DEFAULT_PRINT_SPEED milliseconds. + * Otherwise, that character will be left on the screen indefinitely. + * + * @param s The string to display. + * + * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER. + * + * @code + * display.print("abc123"); + * @endcode + */ + int print(ManagedString s); + /** * Prints the given image to the display. diff --git a/inc/drivers/MicroBitMessageBus.h b/inc/drivers/MicroBitMessageBus.h index 4925a884..8517b7cb 100644 --- a/inc/drivers/MicroBitMessageBus.h +++ b/inc/drivers/MicroBitMessageBus.h @@ -33,6 +33,9 @@ DEALINGS IN THE SOFTWARE. #include "MicroBitListener.h" #include "EventModel.h" +#define MESSAGE_BUS_CONCURRENT_LISTENERS 0 +#define MESSAGE_BUS_CONCURRENT_EVENTS 1 + /** * Class definition for the MicroBitMessageBus. * @@ -105,7 +108,7 @@ class MicroBitMessageBus : public EventModel, public MicroBitComponent * @note It is recommended that all external code uses the send() function instead of this function, * or the constructors provided by MicrobitEvent. */ - int process(MicroBitEvent &evt, bool urgent = false); + int process(MicroBitEvent evt, bool urgent = false); /** * Returns the microBitListener with the given position in our list. @@ -144,7 +147,6 @@ class MicroBitMessageBus : public EventModel, public MicroBitComponent MicroBitListener *listeners; // Chain of active listeners. MicroBitEventQueueItem *evt_queue_head; // Head of queued events to be processed. MicroBitEventQueueItem *evt_queue_tail; // Tail of queued events to be processed. - uint16_t nonce_val; // The last nonce issued. uint16_t queueLength; // The number of events currently waiting to be processed. /** diff --git a/inc/drivers/MicroBitPin.h b/inc/drivers/MicroBitPin.h index 6bb64c45..7d4b21b0 100644 --- a/inc/drivers/MicroBitPin.h +++ b/inc/drivers/MicroBitPin.h @@ -42,8 +42,6 @@ und Björn Eberhardt GbR by arrangement with Calliope GbR. #define IO_STATUS_TOUCH_IN 0x10 // Pin is a makey-makey style touch sensor #define IO_STATUS_EVENT_ON_EDGE 0x20 // Pin will generate events on pin change #define IO_STATUS_EVENT_PULSE_ON_EDGE 0x40 // Pin will generate events on pin change -#define IO_STATUS_EVENTBUS_ENABLED 0x80 // Pin is will generate events on change - #ifdef TARGET_NRF51_CALLIOPE // micro:bit compatibility pin settings for Calliope mini @@ -105,12 +103,12 @@ und Björn Eberhardt GbR by arrangement with Calliope GbR. #define MICROBIT_PIN_MAX_SERVO_RANGE 180 #define MICROBIT_PIN_DEFAULT_SERVO_RANGE 2000 #define MICROBIT_PIN_DEFAULT_SERVO_CENTER 1500 -// config + #define MICROBIT_PIN_EVENT_NONE 0 #define MICROBIT_PIN_EVENT_ON_EDGE 1 #define MICROBIT_PIN_EVENT_ON_PULSE 2 #define MICROBIT_PIN_EVENT_ON_TOUCH 3 -// event output + #define MICROBIT_PIN_EVT_RISE 2 #define MICROBIT_PIN_EVT_FALL 3 #define MICROBIT_PIN_EVT_PULSE_HI 4 diff --git a/inc/drivers/MicroBitRadio.h b/inc/drivers/MicroBitRadio.h index c1320d94..f8a67885 100644 --- a/inc/drivers/MicroBitRadio.h +++ b/inc/drivers/MicroBitRadio.h @@ -67,6 +67,7 @@ struct FrameBuffer; #define MICROBIT_RADIO_DEFAULT_TX_POWER 6 #define MICROBIT_RADIO_MAX_PACKET_SIZE 32 #define MICROBIT_RADIO_HEADER_SIZE 4 +#define MICROBIT_RADIO_MAXIMUM_RX_BUFFERS 4 // Known Protocol Numbers #define MICROBIT_RADIO_PROTOCOL_DATAGRAM 1 // A simple, single frame datagram. a little like UDP but with smaller packets. :-) @@ -74,7 +75,8 @@ struct FrameBuffer; // Events #define MICROBIT_RADIO_EVT_DATAGRAM 1 // Event to signal that a new datagram has been received. -#define MICROBIT_RADIO_EVT_PACKET_DROPPED 2 // Event to signal that a datagram has been dropped. +#define MICROBIT_RADIO_EVT_PACKET_DROPPED 2 + struct FrameBuffer { diff --git a/inc/platform/yotta_cfg_mappings.h b/inc/platform/yotta_cfg_mappings.h index 213b692d..bf79e2ab 100644 --- a/inc/platform/yotta_cfg_mappings.h +++ b/inc/platform/yotta_cfg_mappings.h @@ -7,6 +7,15 @@ */ //DAL mappings + +#ifdef YOTTA_CFG_MICROBIT_DAL_FIBER_USER_DATA + #define MICROBIT_FIBER_USER_DATA YOTTA_CFG_MICROBIT_DAL_FIBER_USER_DATA +#endif + +#ifdef YOTTA_CFG_MICROBIT_DAL_MAXIMUM_FIBER_POOL_SIZE + #define MICROBIT_FIBER_MAXIMUM_FIBER_POOL_SIZE YOTTA_CFG_MICROBIT_DAL_MAXIMUM_FIBER_POOL_SIZE +#endif + #ifdef YOTTA_CFG_MICROBIT_DAL_HEAP_ALLOCATOR #define MICROBIT_HEAP_ALLOCATOR YOTTA_CFG_MICROBIT_DAL_HEAP_ALLOCATOR #endif @@ -48,7 +57,7 @@ #endif #ifdef YOTTA_CFG_MICROBIT_DAL_FULL_RANGE_PITCH_CALCULATION -#define MICROBIT_FULL_RANGE_PITCH_CALCULATION YOTTA_CFG_MICROBIT_DAL_FULL_RANGE_PITCH_CALCULATION +#define MICROBIT_FULL_RANGE_PITCH_CALCULATION YOTTA_CFG_MICROBIT_DAL_FULL_RANGE_PITCH_CALCULATION #endif #ifdef YOTTA_CFG_MICROBIT_DAL_MIN_DISPLAY_BRIGHTNESS @@ -124,10 +133,6 @@ #define MICROBIT_RADIO_UPPER_FREQ_BAND YOTTA_CFG_MICROBIT_DAL_RADIO_UPPER_FREQ_BAND #endif -#ifdef YOTTA_CFG_MICROBIT_DAL_RADIO_MAXIMUM_RX_BUFFERS - #define MICROBIT_RADIO_MAXIMUM_RX_BUFFERS YOTTA_CFG_MICROBIT_DAL_RADIO_MAXIMUM_RX_BUFFERS -#endif - //Bluetooth mappings #ifdef YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_PAIRING_MODE #define MICROBIT_BLE_PAIRING_MODE YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_PAIRING_MODE diff --git a/source/bluetooth/MicroBitAccelerometerService.cpp b/source/bluetooth/MicroBitAccelerometerService.cpp index 4d2b4cfc..736d98eb 100644 --- a/source/bluetooth/MicroBitAccelerometerService.cpp +++ b/source/bluetooth/MicroBitAccelerometerService.cpp @@ -1,4 +1,3 @@ -//TODO /* The MIT License (MIT) @@ -44,11 +43,11 @@ MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble, Micr { // Create the data structures that represent each of our characteristics in Soft Device. GattCharacteristic accelerometerDataCharacteristic(MicroBitAccelerometerServiceDataUUID, (uint8_t *)accelerometerDataCharacteristicBuffer, 0, - sizeof(accelerometerDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + sizeof(accelerometerDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); GattCharacteristic accelerometerPeriodCharacteristic(MicroBitAccelerometerServicePeriodUUID, (uint8_t *)&accelerometerPeriodCharacteristicBuffer, 0, - sizeof(accelerometerPeriodCharacteristicBuffer), - GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); + sizeof(accelerometerPeriodCharacteristicBuffer), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); // Initialise our characteristic values. accelerometerDataCharacteristicBuffer[0] = 0; @@ -82,7 +81,8 @@ MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble, Micr */ void MicroBitAccelerometerService::onDataWritten(const GattWriteCallbackParams *params) { - if (params->handle == accelerometerPeriodCharacteristicHandle && params->len >= sizeof(accelerometerPeriodCharacteristicBuffer)) { + if (params->handle == accelerometerPeriodCharacteristicHandle && params->len >= sizeof(accelerometerPeriodCharacteristicBuffer)) + { accelerometerPeriodCharacteristicBuffer = *((uint16_t *)params->data); accelerometer.setPeriod(accelerometerPeriodCharacteristicBuffer); @@ -98,26 +98,24 @@ void MicroBitAccelerometerService::onDataWritten(const GattWriteCallbackParams * */ void MicroBitAccelerometerService::accelerometerUpdate(MicroBitEvent) { - if (ble.getGapState().connected) { + if (ble.getGapState().connected) + { accelerometerDataCharacteristicBuffer[0] = accelerometer.getX(); accelerometerDataCharacteristicBuffer[1] = accelerometer.getY(); accelerometerDataCharacteristicBuffer[2] = accelerometer.getZ(); -// accelerometerPeriodCharacteristicBuffer = accelerometer.getPeriod(); -// ble.gattServer().write(accelerometerPeriodCharacteristicHandle, (const uint8_t *)&accelerometerPeriodCharacteristicBuffer, sizeof(accelerometerPeriodCharacteristicBuffer)); - ble.gattServer().notify(accelerometerDataCharacteristicHandle,(uint8_t *)accelerometerDataCharacteristicBuffer, sizeof(accelerometerDataCharacteristicBuffer)); } } const uint8_t MicroBitAccelerometerServiceUUID[] = { - 0xe9, 0x5d, 0x07, 0x53, 0x25, 0x1d, 0x47, 0x0a, 0xa0, 0x62, 0xfa, 0x19, 0x22, 0xdf, 0xa9, 0xa8 + 0xe9,0x5d,0x07,0x53,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; const uint8_t MicroBitAccelerometerServiceDataUUID[] = { - 0xe9, 0x5d, 0xca, 0x4b, 0x25, 0x1d, 0x47, 0x0a, 0xa0, 0x62, 0xfa, 0x19, 0x22, 0xdf, 0xa9, 0xa8 + 0xe9,0x5d,0xca,0x4b,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; const uint8_t MicroBitAccelerometerServicePeriodUUID[] = { - 0xe9, 0x5d, 0xfb, 0x24, 0x25, 0x1d, 0x47, 0x0a, 0xa0, 0x62, 0xfa, 0x19, 0x22, 0xdf, 0xa9, 0xa8 + 0xe9,0x5d,0xfb,0x24,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; diff --git a/source/bluetooth/MicroBitBLEManager.cpp b/source/bluetooth/MicroBitBLEManager.cpp index 5d1b43ec..5a077f48 100644 --- a/source/bluetooth/MicroBitBLEManager.cpp +++ b/source/bluetooth/MicroBitBLEManager.cpp @@ -643,7 +643,7 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &a { // Do not page this fiber! currentFiber->flags |= MICROBIT_FIBER_FLAG_DO_NOT_PAGE; - + ManagedString namePrefix("Calliope mini ["); ManagedString namePostfix("]"); ManagedString BLEName = namePrefix + deviceName + namePostfix; diff --git a/source/core/MicroBitFiber.cpp b/source/core/MicroBitFiber.cpp index 5e3068c2..cf514e4e 100644 --- a/source/core/MicroBitFiber.cpp +++ b/source/core/MicroBitFiber.cpp @@ -44,7 +44,6 @@ DEALINGS IN THE SOFTWARE. Fiber *currentFiber = NULL; // The context in which the current fiber is executing. static Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing. static Fiber *idleFiber = NULL; // the idle task - performs a power efficient sleep, and system maintenance tasks. - /* * Scheduler state. */ @@ -52,6 +51,7 @@ static Fiber *runQueue = NULL; // The list of runnable fiber static Fiber *sleepQueue = NULL; // The list of blocked fibers waiting on a fiber_sleep() operation. static Fiber *waitQueue = NULL; // The list of blocked fibers waiting on an event. static Fiber *fiberPool = NULL; // Pool of unused fibers, just waiting for a job to do. +static Fiber *fiberList = NULL; // List of all active Fibers (excludes those in the fiberPool) /* * Scheduler wide flags @@ -67,44 +67,6 @@ static EventModel *messageBus = NULL; // Array of components which are iterated during idle thread execution. static MicroBitComponent* idleThreadComponents[MICROBIT_IDLE_COMPONENTS]; -static void get_fibers_from(Fiber ***dest, int *sum, Fiber *queue) -{ - if (queue && queue->prev) - microbit_panic(MICROBIT_HEAP_ERROR); - while (queue) { - if (*dest) - *(*dest)++ = queue; - (*sum)++; - queue = queue->next; - } -} - -/** - * Return all current fibers. - * - * @param dest If non-null, it points to an array of pointers to fibers to store results in. - * - * @return the number of fibers (potentially) stored - */ -int list_fibers(Fiber **dest) -{ - int sum = 0; - - // interrupts might move fibers between queues, but should not create new ones - __disable_irq(); - get_fibers_from(&dest, &sum, runQueue); - get_fibers_from(&dest, &sum, sleepQueue); - get_fibers_from(&dest, &sum, waitQueue); - __enable_irq(); - - // idleFiber is used to start event handlers using invoke(), - // so it may in fact have the user_data set if in FOB context - if (dest) - *dest++ = idleFiber; - sum++; - return sum; -} - /** * Utility function to add the currenty running fiber to the given queue. * @@ -128,8 +90,7 @@ void queue_fiber(Fiber *f, Fiber **queue) // list, it results in fairer scheduling. if (*queue == NULL) { - f->next = NULL; - f->prev = NULL; + f->qnext = NULL; *queue = f; } else @@ -138,12 +99,11 @@ void queue_fiber(Fiber *f, Fiber **queue) // We don't maintain a tail pointer to save RAM (queues are nrmally very short). Fiber *last = *queue; - while (last->next != NULL) - last = last->next; + while (last->qnext != NULL) + last = last->qnext; - last->next = f; - f->prev = last; - f->next = NULL; + last->qnext = f; + f->qnext = NULL; } __enable_irq(); @@ -160,23 +120,40 @@ void dequeue_fiber(Fiber *f) if (f->queue == NULL) return; - // Remove this fiber fromm whichever queue it is on. __disable_irq(); - if (f->prev != NULL) - f->prev->next = f->next; + if (*(f->queue) == f) + { + // Remove the fiber from the head of the queue + *(f->queue) = f->qnext; + } else - *(f->queue) = f->next; + { + Fiber *prev = *(f->queue); + + // Scan for the given fiber in its queue + while(prev->qnext != f) + prev = prev->qnext; - if(f->next) - f->next->prev = f->prev; + // Remove the fiber + prev->qnext = f->qnext; + } - f->next = NULL; - f->prev = NULL; + // Ensure old linkage is cleared + f->qnext = NULL; f->queue = NULL; __enable_irq(); +} +/** + * Provides a list of all active fibers. + * + * @return A pointer to the head of the list of all active fibers. + */ +Fiber * get_fiber_list() +{ + return fiberList; } /** @@ -211,9 +188,15 @@ Fiber *getFiberContext() f->flags = 0; f->tcb.stack_base = CORTEX_M0_STACK_BASE; - #if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) +#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) f->user_data = 0; - #endif +#endif + + // Add the new Fiber to the list of all fibers + __disable_irq(); + f->next = fiberList; + fiberList = f; + __enable_irq(); return f; } @@ -288,7 +271,7 @@ void scheduler_tick() // Check the sleep queue, and wake up any fibers as necessary. while (f != NULL) { - t = f->next; + t = f->qnext; if (system_timer_current_time() >= f->context) { @@ -324,7 +307,7 @@ void scheduler_event(MicroBitEvent evt) // Check the wait queue, and wake up any fibers as necessary. while (f != NULL) { - t = f->next; + t = f->qnext; // extract the event data this fiber is blocked on. uint16_t id = f->context & 0xFFFF; @@ -358,6 +341,13 @@ void scheduler_event(MicroBitEvent evt) messageBus->ignore(evt.source, evt.value, scheduler_event); } +/** + * Internal utility function to perform a fork operation on the current fiber, and return + * the current fibers context to the point at which it was checkpointed. + * + * This function is called whenever a fiber requests a "Fork on Block" behaviour and a + * blocking call to the scheduler is requested. + */ static Fiber* handle_fob() { Fiber *f = currentFiber; @@ -369,7 +359,8 @@ static Fiber* handle_fob() // Allocate a TCB from the new fiber. This will come from the tread pool if availiable, // else a new one will be allocated on the heap. forkedFiber = getFiberContext(); - // If we're out of memory, there's nothing we can do. + + // If we're out of memory, there's nothing we can do. // keep running in the context of the current thread as a best effort. if (forkedFiber != NULL) { #if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) @@ -401,6 +392,7 @@ void fiber_sleep(unsigned long t) return; } + // Fork a new fiber if necessary Fiber *f = handle_fob(); // Calculate and store the time we want to wake up. @@ -468,16 +460,9 @@ int fiber_wake_on_event(uint16_t id, uint16_t value) if (messageBus == NULL || !fiber_scheduler_running()) return MICROBIT_NOT_SUPPORTED; + // Fork a new fiber if necessary Fiber *f = handle_fob(); - // in case we created a new fiber, make sure to initialize its context - // in case schedule() isn't called immedietly afterwards - if (f != currentFiber) { - dequeue_fiber(f); - queue_fiber(f, &runQueue); - schedule(); - } - // Encode the event data in the context field. It's handy having a 32 bit core. :-) f->context = value << 16 | id; @@ -492,6 +477,9 @@ int fiber_wake_on_event(uint16_t id, uint16_t value) if (id != MICROBIT_ID_NOTIFY && id != MICROBIT_ID_NOTIFY_ONE) messageBus->listen(id, value, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE); + // NOTE: We intentionally don't re-enter the scheduler here, such that this function + // can be used to create atomic wait events. if using this function, the calling thread MUST + // call schedule() as its next call to the scheduler. return MICROBIT_OK; } @@ -552,9 +540,10 @@ int invoke(void (*entry_fn)(void)) // spawn a thread to deal with it. currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB; entry_fn(); - #if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) + +#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) currentFiber->user_data = 0; - #endif +#endif currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB; // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it. @@ -618,9 +607,10 @@ int invoke(void (*entry_fn)(void *), void *param) // spawn a thread to deal with it. currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB; entry_fn(param); - #if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) + +#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA) currentFiber->user_data = 0; - #endif +#endif currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB; // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it. @@ -758,29 +748,55 @@ void release_fiber(void *) */ void release_fiber(void) { + int fiberPoolSize = 0; + if (!fiber_scheduler_running()) return; // Remove ourselves form the runqueue. dequeue_fiber(currentFiber); - // limit the number of fibers in the pool - int numFree = 0; - for (Fiber *p = fiberPool; p; p = p->next) { - if (!p->next && numFree > 3) { - p->prev->next = NULL; - free((void *)p->stack_bottom); - memset(p, 0, sizeof(*p)); - free(p); - break; - } - numFree++; - } + // Scan the FiberPool and release memory to the heap if it is full. + for (Fiber *p = fiberPool; p; p = p->qnext) + fiberPoolSize++; + while (fiberPoolSize > MICROBIT_FIBER_MAXIMUM_FIBER_POOL_SIZE) + { + // Release Fiber contexts from the head of the FiberPool. + Fiber *p = fiberPool; + fiberPool = p->qnext; + free((void *)p->stack_bottom); + free(p); + fiberPoolSize--; + } // Add ourselves to the list of free fibers queue_fiber(currentFiber, &fiberPool); + // Remove the fiber from the list of active fibers + __disable_irq(); + if (fiberList == currentFiber) + { + fiberList = fiberList->next; + } + else + { + Fiber *p = fiberList; + + while (p) + { + if (p->next == currentFiber) + { + p->next = currentFiber->next; + break; + } + + p = p->next; + } + } + __enable_irq(); + + // Find something else to do! schedule(); } @@ -810,7 +826,7 @@ void verify_stack_size(Fiber *f) // If we're too small, increase our buffer size. if (bufferSize < stackDepth) { - // We are only here, when the current stack is the stack of fiber [f]. + // We are only here when the current stack is the stack of fiber [f]. // Make sure the contents of [currentFiber] variable reflects that, otherwise // an external memory allocator might get confused when scanning fiber stacks. Fiber *prevCurrFiber = currentFiber; @@ -828,7 +844,8 @@ void verify_stack_size(Fiber *f) // Recalculate where the top of the stack is and we're done. f->stack_top = f->stack_bottom + bufferSize; - + + // Restore Fiber context currentFiber = prevCurrFiber; } } @@ -893,7 +910,7 @@ void schedule() else if (currentFiber->queue == &runQueue) // If the current fiber is on the run queue, round robin. - currentFiber = currentFiber->next == NULL ? runQueue : currentFiber->next; + currentFiber = currentFiber->qnext == NULL ? runQueue : currentFiber->qnext; else // Otherwise, just pick the head of the run queue. @@ -1018,3 +1035,84 @@ void idle_task() schedule(); } } + +/** + * Create a new lock that can be used for mutual exclusion and condition synchronisation. + */ +MicroBitLock::MicroBitLock() +{ + queue = NULL; + locked = false; +} + +/** + * Block the calling fiber until the lock is available + **/ +void MicroBitLock::wait() +{ + Fiber *f = currentFiber; + + // If the scheduler is not running, then simply exit, as we're running monothreaded. + if (!fiber_scheduler_running()) + return; + + if (locked) + { + // wait() is a blocking call, so if we're in a fork on block context, + // it's time to spawn a new fiber... + if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB) + { + // Allocate a new fiber. This will come from the fiber pool if availiable, + // else a new one will be allocated on the heap. + forkedFiber = getFiberContext(); + + // If we're out of memory, there's nothing we can do. + // keep running in the context of the current thread as a best effort. + if (forkedFiber != NULL) + f = forkedFiber; + } + + // Remove fiber from the run queue + dequeue_fiber(f); + + // Add fiber to the sleep queue. We maintain strict ordering here to reduce lookup times. + queue_fiber(f, &queue); + + // Finally, enter the scheduler. + schedule(); + } + + locked = true; +} + +/** + * Release the lock, and signal to one waiting fiber to continue + */ +void MicroBitLock::notify() +{ + Fiber *f = queue; + + if (f) + { + dequeue_fiber(f); + queue_fiber(f, &runQueue); + } + locked = false; +} + +/** + * Release the lock, and signal to all waiting fibers to continue + */ +void MicroBitLock::notifyAll() +{ + Fiber *f = queue; + + while (f) + { + dequeue_fiber(f); + queue_fiber(f, &runQueue); + f = queue; + } + + locked = false; +} diff --git a/source/core/MicroBitHeapAllocator.cpp b/source/core/MicroBitHeapAllocator.cpp index 278dfab1..133a106d 100644 --- a/source/core/MicroBitHeapAllocator.cpp +++ b/source/core/MicroBitHeapAllocator.cpp @@ -427,4 +427,4 @@ int microbit_create_heap(uint32_t start, uint32_t end) return MICROBIT_OK; } -#endif +#endif \ No newline at end of file diff --git a/source/drivers/CalliopeMicrophone.cpp b/source/drivers/CalliopeMicrophone.cpp new file mode 100644 index 00000000..693a037f --- /dev/null +++ b/source/drivers/CalliopeMicrophone.cpp @@ -0,0 +1,151 @@ +/* +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. +*/ + +/** + * Class definition for MicroBitLightSensor. + * + * This is an object that interleaves light sensing with MicroBitDisplay. + */ + +#include "MicroBitConfig.h" +#include "MicroBitLightSensor.h" +#include "MicroBitDisplay.h" + +/** + * After the startSensing method has been called, this method will be called + * MICROBIT_LIGHT_SENSOR_AN_SET_TIME after. + * + * It will then read from the currently selected channel using the AnalogIn + * that was configured in the startSensing method. + */ +void MicroBitLightSensor::analogReady() +{ + this->results[chan] = this->sensePin->read_u16(); + + analogDisable(); + + DigitalOut((PinName)(matrixMap.columnStart + chan)).write(1); + + chan++; + + chan = chan % MICROBIT_LIGHT_SENSOR_CHAN_NUM; +} + + + +/** + * Constructor. + * + * Create a representation of the light sensor. + * + * @param map The mapping information that relates pin inputs/outputs to physical screen coordinates. + * Defaults to microbitMatrixMap, defined in MicroBitMatrixMaps.h. + */ +MicroBitLightSensor::MicroBitLightSensor(const MatrixMap &map) : + analogTrigger(), + matrixMap(map) +{ + this->chan = 0; + + for(int i = 0; i < MICROBIT_LIGHT_SENSOR_CHAN_NUM; i++) + results[i] = 0; + + if (EventModel::defaultEventBus) + EventModel::defaultEventBus->listen(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_LIGHT_SENSE, this, &MicroBitLightSensor::startSensing, MESSAGE_BUS_LISTENER_IMMEDIATE); + + this->sensePin = NULL; +} + +/** + * This method returns a summed average of the three sections of the display. + * + + * + * Where each number represents a different section on the 5 x 5 matrix display. + * + * @return returns a value in the range 0 - 255 where 0 is dark, and 255 + * is very bright + */ +int MicroBitLightSensor::read() +{ + int sum = 0; + + for(int i = 0; i < MICROBIT_LIGHT_SENSOR_CHAN_NUM; i++) + sum += results[i]; + + int average = sum / MICROBIT_LIGHT_SENSOR_CHAN_NUM; + + average = min(average, MICROBIT_LIGHT_SENSOR_MAX_VALUE); + + average = max(average, MICROBIT_LIGHT_SENSOR_MIN_VALUE); + + int inverted = (MICROBIT_LIGHT_SENSOR_MAX_VALUE - average) + MICROBIT_LIGHT_SENSOR_MIN_VALUE; + + int a = 0; + + int b = 255; + + int normalised = a + ((((inverted - MICROBIT_LIGHT_SENSOR_MIN_VALUE)) * (b - a))/ (MICROBIT_LIGHT_SENSOR_MAX_VALUE - MICROBIT_LIGHT_SENSOR_MIN_VALUE)); + + return normalised; +} + +/** + * The method that is invoked by sending MICROBIT_DISPLAY_EVT_LIGHT_SENSE + * using the id MICROBIT_ID_DISPLAY. + * + * @note this can be manually driven by calling this member function, with + * a MicroBitEvent using the CREATE_ONLY option of the MicroBitEvent + * constructor. + */ +void MicroBitLightSensor::startSensing(MicroBitEvent) +{ + for(int rowCount = 0; rowCount < matrixMap.rows; rowCount++) + DigitalOut((PinName)(matrixMap.rowStart + rowCount)).write(0); + + PinName currentPin = (PinName)(matrixMap.columnStart + chan); + + DigitalOut(currentPin).write(1); + + DigitalIn(currentPin, PullNone).~DigitalIn(); + + if(this->sensePin != NULL) + delete this->sensePin; + + this->sensePin = new AnalogIn(currentPin); + + analogTrigger.attach_us(this, &MicroBitLightSensor::analogReady, MICROBIT_LIGHT_SENSOR_AN_SET_TIME); +} + +/** + * A destructor for MicroBitLightSensor. + * + * The destructor removes the listener, used by MicroBitLightSensor from the default EventModel. + */ +MicroBitLightSensor::~MicroBitLightSensor() +{ + if (EventModel::defaultEventBus) + EventModel::defaultEventBus->ignore(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_LIGHT_SENSE, this, &MicroBitLightSensor::startSensing); +} diff --git a/source/drivers/MicroBitAccelerometer.cpp b/source/drivers/MicroBitAccelerometer.cpp index 7b051da1..951d8d92 100644 --- a/source/drivers/MicroBitAccelerometer.cpp +++ b/source/drivers/MicroBitAccelerometer.cpp @@ -73,7 +73,7 @@ MicroBitAccelerometer::MicroBitAccelerometer(CoordinateSpace &cspace, uint16_t i * 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 i2c the bus to scan. * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER * */ @@ -168,7 +168,7 @@ int MicroBitAccelerometer::update() MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE); return MICROBIT_OK; -} +}; /** * A service function. diff --git a/source/drivers/MicroBitCompass.cpp b/source/drivers/MicroBitCompass.cpp index 15b10a19..0ce9442b 100644 --- a/source/drivers/MicroBitCompass.cpp +++ b/source/drivers/MicroBitCompass.cpp @@ -95,7 +95,7 @@ void MicroBitCompass::init(uint16_t id) * 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 i2c the bus to scan. * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER * */ diff --git a/source/drivers/MicroBitCompassCalibrator.cpp b/source/drivers/MicroBitCompassCalibrator.cpp index b64520e3..09f5f1b5 100644 --- a/source/drivers/MicroBitCompassCalibrator.cpp +++ b/source/drivers/MicroBitCompassCalibrator.cpp @@ -68,7 +68,7 @@ MicroBitCompassCalibrator::MicroBitCompassCalibrator(MicroBitCompass& _compass, * @param compass The compass instance to calibrate. * @param accelerometer The accelerometer to gather contextual data from. * @param display The LED matrix to display user feedback on. - * @param storage The object to use for storing calibration data in persistent FLASH. + * @param storage The object to use for storing calibration data in persistent FLASH. */ MicroBitCompassCalibrator::MicroBitCompassCalibrator(MicroBitCompass& _compass, MicroBitAccelerometer& _accelerometer, MicroBitDisplay& _display, MicroBitStorage &storage) : compass(_compass), accelerometer(_accelerometer), display(_display) { @@ -95,7 +95,7 @@ MicroBitCompassCalibrator::MicroBitCompassCalibrator(MicroBitCompass& _compass, * @param data a collection of data points * @param samples the number of samples in the 'data' array * - * @return The deviation between the closest and further point in the data array from the point given. + * @return The deviation between the closest and further point in the data array from the point given. */ float MicroBitCompassCalibrator::measureScore(Sample3D &c, Sample3D *data, int samples) { @@ -186,7 +186,7 @@ CompassCalibration MicroBitCompassCalibrator::spherify(Sample3D centre, Sample3D scale = max(scale, s); // next, determine the scale effect this has on each of our components. - float dx = (data[i].x - centre.x); + float dx = (data[i].x - centre.x); float dy = (data[i].y - centre.y); float dz = (data[i].z - centre.z); @@ -334,6 +334,7 @@ void MicroBitCompassCalibrator::calibrateUX(MicroBitEvent) if (remaining_scroll_time == MSG_TIME || remaining_scroll_time <= -REDISPLAY_MSG_TIMEOUT_MS) { display.clear(); display.scrollAsync("TILT TO FILL SCREEN "); // Takes about 14s + remaining_scroll_time = MSG_TIME; samples_this_period = 0; } @@ -406,7 +407,7 @@ void MicroBitCompassCalibrator::calibrateUX(MicroBitEvent) remaining_scroll_time-=TIME_STEP; } - CompassCalibration cal = calibrate(data, samples); + CompassCalibration cal = calibrate(data, samples); compass.setCalibration(cal); if(this->storage) diff --git a/source/drivers/MicroBitDisplay.cpp b/source/drivers/MicroBitDisplay.cpp index d0d7b844..cc2bc916 100644 --- a/source/drivers/MicroBitDisplay.cpp +++ b/source/drivers/MicroBitDisplay.cpp @@ -354,8 +354,7 @@ void MicroBitDisplay::updateScrollText() void MicroBitDisplay::updatePrintText() { image.print(printingChar < printingText.length() ? printingText.charAt(printingChar) : ' ',0,0); - - if (printingChar > printingText.length()) + if (printingChar >= printingText.length()) { animationMode = ANIMATION_MODE_NONE; @@ -448,7 +447,7 @@ void MicroBitDisplay::stopAnimation() void MicroBitDisplay::waitForFreeDisplay() { // If there's an ongoing animation, wait for our turn to display. - if (animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED) + while (animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED) fiber_wait_for_event(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE); } @@ -521,19 +520,19 @@ int MicroBitDisplay::printCharAsync(char c, int delay) */ int MicroBitDisplay::printAsync(ManagedString s, int delay) { - if (s.length() == 1) - return printCharAsync(s.charAt(0)); - //sanitise this value - if (delay <= 0 ) + if (delay < 0 || (delay == 0 && s.length() > 1)) return MICROBIT_INVALID_PARAMETER; + if (s.length() == 1) + return printCharAsync(s.charAt(0), delay); + if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED) { printingChar = 0; printingText = s; animationDelay = delay; - animationTick = 0; + animationTick = delay-1; animationMode = ANIMATION_MODE_PRINT_TEXT; } @@ -545,6 +544,33 @@ int MicroBitDisplay::printAsync(ManagedString s, int delay) return MICROBIT_OK; } +/** + * Prints the given ManagedString to the display, one character at a time. + * Returns immediately, and executes the animation asynchronously. + * + * If the string is greater than one charcter in length, the screen + * will be cleared after MICROBIT_DEFAULT_PRINT_SPEED milliseconds. + * Otherwise, that character will be left on the screen indefinitely. + * + * @param s The string to display. + * + * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. + * + * @code + * display.printAsync("abc123"); + * @endcode + */ +int MicroBitDisplay::printAsync(ManagedString s) +{ + int delay = MICROBIT_DEFAULT_PRINT_SPEED; + + if(s.length() == 1) + delay = 0; + + return printAsync(s, delay); +} + + /** * Prints the given image to the display, if the display is not in use. * Returns immediately, and executes the animation asynchronously. @@ -647,7 +673,7 @@ int MicroBitDisplay::printChar(char c, int delay) int MicroBitDisplay::print(ManagedString s, int delay) { //sanitise this value - if(delay <= 0 ) + if(delay < 0 || (delay == 0 && s.length() > 1)) return MICROBIT_INVALID_PARAMETER; // If there's an ongoing animation, wait for our turn to display. @@ -657,15 +683,11 @@ int MicroBitDisplay::print(ManagedString s, int delay) // If someone called stopAnimation(), then we simply skip... if (animationMode == ANIMATION_MODE_NONE) { + int ret = this->printAsync(s, delay); if (s.length() == 1) - { - return printCharAsync(s.charAt(0)); - } - else - { - this->printAsync(s, delay); - fiberWait(); - } + return ret; + + fiberWait(); } else { @@ -675,6 +697,34 @@ int MicroBitDisplay::print(ManagedString s, int delay) return MICROBIT_OK; } +/** + * Prints the given string to the display, one character at a time. + * + * Blocks the calling thread until all the text has been displayed. + * + * If the string is greater than one charcter in length, the screen + * will be cleared after MICROBIT_DEFAULT_PRINT_SPEED milliseconds. + * Otherwise, that character will be left on the screen indefinitely. + * + * @param s The string to display. + * + * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER. + * + * @code + * display.print("abc123"); + * @endcode + */ +int MicroBitDisplay::print(ManagedString s) +{ + int delay = MICROBIT_DEFAULT_PRINT_SPEED; + + if(s.length() == 1) + delay = 0; + + return print(s, delay); +} + + /** * Prints the given image to the display. * Blocks the calling thread until all the image has been displayed. diff --git a/source/drivers/MicroBitMessageBus.cpp b/source/drivers/MicroBitMessageBus.cpp index c2ad221e..f0bf46c4 100644 --- a/source/drivers/MicroBitMessageBus.cpp +++ b/source/drivers/MicroBitMessageBus.cpp @@ -95,14 +95,19 @@ void async_callback(void *param) // Queue this event up for later, if that's how we've been configured. if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) { +#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_LISTENERS) listener->queue(listener->evt); return; +#endif } } // Determine the calling convention for the callback, and invoke... // C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/ +#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_EVENTS) + listener->lock.wait(); +#endif // Record that we have a fiber going into this listener... listener->flags |= MESSAGE_BUS_LISTENER_BUSY; @@ -138,6 +143,10 @@ void async_callback(void *param) // The fiber of exiting... clear our state. listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY; + +#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_EVENTS) + listener->lock.notify(); +#endif } /** @@ -261,6 +270,13 @@ int MicroBitMessageBus::deleteMarkedListeners() return removed; } +MicroBitEvent last_event; +void process_sequentially(void *param) +{ + MicroBitMessageBus *m = (MicroBitMessageBus *)param; + m->process(last_event); +} + /** * Periodic callback from MicroBit. * @@ -278,7 +294,12 @@ void MicroBitMessageBus::idleTick() while (item) { // send the event to all standard event listeners. +#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_EVENTS) + last_event = item->evt; + invoke(process_sequentially,this); +#else this->process(item->evt); +#endif // Free the queue item. delete item; @@ -337,7 +358,7 @@ int MicroBitMessageBus::send(MicroBitEvent evt) * @note It is recommended that all external code uses the send() function instead of this function, * or the constructors provided by MicrobitEvent. */ -int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent) +int MicroBitMessageBus::process(MicroBitEvent evt, bool urgent) { MicroBitListener *l; int complete = 1; @@ -365,10 +386,12 @@ int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent) // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber // should the event handler attempt a blocking operation, but doesn't have the overhead // of creating a fiber needlessly. (cool huh?) - if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running()) - async_callback(l); - else +#if (MESSAGE_BUS_CONCURRENCY_MODE == MESSAGE_BUS_CONCURRENT_LISTENERS) + if (!(l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING) && fiber_scheduler_running()) invoke(async_callback, l); + else +#endif + async_callback(l); } else { diff --git a/source/drivers/MicroBitSerial.cpp b/source/drivers/MicroBitSerial.cpp index 7fad4e00..198caba4 100644 --- a/source/drivers/MicroBitSerial.cpp +++ b/source/drivers/MicroBitSerial.cpp @@ -295,7 +295,7 @@ void MicroBitSerial::send(MicroBitSerialMode mode) while(txBufferedSize() > 0); if(mode == SYNC_SLEEP) - fiber_sleep(0); + schedule(); } /**