From 8ea2e953c88e48cd0ff7621b1da4ca812dc04187 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 10 Sep 2015 00:04:27 +0100 Subject: [PATCH 1/4] microbit: Added C++ member function callback support into MicroBitMessageBus Event callbacks can now be transparently added to any C++ member function matching the MicroBitMessageBud signature: void fn(MicroBitEvent e) --- inc/MemberFunctionCallback.h | 77 ++++++++++++++++++ inc/MicroBit.h | 9 +- inc/MicroBitListener.h | 81 ++++++++++++++++++ inc/MicroBitMessageBus.h | 82 ++++++++++++------- source/CMakeLists.txt | 2 + source/MemberFunctionCallback.cpp | 32 ++++++++ source/MicroBitListener.cpp | 45 ++++++++++ source/MicroBitMessageBus.cpp | 131 +++++++++++++++++------------- 8 files changed, 367 insertions(+), 92 deletions(-) create mode 100644 inc/MemberFunctionCallback.h create mode 100644 inc/MicroBitListener.h create mode 100644 source/MemberFunctionCallback.cpp create mode 100644 source/MicroBitListener.cpp diff --git a/inc/MemberFunctionCallback.h b/inc/MemberFunctionCallback.h new file mode 100644 index 0000000..b2ada58 --- /dev/null +++ b/inc/MemberFunctionCallback.h @@ -0,0 +1,77 @@ +#ifndef MEMBER_FUNCTION_CALLBACK_H +#define MEMBER_FUNCTION_CALLBACK_H + +#include "mbed.h" +#include "MicroBitEvent.h" + +/** + * Class definition for a MemberFunctionCallback. + * + * C++ member functions (also known as methods) have a more complex + * representation than normal C functions. This allows a referene to + * a C++ member function to be stored then called at a later date. + * + * This class is used extensively by the MicroBitMessageBus to deliver + * events to C++ methods. + */ +class MemberFunctionCallback +{ + private: + void* object; + uint32_t method[4]; + void (*invoke)(void *object, uint32_t *method, MicroBitEvent e); + template static void methodCall(void* object, uint32_t*method, MicroBitEvent e); + + public: + + /** + * Constructor. Creates a MemberFunctionCallback based on a pointer to given method. + * @param object The object the callback method should be invooked on. + * @param method The method to invoke. + */ + template MemberFunctionCallback(T* object, void (T::*method)(MicroBitEvent e)); + + /** + * Comparison of two MemberFunctionCallback objects. + * @return TRUE if the given MemberFunctionCallback is equivalent to this one. FALSE otherwise. + */ + bool operator==(const MemberFunctionCallback &mfc); + + /** + * Calls the method reference held by this MemberFunctionCallback. + * @param e The event to deliver to the method + */ + void fire(MicroBitEvent e); +}; + +/** + * Constructor. Creates a representation of a pointer to a C++ member function (method). + * @param object The object the callback method should be invooked on. + * @param method The method to invoke. + */ +template +MemberFunctionCallback::MemberFunctionCallback(T* object, void (T::*method)(MicroBitEvent e)) +{ + this->object = object; + memclr(this->method, sizeof(method)); + memcpy(this->method, &method, sizeof(method)); + invoke = &MemberFunctionCallback::methodCall; +} + +/** + * Template to create static methods capable of invoking a C++ member function (method) + * based on the given paramters. + */ +template +void MemberFunctionCallback::methodCall(void *object, uint32_t *method, MicroBitEvent e) +{ + T* o = (T*)object; + void (T::*m)(MicroBitEvent); + memcpy(&m, method, sizeof(m)); + + (o->*m)(e); +} + +#endif + + diff --git a/inc/MicroBit.h b/inc/MicroBit.h index ca85a29..e48152b 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -4,10 +4,6 @@ #include "mbed.h" #include "MicroBitConfig.h" #include "MicroBitPanic.h" - -#include "ble/BLE.h" -#include "ble/services/DeviceInformationService.h" - #include "ErrorNo.h" #include "MicroBitHeapAllocator.h" @@ -15,9 +11,10 @@ #include "MicroBitFiber.h" #include "ManagedType.h" #include "ManagedString.h" + +#include "MicroBitEvent.h" #include "MicroBitFont.h" #include "MicroBitImage.h" -#include "MicroBitEvent.h" #include "MicroBitMessageBus.h" #include "DynamicPwm.h" #include "MicroBitComponent.h" @@ -31,6 +28,8 @@ #include "MicroBitCompass.h" #include "MicroBitAccelerometer.h" +#include "ble/BLE.h" +#include "ble/services/DeviceInformationService.h" #include "MicroBitDFUService.h" #include "MicroBitEventService.h" #include "ExternalEvents.h" diff --git a/inc/MicroBitListener.h b/inc/MicroBitListener.h new file mode 100644 index 0000000..7f60cfd --- /dev/null +++ b/inc/MicroBitListener.h @@ -0,0 +1,81 @@ +#ifndef MICROBIT_LISTENER_H +#define MICROBIT_LISTENER_H + +#include "mbed.h" +#include "MicroBitEvent.h" + +// MessageBusListener flags... +#define MESSAGE_BUS_LISTENER_PARAMETERISED 0x0001 +#define MESSAGE_BUS_LISTENER_METHOD 0x0002 +#define MESSAGE_BUS_LISTENER_REENTRANT 0x0004 +#define MESSAGE_BUS_LISTENER_BUSY 0x0008 + +struct MicroBitListener +{ + uint16_t id; // The ID of the component that this listener is interested in. + uint16_t value; // Value this listener is interested in receiving. + uint16_t flags; // Status and configuration options codes for this listener. + + union + { + void (*cb)(MicroBitEvent); + void (*cb_param)(MicroBitEvent, void *); + MemberFunctionCallback *cb_method; + }; + + void* cb_arg; // Optional argument to be passed to the caller. + + MicroBitEvent evt; + + MicroBitListener *next; + + /** + * Constructor. + * Create a new Message Bus Listener. + * @param id The ID of the component you want to listen to. + * @param value The event ID you would like to listen to from that component + * @param handler A function pointer to call when the event is detected. + */ + MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)); + + /** + * Alternative constructor where we register a value to be passed to the + * callback. + */ + MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg); + + /** + * Constructor. + * Create a new Message Bus Listener, with a callback to a c++ member function. + * @param id The ID of the component you want to listen to. + * @param value The event ID you would like to listen to from that component. + * @param object The C++ object on which to call the event handler. + * @param object The method within the C++ object to call. + */ + template + MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent)); +}; + +/** + * Constructor. + * Create a new Message Bus Listener, with a callback to a c++ member function. + * @param id The ID of the component you want to listen to. + * @param value The event ID you would like to listen to from that component. + * @param object The C++ object on which to call the event handler. + * @param object The method within the C++ object to call. + */ + +template +MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent)) +{ + this->id = id; + this->value = value; + this->cb_method = new MemberFunctionCallback(object, method); + this->cb_arg = NULL; + this->flags = MESSAGE_BUS_LISTENER_METHOD; + this->next = NULL; +} + +#endif + + diff --git a/inc/MicroBitMessageBus.h b/inc/MicroBitMessageBus.h index 2106c7a..ca3069d 100644 --- a/inc/MicroBitMessageBus.h +++ b/inc/MicroBitMessageBus.h @@ -2,6 +2,8 @@ #define MICROBIT_MESSAGE_BUS_H #include "mbed.h" +#include "MemberFunctionCallback.h" +#include "MicroBitListener.h" #include "MicroBitComponent.h" #include "MicroBitEvent.h" @@ -10,33 +12,9 @@ #define MICROBIT_ID_ANY 0 #define MICROBIT_EVT_ANY 0 -struct MicroBitListener -{ - uint16_t id; // The ID of the component that this listener is interested in. - uint16_t value; // Value this listener is interested in receiving. - void* cb; // Callback function associated with this listener. Either (*cb)(MicroBitEvent) or (*cb)(MicroBitEvent, void*) depending on whether cb_arg is NULL. - void* cb_arg; // Argument to be passed to the caller. This is assumed to be a pointer, so passing in NULL means that the function doesn't take an argument. - MicroBitEvent evt; - - MicroBitListener *next; - - /** - * Constructor. - * Create a new Message Bus Listener. - * @param id The ID of the component you want to listen to. - * @param value The event ID you would like to listen to from that component - * @param handler A function pointer to call when the event is detected. - */ - MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)); - - /** - * Alternative constructor where we register a value to be passed to the - * callback. If arg == NULL, the function takes no extra arguemnt. - * Otherwise, the function is understood to take an extra argument. - */ - MicroBitListener(uint16_t id, uint16_t value, void *handler, void* arg); -}; - +/** + * Enclosing class to hold a chain of events. + */ struct MicroBitEventQueueItem { MicroBitEvent evt; @@ -50,6 +28,7 @@ struct MicroBitEventQueueItem MicroBitEventQueueItem(MicroBitEvent evt); }; + /** * Class definition for the MicroBitMessageBus. * @@ -136,14 +115,28 @@ class MicroBitMessageBus : public MicroBitComponent */ void listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg); + /** + * Register a listener function. + * + * As above, but allows callbacks into member functions within a C++ object. + * This one is a bit more complex, but hey, that's C++ for you! + */ + template + void listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)); + private: - + + /** + * Add the given MicroBitListener to the list of event handlers, unconditionally. + * @param listener The MicroBitListener to validate. + * @return 1 if the listener is valid, 0 otherwise. + */ + int add(MicroBitListener *newListener); + 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. - int seq; // Sequence number. Used to invalidate cache entries. - void listen(int id, int value, void* handler, void* arg); void queueEvent(MicroBitEvent &evt); MicroBitEventQueueItem* dequeueEvent(); @@ -151,6 +144,35 @@ class MicroBitMessageBus : public MicroBitComponent virtual int isIdleCallbackNeeded(); }; +/** + * A registrationt function to allow C++ member funcitons (methods) to be registered as an event + * listener. + * + * @param id The source of messages to listen for. Events sent from any other IDs will be filtered. + * Use MICROBIT_ID_ANY to receive events from all components. + * + * @param value The value of messages to listen for. Events with any other values will be filtered. + * Use MICROBIT_EVT_ANY to receive events of any value. + * + * @param object The object on which the method should be invoked. + * @param hander The method to call when an event is received. + */ +template +void MicroBitMessageBus::listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)) +{ + if (object == NULL || handler == NULL) + return; + + MicroBitListener *newListener = new MicroBitListener(id, value, object, handler); + + if(!add(newListener)) + { + delete newListener->cb_method; + delete newListener; + return; + } +} + #endif diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e23a9bc..abb249c 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -28,6 +28,8 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "MicroBitPin.cpp" "MicroBitSerial.cpp" "MicroBitHeapAllocator.cpp" + "MicroBitListener.cpp" + "MemberFunctionCallback.cpp" ) if (YOTTA_CFG_MICROBIT_CONFIGFILE) diff --git a/source/MemberFunctionCallback.cpp b/source/MemberFunctionCallback.cpp new file mode 100644 index 0000000..d594165 --- /dev/null +++ b/source/MemberFunctionCallback.cpp @@ -0,0 +1,32 @@ +/** + * Class definition for a MemberFunctionCallback. + * + * C++ member functions (also known as methods) have a more complex + * representation than normal C functions. This allows a referene to + * a C++ member function to be stored then called at a later date. + * + * This class is used extensively by the MicroBitMessageBus to deliver + * events to C++ methods. + */ + +#include "MicroBit.h" + +/** + * Calls the method reference held by this MemberFunctionCallback. + * @param e The event to deliver to the method + */ +void MemberFunctionCallback::fire(MicroBitEvent e) +{ + invoke(object, method, e); +} + +/** + * Comparison of two MemberFunctionCallback objects. + * @return TRUE if the given MemberFunctionCallback is equivalent to this one. FALSE otherwise. + */ +bool MemberFunctionCallback::operator==(const MemberFunctionCallback &mfc) +{ + return (object == mfc.object && memcmp(method,mfc.method,sizeof(method))==0); +} + + diff --git a/source/MicroBitListener.cpp b/source/MicroBitListener.cpp new file mode 100644 index 0000000..933ab55 --- /dev/null +++ b/source/MicroBitListener.cpp @@ -0,0 +1,45 @@ +/** + * Class definition for a MicroBitListener. + * + * MicroBitListener holds all the information related to a single event handler required + * to match and fire event handlers to incoming events. + */ + +#include "mbed.h" +#include "MicroBit.h" + +/** + * Constructor. + * Create a new Message Bus Listener. + * @param id The ID of the component you want to listen to. + * @param value The event ID you would like to listen to from that component + * @param handler A function pointer to call when the event is detected. + */ +MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)) +{ + this->id = id; + this->value = value; + this->cb = handler; + this->cb_arg = NULL; + this->flags = 0; + this->next = NULL; +} + +/** + * Constructor. + * Create a new parameterised Message Bus Listener. + * @param id The ID of the component you want to listen to. + * @param value The event ID you would like to listen to from that component. + * @param handler A function pointer to call when the event is detected. + * @param arg An additional argument to pass to the event handler function. + */ +MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg) +{ + this->id = id; + this->value = value; + this->cb_param = handler; + this->cb_arg = arg; + this->flags = MESSAGE_BUS_LISTENER_PARAMETERISED; + this->next = NULL; +} + diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index d48fa5f..d59582c 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -16,34 +16,7 @@ MicroBitEventQueueItem::MicroBitEventQueueItem(MicroBitEvent evt) this->evt = evt; this->next = NULL; } -/** - * Constructor. - * Create a new Message Bus Listener. - * @param id The ID of the component you want to listen to. - * @param value The event ID you would like to listen to from that component - * @param handler A function pointer to call when the event is detected. - */ -MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)) -{ - this->id = id; - this->value = value; - this->cb = (void*) handler; - this->cb_arg = NULL; - this->next = NULL; -} -/** - * A low-level, internal version of the constructor, where the handler's type is determined - * by the value of arg. (See MicroBitMessageBus.h). - */ -MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void* handler, void* arg) -{ - this->id = id; - this->value = value; - this->cb = handler; - this->cb_arg = arg; - this->next = NULL; -} /** * Constructor. @@ -65,10 +38,21 @@ MicroBitMessageBus::MicroBitMessageBus() void async_callback(void *param) { MicroBitListener *listener = (MicroBitListener *)param; - if (listener->cb_arg != NULL) - ((void (*)(MicroBitEvent, void*))listener->cb)(listener->evt, listener->cb_arg); + + // 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 :-/ + + // Firstly, check for a method callback into an object. + if (listener->flags & MESSAGE_BUS_LISTENER_METHOD) + listener->cb_method->fire(listener->evt); + + // Now a parameterised C function + else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED) + listener->cb_param(listener->evt, listener->cb_arg); + + // We must have a plain C function else - ((void (*)(MicroBitEvent))listener->cb)(listener->evt); + listener->cb(listener->evt); } @@ -243,8 +227,6 @@ void MicroBitMessageBus::process(MicroBitEvent evt) * * @param hander The function to call when an event is received. * - * TODO: We currently don't support C++ member functions as callbacks, which we should. - * * Example: * @code * void onButtonBClick(MicroBitEvent evt) @@ -255,75 +237,110 @@ void MicroBitMessageBus::process(MicroBitEvent evt) * @endcode */ -void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg) { - this->listen(id, value, (void*) handler, arg); -} - -void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent)) { - this->listen(id, value, (void*) handler, NULL); -} - -void MicroBitMessageBus::listen(int id, int value, void* handler, void* arg) +void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent)) { - //handler can't be NULL! if (handler == NULL) return; + MicroBitListener *newListener = new MicroBitListener(id, value, handler); + + if(!add(newListener)) + { + delete newListener; + return; + } +} + +void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg) +{ + if (handler == NULL) + return; + + MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg); + + if(!add(newListener)) + { + delete newListener; + return; + } +} + + +/** + * Add the given MicroBitListener to the list of event handlers, unconditionally. + * @param listener The MicroBitListener to validate. + * @return 1 if the listener is valid, 0 otherwise. + */ +int MicroBitMessageBus::add(MicroBitListener *newListener) +{ MicroBitListener *l, *p; + int methodCallback; + + //handler can't be NULL! + if (newListener == NULL) + return 0; + + methodCallback = newListener->flags & MESSAGE_BUS_LISTENER_METHOD; l = listeners; // Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler // registered in a that will already capture these events. If we do, silently ignore. - while (l != NULL) - { - if (l->id == id && l->value == value && l->cb == handler) - return; - l = l->next; - } + // We always check the ID, VALUE and CB_METHOD fields. + // If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient. + while (l != NULL) + { + if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb)) + return 0; - MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg); + l = l->next; + } - //if listeners is null - we can automatically add this listener to the list at the beginning... + // We have a valid, new event handler. Add it to the list. + // if listeners is null - we can automatically add this listener to the list at the beginning... if (listeners == NULL) { listeners = newListener; - return; + return 1; } - // Maintain an ordered list of listeners. - // Chain is held stictly in increasing order of ID (first level), then value code (second level). + // We maintain an ordered list of listeners. + // The chain is held stictly in increasing order of ID (first level), then value code (second level). // Find the correct point in the chain for this event. - // Adding a listener is a rare occurance, so we just walk the list. + // Adding a listener is a rare occurance, so we just walk the list... + p = listeners; l = listeners; - while (l != NULL && l->id < id) + while (l != NULL && l->id < newListener->id) { p = l; l = l->next; } - while (l != NULL && l->id == id && l->value < value) + while (l != NULL && l->id == newListener->id && l->value < newListener->value) { p = l; l = l->next; } //add at front of list - if (p == listeners && (id < p->id || (p->id == id && p->value > value))) + if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value))) { newListener->next = p; //this new listener is now the front! listeners = newListener; } + //add after p else { newListener->next = p->next; p->next = newListener; } + + return 1; } From 51493092ff80edff283e3780857ac528d80a106a Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Thu, 10 Sep 2015 12:53:39 +0100 Subject: [PATCH 2/4] microbit: Added support to remove MicroBitListener event handlers. --- inc/MemberFunctionCallback.h | 2 +- inc/MicroBitListener.h | 5 ++ inc/MicroBitMessageBus.h | 139 ++++++++++++++++++++++++++++-- source/MemberFunctionCallback.cpp | 3 +- source/MicroBitListener.cpp | 8 ++ source/MicroBitMessageBus.cpp | 114 ++++++++++++++++++++++-- 6 files changed, 253 insertions(+), 18 deletions(-) diff --git a/inc/MemberFunctionCallback.h b/inc/MemberFunctionCallback.h index b2ada58..308c383 100644 --- a/inc/MemberFunctionCallback.h +++ b/inc/MemberFunctionCallback.h @@ -53,7 +53,7 @@ template MemberFunctionCallback::MemberFunctionCallback(T* object, void (T::*method)(MicroBitEvent e)) { this->object = object; - memclr(this->method, sizeof(method)); + memclr(this->method, sizeof(this->method)); memcpy(this->method, &method, sizeof(method)); invoke = &MemberFunctionCallback::methodCall; } diff --git a/inc/MicroBitListener.h b/inc/MicroBitListener.h index 7f60cfd..784dba8 100644 --- a/inc/MicroBitListener.h +++ b/inc/MicroBitListener.h @@ -54,6 +54,11 @@ struct MicroBitListener */ template MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent)); + + /** + * Destructor. Ensures all resources used by this listener are freed. + */ + ~MicroBitListener(); }; /** diff --git a/inc/MicroBitMessageBus.h b/inc/MicroBitMessageBus.h index ca3069d..f6b1465 100644 --- a/inc/MicroBitMessageBus.h +++ b/inc/MicroBitMessageBus.h @@ -110,20 +110,116 @@ class MicroBitMessageBus : public MicroBitComponent /** * Register a listener function. * - * Same as above, except the listener function is passed an extra argument in addition to the - * MicroBitEvent, when called. + * @param id The source of messages to listen for. Events sent from any other IDs will be filtered. + * Use MICROBIT_ID_ANY to receive events from all components. + * + * @param value The value of messages to listen for. Events with any other values will be filtered. + * Use MICROBIT_EVT_ANY to receive events of any value. + * + * @param hander The function to call when an event is received. + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); // call function when ever a click event is detected. + * @endcode */ void listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg); /** * Register a listener function. + * + * @param id The source of messages to listen for. Events sent from any other IDs will be filtered. + * Use MICROBIT_ID_ANY to receive events from all components. + * + * @param value The value of messages to listen for. Events with any other values will be filtered. + * Use MICROBIT_EVT_ANY to receive events of any value. + * + * @param hander The function to call when an event is received. + * + * Example: + * @code + * void SomeClass::onButtonBClick() + * { + * //do something + * } * - * As above, but allows callbacks into member functions within a C++ object. - * This one is a bit more complex, but hey, that's C++ for you! + * SomeClass s = new SomeClass(); + * uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick); + * @endcode */ template void listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)); + + /** + * Unregister a listener function. + * Listners are identified by the Event ID, Event VALUE and handler registered using listen(). + * + * @param id The Event ID used to register the listener. + * @param value The Event VALUE used to register the listener. + * @param handler The function used to register the listener. + * + * + * Example: + * @code + * void onButtonBClick() + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ + void ignore(int id, int value, void (*handler)(MicroBitEvent)); + + /** + * Unregister a listener function. + * Listners are identified by the Event ID, Event VALUE and handler registered using listen(). + * + * @param id The Event ID used to register the listener. + * @param value The Event VALUE used to register the listener. + * @param handler The function used to register the listener. + * + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ + void ignore(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg); + + /** + * Unregister a listener function. + * Listners are identified by the Event ID, Event VALUE and handler registered using listen(). + * + * @param id The Event ID used to register the listener. + * @param value The Event VALUE used to register the listener. + * @param handler The function used to register the listener. + * + * + * Example: + * @code + * + * void SomeClass::onButtonBClick() + * { + * //do something + * } + * + * SomeClass s = new SomeClass(); + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick); + * @endcode + */ + template + void ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)); private: /** @@ -132,6 +228,7 @@ class MicroBitMessageBus : public MicroBitComponent * @return 1 if the listener is valid, 0 otherwise. */ int add(MicroBitListener *newListener); + int remove(MicroBitListener *newListener); MicroBitListener *listeners; // Chain of active listeners. MicroBitEventQueueItem *evt_queue_head; // Head of queued events to be processed. @@ -166,13 +263,39 @@ void MicroBitMessageBus::listen(uint16_t id, uint16_t value, T* object, void (T: MicroBitListener *newListener = new MicroBitListener(id, value, object, handler); if(!add(newListener)) - { - delete newListener->cb_method; delete newListener; - return; - } } +/** + * Unregister a listener function. + * Listners are identified by the Event ID, Event VALUE and handler registered using listen(). + * + * @param id The Event ID used to register the listener. + * @param value The Event VALUE used to register the listener. + * @param handler The function used to register the listener. + * + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ +template +void MicroBitMessageBus::ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)) +{ + if (handler == NULL) + return; + + MicroBitListener listener(id, value, object, handler); + remove(&listener); +} + + #endif diff --git a/source/MemberFunctionCallback.cpp b/source/MemberFunctionCallback.cpp index d594165..f67f626 100644 --- a/source/MemberFunctionCallback.cpp +++ b/source/MemberFunctionCallback.cpp @@ -26,7 +26,6 @@ void MemberFunctionCallback::fire(MicroBitEvent e) */ bool MemberFunctionCallback::operator==(const MemberFunctionCallback &mfc) { - return (object == mfc.object && memcmp(method,mfc.method,sizeof(method))==0); + return (object == mfc.object && (memcmp(method,mfc.method,sizeof(method))==0)); } - diff --git a/source/MicroBitListener.cpp b/source/MicroBitListener.cpp index 933ab55..cbe56ac 100644 --- a/source/MicroBitListener.cpp +++ b/source/MicroBitListener.cpp @@ -43,3 +43,11 @@ MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)( this->next = NULL; } +/** + * Destructor. Ensures all resources used by this listener are freed. + */ +MicroBitListener::~MicroBitListener() +{ + if(this->flags & MESSAGE_BUS_LISTENER_METHOD) + delete cb_method; +} diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index d59582c..7b8b9d0 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -245,10 +245,7 @@ void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent MicroBitListener *newListener = new MicroBitListener(id, value, handler); if(!add(newListener)) - { delete newListener; - return; - } } void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg) @@ -259,10 +256,63 @@ void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg); if(!add(newListener)) - { delete newListener; - return; - } +} + +/** + * Unregister a listener function. + * Listners are identified by the Event ID, Event VALUE and handler registered using listen(). + * + * @param id The Event ID used to register the listener. + * @param value The Event VALUE used to register the listener. + * @param handler The function used to register the listener. + * + * + * Example: + * @code + * void onButtonBClick() + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ +void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent)) +{ + if (handler == NULL) + return; + + MicroBitListener listener(id, value, handler); + remove(&listener); +} + +/** + * Unregister a listener function. + * Listners are identified by the Event ID, Event VALUE and handler registered using listen(). + * + * @param id The Event ID used to register the listener. + * @param value The Event VALUE used to register the listener. + * @param handler The function used to register the listener. + * + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ +void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg) +{ + if (handler == NULL) + return; + + MicroBitListener listener(id, value, handler, arg); + remove(&listener); } @@ -280,7 +330,6 @@ int MicroBitMessageBus::add(MicroBitListener *newListener) if (newListener == NULL) return 0; - methodCallback = newListener->flags & MESSAGE_BUS_LISTENER_METHOD; l = listeners; @@ -291,6 +340,8 @@ int MicroBitMessageBus::add(MicroBitListener *newListener) // If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient. while (l != NULL) { + methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD); + if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb)) return 0; @@ -344,3 +395,52 @@ int MicroBitMessageBus::add(MicroBitListener *newListener) return 1; } +/** + * Remove a MicroBitListener from the list that matches the given listener. + * @param listener The MicroBitListener to validate. + * @return The number of listeners removed from the list. + */ +int MicroBitMessageBus::remove(MicroBitListener *listener) +{ + MicroBitListener *l, *p; + int removed = 0; + + //handler can't be NULL! + if (listener == NULL) + return 0; + + l = listeners; + p = NULL; + + // Walk this list of event handlers. Delete any that match the given listener. + while (l != NULL) + { + if (l->id == listener->id && l->value == listener->value && ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD))) + { + if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) || + ((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb))) + { + // Found a match. Remove from the list. + if (p == NULL) + listeners = l->next; + else + p->next = l->next; + + // delete the listener. + MicroBitListener *t = l; + l = l->next; + + delete t; + removed++; + + continue; + } + } + + p = l; + l = l->next; + } + + return removed; +} + From a875d5fccd6031513761eced6ca56bded0d4f746 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Fri, 11 Sep 2015 16:39:38 +0100 Subject: [PATCH 3/4] microbit: Added configurable concurrency modes for MicroBitMessageBus handlers. MessageBus handlers can now have one of four concurrency modes for the eventuality of an event being raised whilst a previous event is still being processed. An additional (optional) parameter is provided to the listen() functions to allow this to be selected on a per event handler basis. The permissable options are: MESSAGE_BUS_LISTENER_REENTRANT: The event handler is fired with the new event, regardless of whether or not a previous event is still be processed by that handler. MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY: The new event is queued until such a time as the previous event has completed execution. The new event is then processed. This option does not preclude the processing of the new event by other event handlers. MESSAGE_BUS_LISTENER_DROP_IF_BUSY: The new event is dropped, and will never be processed the the event handler. This option does not preclude the processing of the new event by other event handlers. MESSAGE_BUS_LISTENER_NONBLOCKING: The event handler is self-declaring that it never blocks. This flag is used purely for optimisation, as it permits direct execution of the event hadnelr without inducing any overhead from the scheduler. In addition, the following minor revisions were made in this release: * Cleanup of the #include dependencies contained in the microbit-dal .h files * Bugfix to the scheduler block on event matching code. * Introduced a MICROBIT_ID_ALERT MessageBus channel, for general purpose eventing using nonces. --- inc/MicroBit.h | 61 ++++------------ inc/MicroBitAccelerometer.h | 1 + inc/MicroBitButton.h | 2 + inc/MicroBitCompass.h | 1 + inc/MicroBitComponent.h | 35 +++++++++ inc/MicroBitConfig.h | 13 ++++ inc/MicroBitDFUService.h | 4 +- inc/MicroBitDisplay.h | 8 +- inc/MicroBitEvent.h | 16 +++- inc/MicroBitEventService.h | 2 +- inc/MicroBitFiber.h | 2 +- inc/MicroBitI2C.h | 2 - inc/MicroBitIO.h | 17 ++--- inc/MicroBitListener.h | 27 +++++-- inc/MicroBitMatrixMaps.h | 2 + inc/MicroBitMessageBus.h | 37 ++++------ inc/MicroBitPin.h | 2 +- inc/MicroBitSerial.h | 4 +- source/DynamicPwm.cpp | 4 +- source/ManagedString.cpp | 4 +- source/MicroBitDisplay.cpp | 45 +++++++----- source/MicroBitEvent.cpp | 12 +++ source/MicroBitEventService.cpp | 2 - source/MicroBitFiber.cpp | 4 +- source/MicroBitI2C.cpp | 2 +- source/MicroBitIO.cpp | 54 +++++++------- source/MicroBitListener.cpp | 28 ++++++- source/MicroBitMessageBus.cpp | 126 ++++++++++++++++++++------------ source/MicroBitSerial.cpp | 2 +- 29 files changed, 315 insertions(+), 204 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index e48152b..d7ad736 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -2,31 +2,34 @@ #define MICROBIT_H #include "mbed.h" -#include "MicroBitConfig.h" + +#include "MicroBitConfig.h" +#include "MicroBitHeapAllocator.h" #include "MicroBitPanic.h" #include "ErrorNo.h" - -#include "MicroBitHeapAllocator.h" #include "MicroBitCompat.h" -#include "MicroBitFiber.h" +#include "MicroBitComponent.h" #include "ManagedType.h" #include "ManagedString.h" - -#include "MicroBitEvent.h" +#include "MicroBitImage.h" #include "MicroBitFont.h" -#include "MicroBitImage.h" -#include "MicroBitMessageBus.h" +#include "MicroBitEvent.h" #include "DynamicPwm.h" -#include "MicroBitComponent.h" #include "MicroBitI2C.h" -#include "MicroBitSerial.h" +#include "MESEvents.h" + #include "MicroBitButton.h" -#include "MicroBitMultiButton.h" -#include "MicroBitDisplay.h" #include "MicroBitPin.h" -#include "MicroBitIO.h" #include "MicroBitCompass.h" #include "MicroBitAccelerometer.h" +#include "MicroBitMultiButton.h" + +#include "MicroBitSerial.h" +#include "MicroBitIO.h" +#include "MicroBitDisplay.h" + +#include "MicroBitFiber.h" +#include "MicroBitMessageBus.h" #include "ble/BLE.h" #include "ble/services/DeviceInformationService.h" @@ -43,38 +46,6 @@ // Random number generator #define NRF51822_RNG_ADDRESS 0x4000D000 -#define MICROBIT_IO_PINS 20 - -// 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 -#define MICROBIT_ID_IO_P0 7 //P0 is the left most pad (ANALOG/DIGITAL) -#define MICROBIT_ID_IO_P1 8 //P1 is the middle pad (ANALOG/DIGITAL) -#define MICROBIT_ID_IO_P2 9 //P2 is the right most pad (ANALOG/DIGITAL) -#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 -#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 -#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 -#define MICROBIT_ID_IO_P19 24 //SCL -#define MICROBIT_ID_IO_P20 25 //SDA - -#define MICROBIT_ID_BUTTON_AB 26 // Button A+B multibutton // mBed pin assignments of core components. #define MICROBIT_PIN_SDA P0_30 diff --git a/inc/MicroBitAccelerometer.h b/inc/MicroBitAccelerometer.h index a4cda4a..793cd9d 100644 --- a/inc/MicroBitAccelerometer.h +++ b/inc/MicroBitAccelerometer.h @@ -2,6 +2,7 @@ #define MICROBIT_ACCELEROMETER_H #include "mbed.h" +#include "MicroBitComponent.h" /** * Relevant pin assignments diff --git a/inc/MicroBitButton.h b/inc/MicroBitButton.h index 32eb20e..231cadd 100644 --- a/inc/MicroBitButton.h +++ b/inc/MicroBitButton.h @@ -2,6 +2,8 @@ #define MICROBIT_BUTTON_H #include "mbed.h" +#include "MicroBitComponent.h" +#include "MicroBitEvent.h" //TODO: When platform is built for MB2 - pins will be defined by default, these will change... #define MICROBIT_PIN_BUTTON_A P0_17 diff --git a/inc/MicroBitCompass.h b/inc/MicroBitCompass.h index 6b24ad2..3863f71 100644 --- a/inc/MicroBitCompass.h +++ b/inc/MicroBitCompass.h @@ -2,6 +2,7 @@ #define MICROBIT_COMPASS_H #include "mbed.h" +#include "MicroBitComponent.h" /** * Relevant pin assignments diff --git a/inc/MicroBitComponent.h b/inc/MicroBitComponent.h index 992e38c..7779069 100644 --- a/inc/MicroBitComponent.h +++ b/inc/MicroBitComponent.h @@ -8,6 +8,41 @@ * If it's in the systemTick queue, you should override systemTick and implement the required functionality. * Similarly if the component is in the idleTick queue, the idleTick member function should be overridden. */ + +// 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 +#define MICROBIT_IO_PINS 20 + +#define MICROBIT_ID_IO_P0 7 //P0 is the left most pad (ANALOG/DIGITAL) +#define MICROBIT_ID_IO_P1 8 //P1 is the middle pad (ANALOG/DIGITAL) +#define MICROBIT_ID_IO_P2 9 //P2 is the right most pad (ANALOG/DIGITAL) +#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 +#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 +#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 +#define MICROBIT_ID_IO_P19 24 //SCL +#define MICROBIT_ID_IO_P20 25 //SDA + +#define MICROBIT_ID_BUTTON_AB 26 // Button A+B multibutton +#define MICROBIT_ID_ALERT 27 // Alert channel, used for general purpose condition synchronisation and alerting. + class MicroBitComponent { protected: diff --git a/inc/MicroBitConfig.h b/inc/MicroBitConfig.h index 3ea8f74..c2cdcdd 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -77,6 +77,19 @@ #define FIBER_TICK_PERIOD_MS 6 #endif +// +// Message Bus: +// Default behaviour for event handlers, if not specified in the listen() call +// +// Permissable values are: +// MESSAGE_BUS_LISTENER_REENTRANT +// MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY +// MESSAGE_BUS_LISTENER_DROP_IF_BUSY +// MESSAGE_BUS_LISTENER_NONBLOCKING + +#ifndef MESSAGE_BUS_LISTENER_DEFAULT_FLAGS +#define MESSAGE_BUS_LISTENER_DEFAULT_FLAGS MESSAGE_BUS_LISTENER_REENTRANT +#endif // // Core micro:bit services diff --git a/inc/MicroBitDFUService.h b/inc/MicroBitDFUService.h index 99c24ab..eaa5850 100644 --- a/inc/MicroBitDFUService.h +++ b/inc/MicroBitDFUService.h @@ -1,8 +1,8 @@ #ifndef MICROBIT_DFU_SERVICE_H #define MICROBIT_DFU_SERVICE_H -#include "MicroBit.h" - +#include "mbed.h" +#include "ble/BLE.h" // MicroBit ControlPoint OpCodes // Requests transfer to the Nordic DFU bootloader. diff --git a/inc/MicroBitDisplay.h b/inc/MicroBitDisplay.h index 7ab3ceb..c3b5fcd 100644 --- a/inc/MicroBitDisplay.h +++ b/inc/MicroBitDisplay.h @@ -63,7 +63,10 @@ #define MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS -255 #include "mbed.h" -#include "MicroBit.h" +#include "ManagedString.h" +#include "MicroBitComponent.h" +#include "MicroBitImage.h" +#include "MicroBitFont.h" enum AnimationMode { ANIMATION_MODE_NONE, @@ -102,6 +105,7 @@ class MicroBitDisplay : public MicroBitComponent uint8_t mode; uint8_t greyscaleBitMsk; uint8_t timingCount; + uint16_t nonce; Timeout renderTimer; MicroBitFont font; @@ -217,7 +221,7 @@ class MicroBitDisplay : public MicroBitComponent * Broadcasts an event onto the shared MessageBus * @param eventCode The ID of the event that has occurred. */ - void sendEvent(uint16_t eventcode); + void sendAnimationCompleteEvent(); public: diff --git a/inc/MicroBitEvent.h b/inc/MicroBitEvent.h index 6537e3f..d9c05c9 100644 --- a/inc/MicroBitEvent.h +++ b/inc/MicroBitEvent.h @@ -1,7 +1,7 @@ #ifndef MICROBIT_EVENT_H #define MICROBIT_EVENT_H -#include "MicroBit.h" +#include "mbed.h" /** * Class definition for a MicrobitEvent @@ -43,7 +43,21 @@ class MicroBitEvent void fire(); }; +/** + * Enclosing class to hold a chain of events. + */ +struct MicroBitEventQueueItem +{ + MicroBitEvent evt; + MicroBitEventQueueItem *next; + /** + * Constructor. + * Creates a new MicroBitEventQueueItem. + * @param evt The event that is to be queued. + */ + MicroBitEventQueueItem(MicroBitEvent evt); +}; #endif diff --git a/inc/MicroBitEventService.h b/inc/MicroBitEventService.h index 649ff3e..1c751ca 100644 --- a/inc/MicroBitEventService.h +++ b/inc/MicroBitEventService.h @@ -1,7 +1,7 @@ #ifndef MICROBIT_EVENT_SERVICE_H #define MICROBIT_EVENT_SERVICE_H -#include "MicroBit.h" +#include "MicroBitEvent.h" // UUIDs for our service and characteristics extern const uint8_t MicroBitEventServiceUUID[]; diff --git a/inc/MicroBitFiber.h b/inc/MicroBitFiber.h index 218b501..8e31f99 100644 --- a/inc/MicroBitFiber.h +++ b/inc/MicroBitFiber.h @@ -12,7 +12,7 @@ #include "mbed.h" #include "MicroBitConfig.h" -#include "MicroBitMessageBus.h" +#include "MicroBitEvent.h" // TODO: Consider a split mode scheduler, that monitors used stack size, and maintains a dedicated, persistent // stack for any long lived fibers with large stack diff --git a/inc/MicroBitI2C.h b/inc/MicroBitI2C.h index edcfd65..84303fb 100644 --- a/inc/MicroBitI2C.h +++ b/inc/MicroBitI2C.h @@ -1,8 +1,6 @@ -#include "MicroBit.h" #ifndef MICROBIT_I2C_H #define MICROBIT_I2C_H -#include "MicroBitComponent.h" #include "mbed.h" #define MICROBIT_I2C_MAX_RETRIES 9 diff --git a/inc/MicroBitIO.h b/inc/MicroBitIO.h index 5080d21..ac3cc4a 100644 --- a/inc/MicroBitIO.h +++ b/inc/MicroBitIO.h @@ -2,6 +2,7 @@ #define MICROBIT_IO_H #include "mbed.h" +#include "MicroBitComponent.h" #include "MicroBitPin.h" /** @@ -37,15 +38,13 @@ class MicroBitIO * Constructor. * Create a representation of all given I/O pins on the edge connector */ - MicroBitIO(int MICROBIT_ID_IO_P0, int MICROBIT_ID_IO_P1, int MICROBIT_ID_IO_P2, - int MICROBIT_ID_IO_P3, int MICROBIT_ID_IO_P4, int MICROBIT_ID_IO_P5, - int MICROBIT_ID_IO_P6, int MICROBIT_ID_IO_P7, int MICROBIT_ID_IO_P8, - int MICROBIT_ID_IO_P9, int MICROBIT_ID_IO_P10,int MICROBIT_ID_IO_P11, - int MICROBIT_ID_IO_P12,int MICROBIT_ID_IO_P13,int MICROBIT_ID_IO_P14, - int MICROBIT_ID_IO_P15,int MICROBIT_ID_IO_P16,int MICROBIT_ID_IO_P19, - int MICROBIT_ID_IO_P20); - - + MicroBitIO(int ID_P0, int ID_P1, int ID_P2, + int ID_P3, int ID_P4, int ID_P5, + int ID_P6, int ID_P7, int ID_P8, + int ID_P9, int ID_P10,int ID_P11, + int ID_P12,int ID_P13,int ID_P14, + int ID_P15,int ID_P16,int ID_P19, + int ID_P20); }; #endif diff --git a/inc/MicroBitListener.h b/inc/MicroBitListener.h index 784dba8..176dd0c 100644 --- a/inc/MicroBitListener.h +++ b/inc/MicroBitListener.h @@ -3,12 +3,16 @@ #include "mbed.h" #include "MicroBitEvent.h" +#include "MemberFunctionCallback.h" // MessageBusListener flags... #define MESSAGE_BUS_LISTENER_PARAMETERISED 0x0001 #define MESSAGE_BUS_LISTENER_METHOD 0x0002 -#define MESSAGE_BUS_LISTENER_REENTRANT 0x0004 -#define MESSAGE_BUS_LISTENER_BUSY 0x0008 +#define MESSAGE_BUS_LISTENER_BUSY 0x0004 +#define MESSAGE_BUS_LISTENER_REENTRANT 0x0008 +#define MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY 0x0010 +#define MESSAGE_BUS_LISTENER_DROP_IF_BUSY 0x0020 +#define MESSAGE_BUS_LISTENER_NONBLOCKING 0x0040 struct MicroBitListener { @@ -25,7 +29,8 @@ struct MicroBitListener void* cb_arg; // Optional argument to be passed to the caller. - MicroBitEvent evt; + MicroBitEvent evt; + MicroBitEventQueueItem *evt_queue; MicroBitListener *next; @@ -36,13 +41,13 @@ struct MicroBitListener * @param value The event ID you would like to listen to from that component * @param handler A function pointer to call when the event is detected. */ - MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)); + MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); /** * Alternative constructor where we register a value to be passed to the * callback. */ - MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg); + MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg, uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); /** * Constructor. @@ -53,12 +58,18 @@ struct MicroBitListener * @param object The method within the C++ object to call. */ template - MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent)); + MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); /** * Destructor. Ensures all resources used by this listener are freed. */ ~MicroBitListener(); + + /** + * Queues and event up to be processed. + * @param e The event to queue + */ + void queue(MicroBitEvent e); }; /** @@ -71,13 +82,13 @@ struct MicroBitListener */ template -MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent)) +MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent), uint16_t flags) { this->id = id; this->value = value; this->cb_method = new MemberFunctionCallback(object, method); this->cb_arg = NULL; - this->flags = MESSAGE_BUS_LISTENER_METHOD; + this->flags = flags | MESSAGE_BUS_LISTENER_METHOD; this->next = NULL; } diff --git a/inc/MicroBitMatrixMaps.h b/inc/MicroBitMatrixMaps.h index 639ed20..df01ca9 100644 --- a/inc/MicroBitMatrixMaps.h +++ b/inc/MicroBitMatrixMaps.h @@ -8,6 +8,8 @@ #ifndef MICROBIT_MATRIX_MAPS_H #define MICROBIT_MATRIX_MAPS_H +#include "mbed.h" +#include "MicroBitDisplay.h" /** * Provides the mapping from Matrix ROW/COL to a linear X/Y buffer. * It's arranged such that matrixMap[col, row] provides the [x,y] screen co-ord. diff --git a/inc/MicroBitMessageBus.h b/inc/MicroBitMessageBus.h index f6b1465..85990f6 100644 --- a/inc/MicroBitMessageBus.h +++ b/inc/MicroBitMessageBus.h @@ -2,33 +2,15 @@ #define MICROBIT_MESSAGE_BUS_H #include "mbed.h" -#include "MemberFunctionCallback.h" -#include "MicroBitListener.h" #include "MicroBitComponent.h" #include "MicroBitEvent.h" +#include "MicroBitListener.h" // Enumeration of core components. #define MICROBIT_CONTROL_BUS_ID 0 #define MICROBIT_ID_ANY 0 #define MICROBIT_EVT_ANY 0 -/** - * Enclosing class to hold a chain of events. - */ -struct MicroBitEventQueueItem -{ - MicroBitEvent evt; - MicroBitEventQueueItem *next; - - /** - * Constructor. - * Creates a new MicroBitEventQueueItem. - * @param evt The event that is to be queued. - */ - MicroBitEventQueueItem(MicroBitEvent evt); -}; - - /** * Class definition for the MicroBitMessageBus. * @@ -105,7 +87,7 @@ class MicroBitMessageBus : public MicroBitComponent * uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); // call function when ever a click event is detected. * @endcode */ - void listen(int id, int value, void (*handler)(MicroBitEvent)); + void listen(int id, int value, void (*handler)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); /** * Register a listener function. @@ -127,7 +109,7 @@ class MicroBitMessageBus : public MicroBitComponent * uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); // call function when ever a click event is detected. * @endcode */ - void listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg); + void listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg, uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); /** * Register a listener function. @@ -152,7 +134,7 @@ class MicroBitMessageBus : public MicroBitComponent * @endcode */ template - void listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)); + void listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); /** @@ -220,6 +202,12 @@ class MicroBitMessageBus : public MicroBitComponent */ template void ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)); + + /** + * Returns a 'nonce' for use with the NONCE_ID channel of the message bus. + */ + uint16_t nonce(); + private: /** @@ -233,6 +221,7 @@ class MicroBitMessageBus : 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. void queueEvent(MicroBitEvent &evt); MicroBitEventQueueItem* dequeueEvent(); @@ -255,12 +244,12 @@ class MicroBitMessageBus : public MicroBitComponent * @param hander The method to call when an event is received. */ template -void MicroBitMessageBus::listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)) +void MicroBitMessageBus::listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent), uint16_t flags) { if (object == NULL || handler == NULL) return; - MicroBitListener *newListener = new MicroBitListener(id, value, object, handler); + MicroBitListener *newListener = new MicroBitListener(id, value, object, handler, flags); if(!add(newListener)) delete newListener; diff --git a/inc/MicroBitPin.h b/inc/MicroBitPin.h index edaf844..e4ba2fd 100644 --- a/inc/MicroBitPin.h +++ b/inc/MicroBitPin.h @@ -1,8 +1,8 @@ #ifndef MICROBIT_PIN_H #define MICROBIT_PIN_H -#include "MicroBitComponent.h" #include "mbed.h" +#include "MicroBitComponent.h" // Status Field flags... #define IO_STATUS_DIGITAL_IN 0x01 // Pin is configured as a digital input, with no pull up. #define IO_STATUS_DIGITAL_OUT 0x02 // Pin is configured as a digital output diff --git a/inc/MicroBitSerial.h b/inc/MicroBitSerial.h index 2fd34de..ab8ba6e 100644 --- a/inc/MicroBitSerial.h +++ b/inc/MicroBitSerial.h @@ -1,9 +1,9 @@ -#include "MicroBit.h" #ifndef MICROBIT_SERIAL_H #define MICROBIT_SERIAL_H -#include "MicroBitComponent.h" #include "mbed.h" +#include "ManagedString.h" +#include "MicroBitImage.h" #define MICROBIT_SERIAL_DEFAULT_BAUD_RATE 115200 #define MICROBIT_SERIAL_BUFFER_SIZE 20 diff --git a/source/DynamicPwm.cpp b/source/DynamicPwm.cpp index 16d6459..8f82258 100644 --- a/source/DynamicPwm.cpp +++ b/source/DynamicPwm.cpp @@ -1,6 +1,4 @@ -#include "MicroBitHeapAllocator.h" -#include "DynamicPwm.h" - +#include "MicroBit.h" DynamicPwm* DynamicPwm::pwms[NO_PWMS] = { NULL }; diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index a751fb6..aa5d315 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -1,9 +1,7 @@ #include #include #include "mbed.h" -#include "MicroBitHeapAllocator.h" -#include "MicroBitCompat.h" -#include "ManagedString.h" +#include "MicroBit.h" /** diff --git a/source/MicroBitDisplay.cpp b/source/MicroBitDisplay.cpp index 3020ddb..5c71143 100644 --- a/source/MicroBitDisplay.cpp +++ b/source/MicroBitDisplay.cpp @@ -3,11 +3,10 @@ * * A MicroBitDisplay represents the LED matrix array on the MicroBit device. */ +#include "mbed.h" #include "MicroBit.h" #include "MicroBitMatrixMaps.h" -#include #include "nrf_gpio.h" -#include "mbed.h" const float timings[MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH] = {0.000010, 0.000047, 0.000094, 0.000187, 0.000375, 0.000750, 0.001500, 0.003000}; @@ -234,7 +233,7 @@ MicroBitDisplay::animationUpdate() if(animationMode == ANIMATION_MODE_PRINT_CHARACTER) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); } } } @@ -243,9 +242,13 @@ MicroBitDisplay::animationUpdate() * Broadcasts an event onto the shared MessageBus * @param eventCode The ID of the event that has occurred. */ -void MicroBitDisplay::sendEvent(uint16_t eventCode) +void MicroBitDisplay::sendAnimationCompleteEvent() { - MicroBitEvent evt(id,eventCode); + // Signal that we've completed an animation. + MicroBitEvent evt1(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + + // Wake up any fibers that were blocked on the animation (if any). + MicroBitEvent evt2(MICROBIT_ID_ALERT, nonce); } /** @@ -266,7 +269,7 @@ void MicroBitDisplay::updateScrollText() if (scrollingChar > scrollingText.length()) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); return; } scrollingChar++; @@ -284,7 +287,8 @@ void MicroBitDisplay::updatePrintText() if (printingChar > printingText.length()) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + + this->sendAnimationCompleteEvent(); return; } @@ -302,7 +306,8 @@ void MicroBitDisplay::updateScrollImage() if ((image.paste(scrollingImage, scrollingImagePosition, 0, 0) == 0) && scrollingImageRendered) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); + return; } @@ -321,7 +326,7 @@ void MicroBitDisplay::updateAnimateImage() if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); return; } @@ -349,7 +354,7 @@ void MicroBitDisplay::resetAnimation(uint16_t delay) if (animationMode != ANIMATION_MODE_NONE) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); } // Clear the display and setup the animation timers. @@ -406,7 +411,8 @@ void MicroBitDisplay::print(char c, int delay) animationMode = ANIMATION_MODE_PRINT_CHARACTER; // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -432,7 +438,8 @@ void MicroBitDisplay::print(ManagedString s, int delay) this->printAsync(s, delay); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -459,7 +466,8 @@ void MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay) animationMode = ANIMATION_MODE_PRINT_CHARACTER; // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -542,7 +550,8 @@ void MicroBitDisplay::scroll(ManagedString s, int delay) this->scrollAsync(s, delay); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -569,7 +578,8 @@ void MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride) this->scrollAsync(image, delay, stride); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -603,7 +613,7 @@ void MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, i if (animationMode != ANIMATION_MODE_NONE) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); } this->animationDelay = delay; @@ -646,7 +656,8 @@ void MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int st this->animateAsync(image, delay, stride, startingPosition); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } diff --git a/source/MicroBitEvent.cpp b/source/MicroBitEvent.cpp index ea6fb14..c12d33b 100644 --- a/source/MicroBitEvent.cpp +++ b/source/MicroBitEvent.cpp @@ -44,3 +44,15 @@ void MicroBitEvent::fire() { uBit.MessageBus.send(*this); } + +/** + * Constructor. + * Create a new MicroBitEventQueueItem. + * @param evt The event to be queued. + */ +MicroBitEventQueueItem::MicroBitEventQueueItem(MicroBitEvent evt) +{ + this->evt = evt; + this->next = NULL; +} + diff --git a/source/MicroBitEventService.cpp b/source/MicroBitEventService.cpp index 121ae1f..0963168 100644 --- a/source/MicroBitEventService.cpp +++ b/source/MicroBitEventService.cpp @@ -5,9 +5,7 @@ #include "MicroBit.h" #include "ble/UUID.h" - #include "ExternalEvents.h" -#include "MicroBitEventService.h" /** * Constructor. diff --git a/source/MicroBitFiber.cpp b/source/MicroBitFiber.cpp index 79dbd62..636f485 100644 --- a/source/MicroBitFiber.cpp +++ b/source/MicroBitFiber.cpp @@ -212,8 +212,8 @@ void scheduler_event(MicroBitEvent evt) t = f->next; // extract the event data this fiber is blocked on. - uint16_t id = f->context & 0xFF; - uint16_t value = (f->context & 0xFF00) >> 16; + uint16_t id = f->context & 0xFFFF; + uint16_t value = (f->context & 0xFFFF0000) >> 16; if ((id == MICROBIT_ID_ANY || id == evt.source) && (value == MICROBIT_EVT_ANY || value == evt.value)) { diff --git a/source/MicroBitI2C.cpp b/source/MicroBitI2C.cpp index a9e025f..67aa723 100644 --- a/source/MicroBitI2C.cpp +++ b/source/MicroBitI2C.cpp @@ -1,5 +1,5 @@ -#include "MicroBit.h" #include "mbed.h" +#include "MicroBit.h" #include "twi_master.h" #include "nrf_delay.h" diff --git a/source/MicroBitIO.cpp b/source/MicroBitIO.cpp index fad8a30..e314c9c 100644 --- a/source/MicroBitIO.cpp +++ b/source/MicroBitIO.cpp @@ -4,37 +4,37 @@ * Represents a single IO pin on the edge connector. */ -#include "MicroBitIO.h" +#include "MicroBit.h" /** * Constructor. * Create a representation of all given I/O pins on the edge connector */ -MicroBitIO::MicroBitIO(int MICROBIT_ID_IO_P0, int MICROBIT_ID_IO_P1, int MICROBIT_ID_IO_P2, - int MICROBIT_ID_IO_P3, int MICROBIT_ID_IO_P4, int MICROBIT_ID_IO_P5, - int MICROBIT_ID_IO_P6, int MICROBIT_ID_IO_P7, int MICROBIT_ID_IO_P8, - int MICROBIT_ID_IO_P9, int MICROBIT_ID_IO_P10,int MICROBIT_ID_IO_P11, - int MICROBIT_ID_IO_P12,int MICROBIT_ID_IO_P13,int MICROBIT_ID_IO_P14, - int MICROBIT_ID_IO_P15,int MICROBIT_ID_IO_P16,int MICROBIT_ID_IO_P19, - int MICROBIT_ID_IO_P20) : - P0 (MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL), //P0 is the left most pad (ANALOG/DIGITAL/TOUCH) - P1 (MICROBIT_ID_IO_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL), //P1 is the middle pad (ANALOG/DIGITAL/TOUCH) - P2 (MICROBIT_ID_IO_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL), //P2 is the right most pad (ANALOG/DIGITAL/TOUCH) - P3 (MICROBIT_ID_IO_P3, MICROBIT_PIN_P3, PIN_CAPABILITY_AD), //COL1 (ANALOG/DIGITAL) - P4 (MICROBIT_ID_IO_P4, MICROBIT_PIN_P4, PIN_CAPABILITY_DIGITAL), //BTN_A - P5 (MICROBIT_ID_IO_P5, MICROBIT_PIN_P5, PIN_CAPABILITY_AD), //COL2 (ANALOG/DIGITAL) - P6 (MICROBIT_ID_IO_P6, MICROBIT_PIN_P6, PIN_CAPABILITY_DIGITAL), //ROW2 - P7 (MICROBIT_ID_IO_P7, MICROBIT_PIN_P7, PIN_CAPABILITY_DIGITAL), //ROW1 - P8 (MICROBIT_ID_IO_P8, MICROBIT_PIN_P8, PIN_CAPABILITY_DIGITAL), //PIN 18 - P9 (MICROBIT_ID_IO_P9, MICROBIT_PIN_P9, PIN_CAPABILITY_DIGITAL), //ROW3 - P10(MICROBIT_ID_IO_P10,MICROBIT_PIN_P10,PIN_CAPABILITY_AD), //COL3 (ANALOG/DIGITAL) - P11(MICROBIT_ID_IO_P11,MICROBIT_PIN_P11,PIN_CAPABILITY_DIGITAL), //BTN_B - P12(MICROBIT_ID_IO_P12,MICROBIT_PIN_P12,PIN_CAPABILITY_DIGITAL), //PIN 20 - P13(MICROBIT_ID_IO_P13,MICROBIT_PIN_P13,PIN_CAPABILITY_DIGITAL), //SCK - P14(MICROBIT_ID_IO_P14,MICROBIT_PIN_P14,PIN_CAPABILITY_DIGITAL), //MISO - P15(MICROBIT_ID_IO_P15,MICROBIT_PIN_P15,PIN_CAPABILITY_DIGITAL), //MOSI - P16(MICROBIT_ID_IO_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_DIGITAL), //PIN 16 - P19(MICROBIT_ID_IO_P19,MICROBIT_PIN_P19,PIN_CAPABILITY_DIGITAL), //SCL - P20(MICROBIT_ID_IO_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_DIGITAL) //SDA +MicroBitIO::MicroBitIO(int ID_P0, int ID_P1, int ID_P2, + int ID_P3, int ID_P4, int ID_P5, + int ID_P6, int ID_P7, int ID_P8, + int ID_P9, int ID_P10,int ID_P11, + int ID_P12,int ID_P13,int ID_P14, + int ID_P15,int ID_P16,int ID_P19, + int ID_P20) : + P0 (ID_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL), //P0 is the left most pad (ANALOG/DIGITAL/TOUCH) + P1 (ID_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL), //P1 is the middle pad (ANALOG/DIGITAL/TOUCH) + P2 (ID_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL), //P2 is the right most pad (ANALOG/DIGITAL/TOUCH) + P3 (ID_P3, MICROBIT_PIN_P3, PIN_CAPABILITY_AD), //COL1 (ANALOG/DIGITAL) + P4 (ID_P4, MICROBIT_PIN_P4, PIN_CAPABILITY_DIGITAL), //BTN_A + P5 (ID_P5, MICROBIT_PIN_P5, PIN_CAPABILITY_AD), //COL2 (ANALOG/DIGITAL) + P6 (ID_P6, MICROBIT_PIN_P6, PIN_CAPABILITY_DIGITAL), //ROW2 + P7 (ID_P7, MICROBIT_PIN_P7, PIN_CAPABILITY_DIGITAL), //ROW1 + P8 (ID_P8, MICROBIT_PIN_P8, PIN_CAPABILITY_DIGITAL), //PIN 18 + P9 (ID_P9, MICROBIT_PIN_P9, PIN_CAPABILITY_DIGITAL), //ROW3 + P10(ID_P10,MICROBIT_PIN_P10,PIN_CAPABILITY_AD), //COL3 (ANALOG/DIGITAL) + P11(ID_P11,MICROBIT_PIN_P11,PIN_CAPABILITY_DIGITAL), //BTN_B + P12(ID_P12,MICROBIT_PIN_P12,PIN_CAPABILITY_DIGITAL), //PIN 20 + P13(ID_P13,MICROBIT_PIN_P13,PIN_CAPABILITY_DIGITAL), //SCK + P14(ID_P14,MICROBIT_PIN_P14,PIN_CAPABILITY_DIGITAL), //MISO + P15(ID_P15,MICROBIT_PIN_P15,PIN_CAPABILITY_DIGITAL), //MOSI + P16(ID_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_DIGITAL), //PIN 16 + P19(ID_P19,MICROBIT_PIN_P19,PIN_CAPABILITY_DIGITAL), //SCL + P20(ID_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_DIGITAL) //SDA { } diff --git a/source/MicroBitListener.cpp b/source/MicroBitListener.cpp index cbe56ac..09f1498 100644 --- a/source/MicroBitListener.cpp +++ b/source/MicroBitListener.cpp @@ -15,13 +15,13 @@ * @param value The event ID you would like to listen to from that component * @param handler A function pointer to call when the event is detected. */ -MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)) +MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent), uint16_t flags) { this->id = id; this->value = value; this->cb = handler; this->cb_arg = NULL; - this->flags = 0; + this->flags = flags; this->next = NULL; } @@ -33,13 +33,13 @@ MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)( * @param handler A function pointer to call when the event is detected. * @param arg An additional argument to pass to the event handler function. */ -MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg) +MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg, uint16_t flags) { this->id = id; this->value = value; this->cb_param = handler; this->cb_arg = arg; - this->flags = MESSAGE_BUS_LISTENER_PARAMETERISED; + this->flags = flags | MESSAGE_BUS_LISTENER_PARAMETERISED; this->next = NULL; } @@ -51,3 +51,23 @@ MicroBitListener::~MicroBitListener() if(this->flags & MESSAGE_BUS_LISTENER_METHOD) delete cb_method; } + +/** + * Queues and event up to be processed. + * @param e The event to queue + */ +void MicroBitListener::queue(MicroBitEvent e) +{ + MicroBitEventQueueItem *q = new MicroBitEventQueueItem(e); + MicroBitEventQueueItem *p = evt_queue; + + if (evt_queue == NULL) + evt_queue = q; + else + { + while (p->next != NULL) + p = p->next; + + p->next = q; + } +} diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index 7b8b9d0..1223c32 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -6,18 +6,6 @@ #include "MicroBit.h" -/** - * Constructor. - * Create a new MicroBitEventQueueItem. - * @param evt The event to be queued. - */ -MicroBitEventQueueItem::MicroBitEventQueueItem(MicroBitEvent evt) -{ - this->evt = evt; - this->next = NULL; -} - - /** * Constructor. * Create a new Message Bus. @@ -27,6 +15,18 @@ MicroBitMessageBus::MicroBitMessageBus() this->listeners = NULL; this->evt_queue_head = NULL; this->evt_queue_tail = NULL; + this->nonce_val = 0; +} + +/** + * Returns a 'nonce' for use with the NONCE_ID channel of the message bus. + */ +uint16_t MicroBitMessageBus::nonce() +{ + // In the global scheme of things, a terrible nonce generator. + // However, for our purposes, this is simple and adequate for local use. + // This would be a bad idea if our events were networked though - can you think why? + return nonce_val++; } /** @@ -39,20 +39,60 @@ void async_callback(void *param) { MicroBitListener *listener = (MicroBitListener *)param; + // OK, now we need to decide how to behave depending on our configuration. + // If this a fiber f already active within this listener then check our + // configuration to determine the correct course of action. + // + + if (listener->flags & MESSAGE_BUS_LISTENER_BUSY) + { + // Drop this event, if that's how we've been configured. + if (listener->flags & MESSAGE_BUS_LISTENER_DROP_IF_BUSY) + return; + + // Queue this event up for later, if that's how we've been configured. + if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) + { + listener->queue(listener->evt); + return; + } + } + // 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 :-/ - // Firstly, check for a method callback into an object. - if (listener->flags & MESSAGE_BUS_LISTENER_METHOD) - listener->cb_method->fire(listener->evt); + // Record that we have a fiber going into this listener... + listener->flags |= MESSAGE_BUS_LISTENER_BUSY; - // Now a parameterised C function - else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED) - listener->cb_param(listener->evt, listener->cb_arg); - - // We must have a plain C function - else - listener->cb(listener->evt); + while (1) + { + // Firstly, check for a method callback into an object. + if (listener->flags & MESSAGE_BUS_LISTENER_METHOD) + listener->cb_method->fire(listener->evt); + + // Now a parameterised C function + else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED) + listener->cb_param(listener->evt, listener->cb_arg); + + // We must have a plain C function + else + listener->cb(listener->evt); + + // If there are more events to process, dequeue te next one and process it. + if ((listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) && listener->evt_queue) + { + MicroBitEventQueueItem *item = listener->evt_queue; + + listener->evt = item->evt; + listener->evt_queue = listener->evt_queue->next; + delete item; + } + else + break; + } + + // The fiber of exiting... clear our state. + listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY; } @@ -176,33 +216,27 @@ void MicroBitMessageBus::process(MicroBitEvent evt) { MicroBitListener *l; - // Find the start of the sublist where we'll send this event. l = listeners; - while (l != NULL && l->id != evt.source) - l = l->next; - - // Now, send the event to all listeners registered for this event. - while (l != NULL && l->id == evt.source) - { - if(l->value == MICROBIT_EVT_ANY || l->value == evt.value) - { + while (l != NULL) + { + if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY)) + { l->evt = evt; - invoke(async_callback, (void *)l); + + // OK, if this handler has regisitered itself as non-blocking, we just execute it directly... + // This is normally only done for trusted system components. + // 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) + async_callback(l); + else + invoke(async_callback, l); } l = l->next; } - // Next, send to any listeners registered for ALL event sources. - l = listeners; - while (l != NULL && l->id == MICROBIT_ID_ANY) - { - l->evt = evt; - invoke(async_callback, (void *)l); - - l = l->next; - } - // Finally, forward the event to any other internal subsystems that may be interested. // We *could* do this through the message bus of course, but this saves additional RAM, // and procssor time (as we know these are non-blocking calls). @@ -237,23 +271,23 @@ void MicroBitMessageBus::process(MicroBitEvent evt) * @endcode */ -void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent)) +void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent), uint16_t flags) { if (handler == NULL) return; - MicroBitListener *newListener = new MicroBitListener(id, value, handler); + MicroBitListener *newListener = new MicroBitListener(id, value, handler, flags); if(!add(newListener)) delete newListener; } -void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg) +void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg, uint16_t flags) { if (handler == NULL) return; - MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg); + MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg, flags); if(!add(newListener)) delete newListener; diff --git a/source/MicroBitSerial.cpp b/source/MicroBitSerial.cpp index 499a515..9d54c51 100644 --- a/source/MicroBitSerial.cpp +++ b/source/MicroBitSerial.cpp @@ -1,5 +1,5 @@ -#include "MicroBit.h" #include "mbed.h" +#include "MicroBit.h" /** * Constructor. From 3395f66d3de6124d561baad11d2144f1c8617ff7 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Sat, 12 Sep 2015 11:34:16 +0100 Subject: [PATCH 4/4] microbit: Added configurable event processing options into MicroBitEvent Events in the micro:bit runtime are normally launched through making an instance of the MicroBitEvent class. This update added configuration support to all the code creating the MicroBitEvent decide if the newly created event should then be automatically queued on the MessageBus or processed immediately by event handlers. --- inc/MicroBitEvent.h | 36 +++++++++++++++++++++++++++++------ inc/MicroBitMessageBus.h | 4 ++-- source/MicroBitEvent.cpp | 21 ++++++++++++++++---- source/MicroBitMessageBus.cpp | 3 ++- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/inc/MicroBitEvent.h b/inc/MicroBitEvent.h index d9c05c9..425848b 100644 --- a/inc/MicroBitEvent.h +++ b/inc/MicroBitEvent.h @@ -3,6 +3,15 @@ #include "mbed.h" +enum MicroBitEventLaunchMode +{ + CREATE_ONLY, + CREATE_AND_QUEUE, + CREATE_AND_FIRE +}; + +#define MICROBIT_EVENT_DEFAULT_LAUNCH_MODE CREATE_AND_QUEUE + /** * Class definition for a MicrobitEvent * It represents a common event that is generated by the various components on the MB. @@ -22,15 +31,24 @@ class MicroBitEvent * Constructor. * @param src ID of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A. * @param value Component specific code indicating the cause of the event. - * @param fire whether the event should be fire immediately upon construction + * @param mode optional definition of how the event should be processed after construction (if at all): + * + * CREATE_ONLY: MicroBitEvent is initialised, and no further processing takes place. + * CREATE_AND_QUEUE: MicroBitEvent is initialised, and queued on the MicroBitMessageBus. + * CREATE_AND_FIRE: MicroBitEvent is initialised, and its event handlers are immediately fired (not suitable for use in interrupts!). * - * Example: + * Example: Create and launch an event using the default configuration * @code - * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,true); // auto fire + * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK); + * @endcode + * + * Example: Create and launch an event and process all registered event handlers immediately. + * @code + * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,CREATE_AND_FIRE); * @endcode */ - - MicroBitEvent(uint16_t source, uint16_t value, bool fire = true); + + MicroBitEvent(uint16_t source, uint16_t value, MicroBitEventLaunchMode mode = MICROBIT_EVENT_DEFAULT_LAUNCH_MODE); /** * Default constructor - initialises all values, and sets timestamp to the current time. @@ -38,9 +56,15 @@ class MicroBitEvent MicroBitEvent(); /** - * Fires the represented event onto the message bus. + * Fires the represented event onto the message bus using the default configuration. */ void fire(); + + /** + * Fires the represented event onto the message bus. + * @param mode Configuration of how the event is processed. + */ + void fire(MicroBitEventLaunchMode mode); }; /** diff --git a/inc/MicroBitMessageBus.h b/inc/MicroBitMessageBus.h index 85990f6..fdace6a 100644 --- a/inc/MicroBitMessageBus.h +++ b/inc/MicroBitMessageBus.h @@ -60,10 +60,10 @@ class MicroBitMessageBus : public MicroBitComponent * Internal function, used to deliver the given event to all relevant recipients. * Normally, this is called once an event has been removed from the event queue. * - * IT IS RECOMMENDED THAT ALL EXTERNAL CODE USE THE send() FUNCTIONS INSTEAD OF THIS FUNCTION. + * IT IS RECOMMENDED THAT ALL EXTERNAL CODE USE THE send() FUNCTIONS INSTEAD OF THIS FUNCTION, + * or the constructors provided by MicroBitEvent. * * @param evt The event to send. - * @param c The cache entry to reduce lookups for commonly used channels. */ void process(MicroBitEvent evt); diff --git a/source/MicroBitEvent.cpp b/source/MicroBitEvent.cpp index c12d33b..330048a 100644 --- a/source/MicroBitEvent.cpp +++ b/source/MicroBitEvent.cpp @@ -17,14 +17,14 @@ * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,true); // auto fire * @endcode */ -MicroBitEvent::MicroBitEvent(uint16_t source, uint16_t value, bool fire) +MicroBitEvent::MicroBitEvent(uint16_t source, uint16_t value, MicroBitEventLaunchMode mode) { this->source = source; this->value = value; this->timestamp = ticks; - if(fire) - this->fire(); + if(mode != CREATE_ONLY) + this->fire(mode); } /** @@ -37,14 +37,27 @@ MicroBitEvent::MicroBitEvent() this->timestamp = ticks; } +/** + * Fires the represented event onto the message bus. + */ +void MicroBitEvent::fire(MicroBitEventLaunchMode mode) +{ + if (mode == CREATE_AND_QUEUE) + uBit.MessageBus.send(*this); + + else if (mode == CREATE_AND_FIRE) + uBit.MessageBus.process(*this); +} + /** * Fires the represented event onto the message bus. */ void MicroBitEvent::fire() { - uBit.MessageBus.send(*this); + fire(MICROBIT_EVENT_DEFAULT_LAUNCH_MODE); } + /** * Constructor. * Create a new MicroBitEventQueueItem. diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index 1223c32..3d7b999 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -189,8 +189,9 @@ int MicroBitMessageBus::isIdleCallbackNeeded() * * Example: * @code - * MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN,ticks,false); + * MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN,ticks,CREATE_ONLY); * evt.fire(); + * * //OR YOU CAN DO THIS... * MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN); * @endcode