microbit: Updates to enable queing of display animation calls
Updates to change the behaviour of the scroll/print/animate faily of function away from being pre-emtive and instead prroviding queing behaviour. Minor updates to provide complete sets of async equivalent operations Updates to the scheduler to provide wait/notify/waitone semantics.
This commit is contained in:
parent
85b2b1e09e
commit
aca544677e
|
@ -41,7 +41,9 @@
|
||||||
#define MICROBIT_ID_IO_P20 25 //SDA
|
#define MICROBIT_ID_IO_P20 25 //SDA
|
||||||
|
|
||||||
#define MICROBIT_ID_BUTTON_AB 26 // Button A+B multibutton
|
#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.
|
|
||||||
|
#define MICROBIT_ID_NOTIFY 1023 // Notfication channel, for general purpose synchronisation
|
||||||
|
#define MICROBIT_ID_NOTIFY_ONE 1022 // Notfication channel, for general purpose synchronisation
|
||||||
|
|
||||||
class MicroBitComponent
|
class MicroBitComponent
|
||||||
{
|
{
|
||||||
|
|
|
@ -88,7 +88,7 @@
|
||||||
// MESSAGE_BUS_LISTENER_NONBLOCKING
|
// MESSAGE_BUS_LISTENER_NONBLOCKING
|
||||||
|
|
||||||
#ifndef MESSAGE_BUS_LISTENER_DEFAULT_FLAGS
|
#ifndef MESSAGE_BUS_LISTENER_DEFAULT_FLAGS
|
||||||
#define MESSAGE_BUS_LISTENER_DEFAULT_FLAGS MESSAGE_BUS_LISTENER_REENTRANT
|
#define MESSAGE_BUS_LISTENER_DEFAULT_FLAGS MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
* MessageBus Event Codes
|
* MessageBus Event Codes
|
||||||
*/
|
*/
|
||||||
#define MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE 1
|
#define MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE 1
|
||||||
|
#define MICROBIT_DISPLAY_EVT_FREE 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* I/O configurations for common devices.
|
* I/O configurations for common devices.
|
||||||
|
@ -70,6 +71,7 @@
|
||||||
|
|
||||||
enum AnimationMode {
|
enum AnimationMode {
|
||||||
ANIMATION_MODE_NONE,
|
ANIMATION_MODE_NONE,
|
||||||
|
ANIMATION_MODE_STOPPED,
|
||||||
ANIMATION_MODE_SCROLL_TEXT,
|
ANIMATION_MODE_SCROLL_TEXT,
|
||||||
ANIMATION_MODE_PRINT_TEXT,
|
ANIMATION_MODE_PRINT_TEXT,
|
||||||
ANIMATION_MODE_SCROLL_IMAGE,
|
ANIMATION_MODE_SCROLL_IMAGE,
|
||||||
|
@ -103,7 +105,6 @@ class MicroBitDisplay : public MicroBitComponent
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
uint8_t greyscaleBitMsk;
|
uint8_t greyscaleBitMsk;
|
||||||
uint8_t timingCount;
|
uint8_t timingCount;
|
||||||
uint16_t nonce;
|
|
||||||
Timeout renderTimer;
|
Timeout renderTimer;
|
||||||
|
|
||||||
MicroBitFont font;
|
MicroBitFont font;
|
||||||
|
@ -221,6 +222,11 @@ class MicroBitDisplay : public MicroBitComponent
|
||||||
*/
|
*/
|
||||||
void sendAnimationCompleteEvent();
|
void sendAnimationCompleteEvent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks the current fiber until the display is available (i.e. not effect is being displayed).
|
||||||
|
* Animations are queued until their time to display.
|
||||||
|
*/
|
||||||
|
void waitForFreeDisplay();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// The mutable bitmap buffer being rendered to the LED matrix.
|
// The mutable bitmap buffer being rendered to the LED matrix.
|
||||||
|
@ -242,16 +248,29 @@ public:
|
||||||
MicroBitDisplay(uint16_t id, uint8_t x, uint8_t y);
|
MicroBitDisplay(uint16_t id, uint8_t x, uint8_t y);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the current given animation.
|
* Stops any currently running animation, and any that are waiting to be displayed.
|
||||||
* @param delay the delay after which the animation is reset.
|
|
||||||
*/
|
*/
|
||||||
void resetAnimation(uint16_t delay);
|
void stopAnimation();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frame update method, invoked periodically to strobe the display.
|
* Frame update method, invoked periodically to strobe the display.
|
||||||
*/
|
*/
|
||||||
virtual void systemTick();
|
virtual void systemTick();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the given character to the display, if it is not in use.
|
||||||
|
*
|
||||||
|
* @param c The character to display.
|
||||||
|
* @param d Optional parameter - the time for which to show the character. Zero displays the character forever.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* uBit.display.printAsync('p');
|
||||||
|
* uBit.display.printAsync('p',100);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
void printAsync(char c, int delay = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the given string to the display, one character at a time.
|
* Prints the given string to the display, one character at a time.
|
||||||
* Uses the given delay between characters.
|
* Uses the given delay between characters.
|
||||||
|
@ -267,6 +286,24 @@ public:
|
||||||
*/
|
*/
|
||||||
void printAsync(ManagedString s, int delay = MICROBIT_DEFAULT_PRINT_SPEED);
|
void printAsync(ManagedString s, int delay = MICROBIT_DEFAULT_PRINT_SPEED);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the given image to the display, if the display is not in use.
|
||||||
|
* Returns immediately, and executes the animation asynchronously.
|
||||||
|
*
|
||||||
|
* @param i The image to display.
|
||||||
|
* @param x The horizontal position on the screen to display the image (default 0)
|
||||||
|
* @param y The vertical position on the screen to display the image (default 0)
|
||||||
|
* @param alpha Treats the brightness level '0' as transparent (default 0)
|
||||||
|
* @param delay The time to delay between characters, in timer ticks.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
|
||||||
|
* uBit.display.print(i,400);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
void printAsync(MicroBitImage i, int x, int y, int alpha, int delay = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the given character to the display.
|
* Prints the given character to the display.
|
||||||
*
|
*
|
||||||
|
@ -307,7 +344,7 @@ public:
|
||||||
* uBit.display.print(i,400);
|
* uBit.display.print(i,400);
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
void print(MicroBitImage i, int x, int y, int alpha, int delay = MICROBIT_DEFAULT_PRINT_SPEED);
|
void print(MicroBitImage i, int x, int y, int alpha, int delay = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrolls the given string to the display, from right to left.
|
* Scrolls the given string to the display, from right to left.
|
||||||
|
|
|
@ -203,11 +203,6 @@ class MicroBitMessageBus : public MicroBitComponent
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent));
|
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:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -191,7 +191,7 @@ void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params)
|
||||||
*/
|
*/
|
||||||
void MicroBitDFUService::showTick()
|
void MicroBitDFUService::showTick()
|
||||||
{
|
{
|
||||||
uBit.display.resetAnimation(0);
|
uBit.display.stopAnimation();
|
||||||
|
|
||||||
uBit.display.image.setPixelValue(0,3, 255);
|
uBit.display.image.setPixelValue(0,3, 255);
|
||||||
uBit.display.image.setPixelValue(1,4, 255);
|
uBit.display.image.setPixelValue(1,4, 255);
|
||||||
|
@ -206,7 +206,7 @@ void MicroBitDFUService::showTick()
|
||||||
*/
|
*/
|
||||||
void MicroBitDFUService::showNameHistogram()
|
void MicroBitDFUService::showNameHistogram()
|
||||||
{
|
{
|
||||||
uBit.display.resetAnimation(0);
|
uBit.display.stopAnimation();
|
||||||
|
|
||||||
uint32_t n = NRF_FICR->DEVICEID[1];
|
uint32_t n = NRF_FICR->DEVICEID[1];
|
||||||
int ld = 1;
|
int ld = 1;
|
||||||
|
|
|
@ -234,10 +234,10 @@ MicroBitDisplay::animationUpdate()
|
||||||
void MicroBitDisplay::sendAnimationCompleteEvent()
|
void MicroBitDisplay::sendAnimationCompleteEvent()
|
||||||
{
|
{
|
||||||
// Signal that we've completed an animation.
|
// Signal that we've completed an animation.
|
||||||
MicroBitEvent evt1(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
||||||
|
|
||||||
// Wake up any fibers that were blocked on the animation (if any).
|
// Wake up a fiber that was blocked on the animation (if any).
|
||||||
MicroBitEvent evt2(MICROBIT_ID_ALERT, nonce);
|
MicroBitEvent(MICROBIT_ID_NOTIFY_ONE, MICROBIT_DISPLAY_EVT_FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -304,7 +304,6 @@ void MicroBitDisplay::updateScrollImage()
|
||||||
scrollingImageRendered = true;
|
scrollingImageRendered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal animateImage update method.
|
* Internal animateImage update method.
|
||||||
* Paste the stored bitmap at the appropriate point and stop on the last frame.
|
* Paste the stored bitmap at the appropriate point and stop on the last frame.
|
||||||
|
@ -333,28 +332,70 @@ void MicroBitDisplay::updateAnimateImage()
|
||||||
* Resets the current given animation.
|
* Resets the current given animation.
|
||||||
* @param delay the delay after which the animation is reset.
|
* @param delay the delay after which the animation is reset.
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::resetAnimation(uint16_t delay)
|
void MicroBitDisplay::stopAnimation()
|
||||||
{
|
{
|
||||||
//sanitise this value
|
|
||||||
if(delay <= 0 )
|
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
|
||||||
|
|
||||||
// Reset any ongoing animation.
|
// Reset any ongoing animation.
|
||||||
if (animationMode != ANIMATION_MODE_NONE)
|
if (animationMode != ANIMATION_MODE_NONE)
|
||||||
{
|
{
|
||||||
animationMode = ANIMATION_MODE_NONE;
|
animationMode = ANIMATION_MODE_NONE;
|
||||||
this->sendAnimationCompleteEvent();
|
|
||||||
|
// Indicate that we've completed an animation.
|
||||||
|
MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
||||||
|
|
||||||
|
// Wake up aall fibers that may blocked on the animation (if any).
|
||||||
|
MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the display and setup the animation timers.
|
// Clear the display and setup the animation timers.
|
||||||
this->image.clear();
|
this->image.clear();
|
||||||
this->animationDelay = delay;
|
|
||||||
this->animationTick = delay-1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the given string to the display, one character at a time.
|
* Blocks the current fiber until the display is available (i.e. not effect is being displayed).
|
||||||
* Uses the given delay between characters.
|
* Animations are queued until their time to display.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void MicroBitDisplay::waitForFreeDisplay()
|
||||||
|
{
|
||||||
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
|
if (animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED)
|
||||||
|
fiber_wait_for_event(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the given character to the display, if it is not in use.
|
||||||
|
*
|
||||||
|
* @param c The character to display.
|
||||||
|
* @param d Optional parameter - the time for which to show the character. Zero displays the character forever.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* uBit.display.printAsync('p');
|
||||||
|
* uBit.display.printAsync('p',100);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
void MicroBitDisplay::printAsync(char c, int delay)
|
||||||
|
{
|
||||||
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
|
this->waitForFreeDisplay();
|
||||||
|
|
||||||
|
// If the display is free, it's our turn to display.
|
||||||
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
||||||
|
{
|
||||||
|
image.print(c, 0, 0);
|
||||||
|
|
||||||
|
if(delay <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
animationDelay = delay;
|
||||||
|
animationTick = 0;
|
||||||
|
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the given string to the display, one character at a time, if the display is not in use.
|
||||||
* Returns immediately, and executes the animation asynchronously.
|
* Returns immediately, and executes the animation asynchronously.
|
||||||
*
|
*
|
||||||
* @param s The string to display.
|
* @param s The string to display.
|
||||||
|
@ -367,41 +408,77 @@ void MicroBitDisplay::resetAnimation(uint16_t delay)
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::printAsync(ManagedString s, int delay)
|
void MicroBitDisplay::printAsync(ManagedString s, int delay)
|
||||||
{
|
{
|
||||||
//sanitise this value
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
||||||
if(delay <= 0 )
|
{
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
//sanitise this value
|
||||||
|
if(delay <= 0 )
|
||||||
this->resetAnimation(delay);
|
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
||||||
|
|
||||||
this->printingChar = 0;
|
printingChar = 0;
|
||||||
this->printingText = s;
|
printingText = s;
|
||||||
|
animationDelay = delay;
|
||||||
animationMode = ANIMATION_MODE_PRINT_TEXT;
|
animationTick = 0;
|
||||||
|
|
||||||
|
animationMode = ANIMATION_MODE_PRINT_TEXT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the given character to the display.
|
* Prints the given image to the display, if the display is not in use.
|
||||||
|
* Returns immediately, and executes the animation asynchronously.
|
||||||
|
*
|
||||||
|
* @param i The image to display.
|
||||||
|
* @param x The horizontal position on the screen to display the image (default 0)
|
||||||
|
* @param y The vertical position on the screen to display the image (default 0)
|
||||||
|
* @param alpha Treats the brightness level '0' as transparent (default 0)
|
||||||
|
* @param delay The time to delay between characters, in timer ticks.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
|
||||||
|
* uBit.display.print(i,400);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
void MicroBitDisplay::printAsync(MicroBitImage i, int x, int y, int alpha, int delay)
|
||||||
|
{
|
||||||
|
// If the display is free, it's our turn to display.
|
||||||
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
||||||
|
{
|
||||||
|
image.paste(i, x, y, alpha);
|
||||||
|
|
||||||
|
if(delay <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
animationDelay = delay;
|
||||||
|
animationTick = 0;
|
||||||
|
|
||||||
|
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the given character to the display, and wait for it to complete.
|
||||||
*
|
*
|
||||||
* @param c The character to display.
|
* @param c The character to display.
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* @code
|
* @code
|
||||||
* uBit.display.print('p');
|
* uBit.display.print('p');
|
||||||
|
* uBit.display.print('p',100);
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::print(char c, int delay)
|
void MicroBitDisplay::print(char c, int delay)
|
||||||
{
|
{
|
||||||
image.print(c, 0, 0);
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
|
this->waitForFreeDisplay();
|
||||||
if(delay <= 0)
|
|
||||||
return;
|
// If the display is free, it's our turn to display.
|
||||||
|
// If someone called stopAnimation(), then we simply skip...
|
||||||
this->animationDelay = delay;
|
if (animationMode == ANIMATION_MODE_NONE)
|
||||||
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
|
{
|
||||||
|
this->printAsync(c, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -423,12 +500,16 @@ void MicroBitDisplay::print(ManagedString s, int delay)
|
||||||
if(delay <= 0 )
|
if(delay <= 0 )
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
||||||
|
|
||||||
// Start the effect.
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
this->printAsync(s, delay);
|
this->waitForFreeDisplay();
|
||||||
|
|
||||||
// Wait for completion.
|
// If the display is free, it's our turn to display.
|
||||||
nonce = uBit.MessageBus.nonce();
|
// If someone called stopAnimation(), then we simply skip...
|
||||||
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
|
if (animationMode == ANIMATION_MODE_NONE)
|
||||||
|
{
|
||||||
|
this->printAsync(s, delay);
|
||||||
|
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -446,17 +527,17 @@ void MicroBitDisplay::print(ManagedString s, int delay)
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
|
void MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
|
||||||
{
|
{
|
||||||
image.paste(i, x, y, alpha);
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
if(delay <= 0)
|
this->waitForFreeDisplay();
|
||||||
return;
|
|
||||||
|
// If the display is free, it's our turn to display.
|
||||||
this->animationDelay = delay;
|
// If someone called stopAnimation(), then we simply skip...
|
||||||
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
|
if (animationMode == ANIMATION_MODE_NONE)
|
||||||
|
{
|
||||||
// Wait for completion.
|
this->printAsync(i, x, y, alpha, delay);
|
||||||
nonce = uBit.MessageBus.nonce();
|
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
||||||
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -474,17 +555,19 @@ void MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::scrollAsync(ManagedString s, int delay)
|
void MicroBitDisplay::scrollAsync(ManagedString s, int delay)
|
||||||
{
|
{
|
||||||
//sanitise this value
|
// If the display is free, it's our turn to display.
|
||||||
if(delay <= 0 )
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
{
|
||||||
|
//sanitise the delay parameter
|
||||||
this->resetAnimation(delay);
|
if(delay <= 0 )
|
||||||
|
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
||||||
this->scrollingPosition = width-1;
|
|
||||||
this->scrollingChar = 0;
|
scrollingPosition = width-1;
|
||||||
this->scrollingText = s;
|
scrollingChar = 0;
|
||||||
|
scrollingText = s;
|
||||||
animationMode = ANIMATION_MODE_SCROLL_TEXT;
|
|
||||||
|
animationMode = ANIMATION_MODE_SCROLL_TEXT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -502,18 +585,20 @@ void MicroBitDisplay::scrollAsync(ManagedString s, int delay)
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
|
void MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
|
||||||
{
|
{
|
||||||
//sanitise the delay value
|
// If the display is free, it's our turn to display.
|
||||||
if(delay <= 0 )
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
{
|
||||||
|
//sanitise the delay value
|
||||||
this->resetAnimation(delay);
|
if(delay <= 0 )
|
||||||
|
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
||||||
|
|
||||||
this->scrollingImagePosition = stride < 0 ? width : -image.getWidth();
|
this->scrollingImagePosition = stride < 0 ? width : -image.getWidth();
|
||||||
this->scrollingImageStride = stride;
|
this->scrollingImageStride = stride;
|
||||||
this->scrollingImage = image;
|
this->scrollingImage = image;
|
||||||
this->scrollingImageRendered = false;
|
this->scrollingImageRendered = false;
|
||||||
|
|
||||||
animationMode = ANIMATION_MODE_SCROLL_IMAGE;
|
animationMode = ANIMATION_MODE_SCROLL_IMAGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -531,16 +616,19 @@ void MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::scroll(ManagedString s, int delay)
|
void MicroBitDisplay::scroll(ManagedString s, int delay)
|
||||||
{
|
{
|
||||||
//sanitise this value
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
if(delay <= 0 )
|
this->waitForFreeDisplay();
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
|
||||||
|
// If the display is free, it's our turn to display.
|
||||||
// Start the effect.
|
// If someone called stopAnimation(), then we simply skip...
|
||||||
this->scrollAsync(s, delay);
|
if (animationMode == ANIMATION_MODE_NONE)
|
||||||
|
{
|
||||||
// Wait for completion.
|
// Start the effect.
|
||||||
nonce = uBit.MessageBus.nonce();
|
this->scrollAsync(s, delay);
|
||||||
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
|
|
||||||
|
// Wait for completion.
|
||||||
|
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -559,16 +647,19 @@ void MicroBitDisplay::scroll(ManagedString s, int delay)
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
|
void MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
|
||||||
{
|
{
|
||||||
//sanitise the delay value
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
if(delay <= 0 )
|
this->waitForFreeDisplay();
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
|
||||||
|
// If the display is free, it's our turn to display.
|
||||||
// Start the effect.
|
// If someone called stopAnimation(), then we simply skip...
|
||||||
this->scrollAsync(image, delay, stride);
|
if (animationMode == ANIMATION_MODE_NONE)
|
||||||
|
{
|
||||||
// Wait for completion.
|
// Start the effect.
|
||||||
nonce = uBit.MessageBus.nonce();
|
this->scrollAsync(image, delay, stride);
|
||||||
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
|
|
||||||
|
// Wait for completion.
|
||||||
|
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -591,30 +682,27 @@ void MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition)
|
void MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition)
|
||||||
{
|
{
|
||||||
// Assume right to left functionality, to align with scrollString()
|
// If the display is free, it's our turn to display.
|
||||||
stride = -stride;
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
||||||
|
|
||||||
//sanitise the delay value
|
|
||||||
if(delay <= 0 )
|
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
|
||||||
|
|
||||||
// Reset any ongoing animation.
|
|
||||||
if (animationMode != ANIMATION_MODE_NONE)
|
|
||||||
{
|
{
|
||||||
animationMode = ANIMATION_MODE_NONE;
|
// Assume right to left functionality, to align with scrollString()
|
||||||
this->sendAnimationCompleteEvent();
|
stride = -stride;
|
||||||
}
|
|
||||||
|
|
||||||
this->animationDelay = delay;
|
|
||||||
this->animationTick = delay-1;
|
|
||||||
|
|
||||||
//calculate starting position which is offset by the stride
|
//sanitise the delay value
|
||||||
this->scrollingImagePosition = (startingPosition == MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS)?MICROBIT_DISPLAY_WIDTH + stride:startingPosition;
|
if(delay <= 0 )
|
||||||
this->scrollingImageStride = stride;
|
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
||||||
this->scrollingImage = image;
|
|
||||||
this->scrollingImageRendered = false;
|
animationDelay = delay;
|
||||||
|
animationTick = delay-1;
|
||||||
animationMode = ANIMATION_MODE_ANIMATE_IMAGE;
|
|
||||||
|
//calculate starting position which is offset by the stride
|
||||||
|
scrollingImagePosition = (startingPosition == MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS) ? MICROBIT_DISPLAY_WIDTH + stride : startingPosition;
|
||||||
|
scrollingImageStride = stride;
|
||||||
|
scrollingImage = image;
|
||||||
|
scrollingImageRendered = false;
|
||||||
|
|
||||||
|
animationMode = ANIMATION_MODE_ANIMATE_IMAGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -637,16 +725,19 @@ void MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, i
|
||||||
*/
|
*/
|
||||||
void MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition)
|
void MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition)
|
||||||
{
|
{
|
||||||
//sanitise the delay value
|
// If there's an ongoing animation, wait for our turn to display.
|
||||||
if(delay <= 0 )
|
this->waitForFreeDisplay();
|
||||||
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
|
|
||||||
|
// If the display is free, it's our turn to display.
|
||||||
// Start the effect.
|
// If someone called stopAnimation(), then we simply skip...
|
||||||
this->animateAsync(image, delay, stride, startingPosition);
|
if (animationMode == ANIMATION_MODE_NONE)
|
||||||
|
{
|
||||||
// Wait for completion.
|
// Start the effect.
|
||||||
nonce = uBit.MessageBus.nonce();
|
this->animateAsync(image, delay, stride, startingPosition);
|
||||||
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
|
|
||||||
|
// Wait for completion.
|
||||||
|
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
Fiber *currentFiber = NULL; // The context in which the current fiber is executing.
|
Fiber *currentFiber = NULL; // The context in which the current fiber is executing.
|
||||||
Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing.
|
Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing.
|
||||||
Fiber *idleFiber = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks.
|
Fiber *idleFiber = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scheduler state.
|
* Scheduler state.
|
||||||
|
@ -205,6 +205,7 @@ void scheduler_event(MicroBitEvent evt)
|
||||||
{
|
{
|
||||||
Fiber *f = waitQueue;
|
Fiber *f = waitQueue;
|
||||||
Fiber *t;
|
Fiber *t;
|
||||||
|
int notifyOneComplete = 0;
|
||||||
|
|
||||||
// Check the wait queue, and wake up any fibers as necessary.
|
// Check the wait queue, and wake up any fibers as necessary.
|
||||||
while (f != NULL)
|
while (f != NULL)
|
||||||
|
@ -214,7 +215,16 @@ void scheduler_event(MicroBitEvent evt)
|
||||||
// extract the event data this fiber is blocked on.
|
// extract the event data this fiber is blocked on.
|
||||||
uint16_t id = f->context & 0xFFFF;
|
uint16_t id = f->context & 0xFFFF;
|
||||||
uint16_t value = (f->context & 0xFFFF0000) >> 16;
|
uint16_t value = (f->context & 0xFFFF0000) >> 16;
|
||||||
|
|
||||||
|
// Special case for the NOTIFY_ONE channel...
|
||||||
|
if ((notifyOneComplete == 0) && (id == MICROBIT_ID_NOTIFY && evt.source == MICROBIT_ID_NOTIFY_ONE) && (value == MICROBIT_EVT_ANY || value == evt.value))
|
||||||
|
{
|
||||||
|
// Wakey wakey!
|
||||||
|
dequeue_fiber(f);
|
||||||
|
queue_fiber(f,&runQueue);
|
||||||
|
notifyOneComplete = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if ((id == MICROBIT_ID_ANY || id == evt.source) && (value == MICROBIT_EVT_ANY || value == evt.value))
|
if ((id == MICROBIT_ID_ANY || id == evt.source) && (value == MICROBIT_EVT_ANY || value == evt.value))
|
||||||
{
|
{
|
||||||
// Wakey wakey!
|
// Wakey wakey!
|
||||||
|
|
|
@ -15,18 +15,6 @@ MicroBitMessageBus::MicroBitMessageBus()
|
||||||
this->listeners = NULL;
|
this->listeners = NULL;
|
||||||
this->evt_queue_head = NULL;
|
this->evt_queue_head = NULL;
|
||||||
this->evt_queue_tail = 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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,6 +74,9 @@ void async_callback(void *param)
|
||||||
listener->evt = item->evt;
|
listener->evt = item->evt;
|
||||||
listener->evt_queue = listener->evt_queue->next;
|
listener->evt_queue = listener->evt_queue->next;
|
||||||
delete item;
|
delete item;
|
||||||
|
|
||||||
|
// We spin the scheduler here, to preven any particular event handler from continuously holding onto resources.
|
||||||
|
schedule();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue