microbit-dal/source/MicroBitDisplay.cpp

1083 lines
32 KiB
C++
Raw Normal View History

/**
* Class definition for a MicroBitDisplay.
*
* A MicroBitDisplay represents the LED matrix array on the MicroBit device.
*/
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.
2015-09-11 15:39:38 +00:00
#include "mbed.h"
#include "MicroBit.h"
#include "MicroBitMatrixMaps.h"
#include "nrf_gpio.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};
/**
* Constructor.
* Create a representation of a display of a given size.
* The display is initially blank.
*
* @param x the width of the display in pixels.
* @param y the height of the display in pixels.
*
* Example:
* @code
* MicroBitDisplay display(MICROBIT_ID_DISPLAY, 5, 5),
* @endcode
*/
MicroBitDisplay::MicroBitDisplay(uint16_t id, uint8_t x, uint8_t y) :
font(),
image(x*2,y)
{
//set pins as output
nrf_gpio_range_cfg_output(MICROBIT_DISPLAY_COLUMN_START,MICROBIT_DISPLAY_COLUMN_START + MICROBIT_DISPLAY_COLUMN_COUNT + MICROBIT_DISPLAY_ROW_COUNT);
this->id = id;
this->width = x;
this->height = y;
this->strobeRow = 0;
this->strobeBitMsk = 0x20;
this->rotation = MICROBIT_DISPLAY_ROTATION_0;
this->greyscaleBitMsk = 0x01;
this->timingCount = 0;
this->setBrightness(MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS);
this->mode = DISPLAY_MODE_BLACK_AND_WHITE;
this->animationMode = ANIMATION_MODE_NONE;
uBit.flags |= MICROBIT_FLAG_DISPLAY_RUNNING;
}
/**
* Internal frame update method, used to strobe the display.
*
* TODO: Write a more efficient, complementary variation of this method for the case where
* MICROBIT_DISPLAY_ROW_COUNT > MICROBIT_DISPLAY_COLUMN_COUNT.
*/
void MicroBitDisplay::systemTick()
{
if(!(uBit.flags & MICROBIT_FLAG_DISPLAY_RUNNING))
return;
// Move on to the next row.
strobeBitMsk <<= 1;
strobeRow++;
//reset the row counts and bit mask when we have hit the max.
if(strobeRow == MICROBIT_DISPLAY_ROW_COUNT){
strobeRow = 0;
strobeBitMsk = 0x20;
}
if(mode == DISPLAY_MODE_BLACK_AND_WHITE)
render();
if(mode == DISPLAY_MODE_GREYSCALE)
{
greyscaleBitMsk = 0x01;
timingCount = 0;
renderGreyscale();
}
// Update text and image animations if we need to.
this->animationUpdate();
}
void MicroBitDisplay::renderFinish()
{
//kept inline to reduce overhead
//clear the old bit pattern for this row.
//clear port 0 4-7 and retain lower 4 bits
2015-10-27 22:17:04 +00:00
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, 0xF0 | (nrf_gpio_port_read(NRF_GPIO_PORT_SELECT_PORT0) & 0x0F));
// clear port 1 8-12 for the current row
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | 0x1F);
}
void MicroBitDisplay::render()
{
// Simple optimisation. If display is at zero brightness, there's nothign to do.
if(brightness == 0)
return;
int coldata = 0;
// Calculate the bitpattern to write.
for (int i = 0; i<MICROBIT_DISPLAY_COLUMN_COUNT; i++)
{
int x = matrixMap[i][strobeRow].x;
int y = matrixMap[i][strobeRow].y;
int t = x;
if(rotation == MICROBIT_DISPLAY_ROTATION_90)
{
x = width - 1 - y;
y = t;
}
if(rotation == MICROBIT_DISPLAY_ROTATION_180)
{
x = width - 1 - x;
y = height - 1 - y;
}
if(rotation == MICROBIT_DISPLAY_ROTATION_270)
{
x = y;
y = height - 1 - t;
}
2015-10-25 03:58:03 +00:00
if(image.getBitmap()[y*(width*2)+x])
coldata |= (1 << i);
}
//write the new bit pattern
//set port 0 4-7 and retain lower 4 bits
2015-10-27 22:17:04 +00:00
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, (~coldata<<4 & 0xF0) | (nrf_gpio_port_read(NRF_GPIO_PORT_SELECT_PORT0) & 0x0F));
//set port 1 8-12 for the current row
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | (~coldata>>4 & 0x1F));
//timer does not have enough resolution for brightness of 1. 23.53 us
if(brightness != MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS && brightness > MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS)
renderTimer.attach(this, &MicroBitDisplay::renderFinish, (((float)brightness) / ((float)MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS)) * (float)MICROBIT_DISPLAY_REFRESH_PERIOD);
//this will take around 23us to execute
if(brightness <= MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS)
renderFinish();
}
void MicroBitDisplay::renderGreyscale()
{
int coldata = 0;
// Calculate the bitpattern to write.
for (int i = 0; i<MICROBIT_DISPLAY_COLUMN_COUNT; i++)
{
int x = matrixMap[i][strobeRow].x;
int y = matrixMap[i][strobeRow].y;
int t = x;
if(rotation == MICROBIT_DISPLAY_ROTATION_90)
{
x = width - 1 - y;
y = t;
}
if(rotation == MICROBIT_DISPLAY_ROTATION_180)
{
x = width - 1 - x;
y = height - 1 - y;
}
if(rotation == MICROBIT_DISPLAY_ROTATION_270)
{
x = y;
y = height - 1 - t;
}
2015-10-25 03:58:03 +00:00
if(min(image.getBitmap()[y * (width * 2) + x],brightness) & greyscaleBitMsk)
coldata |= (1 << i);
}
//write the new bit pattern
//set port 0 4-7 and retain lower 4 bits
2015-10-27 22:17:04 +00:00
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, (~coldata<<4 & 0xF0) | (nrf_gpio_port_read(NRF_GPIO_PORT_SELECT_PORT0) & 0x0F));
//set port 1 8-12 for the current row
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | (~coldata>>4 & 0x1F));
if(timingCount > MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH-1)
return;
greyscaleBitMsk <<= 1;
renderTimer.attach(this,&MicroBitDisplay::renderGreyscale, timings[timingCount++]);
}
/**
* Periodic callback, that we use to perform any animations we have running.
*/
void
MicroBitDisplay::animationUpdate()
{
// If there's no ongoing animation, then nothing to do.
if (animationMode == ANIMATION_MODE_NONE)
return;
animationTick += FIBER_TICK_PERIOD_MS;
if(animationTick >= animationDelay)
{
animationTick = 0;
if (animationMode == ANIMATION_MODE_SCROLL_TEXT)
this->updateScrollText();
if (animationMode == ANIMATION_MODE_PRINT_TEXT)
this->updatePrintText();
if (animationMode == ANIMATION_MODE_SCROLL_IMAGE)
this->updateScrollImage();
if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE)
this->updateAnimateImage();
if(animationMode == ANIMATION_MODE_PRINT_CHARACTER)
{
animationMode = ANIMATION_MODE_NONE;
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.
2015-09-11 15:39:38 +00:00
this->sendAnimationCompleteEvent();
}
}
}
/**
* Broadcasts an event onto the shared MessageBus
* @param eventCode The ID of the event that has occurred.
*/
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.
2015-09-11 15:39:38 +00:00
void MicroBitDisplay::sendAnimationCompleteEvent()
{
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.
2015-09-11 15:39:38 +00:00
// Signal that we've completed an animation.
MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
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.
2015-09-11 15:39:38 +00:00
// Wake up a fiber that was blocked on the animation (if any).
MicroBitEvent(MICROBIT_ID_NOTIFY_ONE, MICROBIT_DISPLAY_EVT_FREE);
}
/**
* Internal scrollText update method.
* Shift the screen image by one pixel to the left. If necessary, paste in the next char.
*/
void MicroBitDisplay::updateScrollText()
{
image.shiftLeft(1);
scrollingPosition++;
if (scrollingPosition == width + MICROBIT_DISPLAY_SPACING)
{
scrollingPosition = 0;
image.print(scrollingChar < scrollingText.length() ? scrollingText.charAt(scrollingChar) : ' ',width,0);
if (scrollingChar > scrollingText.length())
{
animationMode = ANIMATION_MODE_NONE;
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.
2015-09-11 15:39:38 +00:00
this->sendAnimationCompleteEvent();
return;
}
scrollingChar++;
}
}
/**
* Internal printText update method.
* Paste in the next char in the string.
*/
void MicroBitDisplay::updatePrintText()
{
image.print(printingChar < printingText.length() ? printingText.charAt(printingChar) : ' ',0,0);
if (printingChar > printingText.length())
{
animationMode = ANIMATION_MODE_NONE;
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.
2015-09-11 15:39:38 +00:00
this->sendAnimationCompleteEvent();
return;
}
printingChar++;
}
/**
* Internal scrollImage update method.
* Paste the stored bitmap at the appropriate point.
*/
void MicroBitDisplay::updateScrollImage()
{
image.clear();
if ((image.paste(scrollingImage, scrollingImagePosition, 0, 0) == 0) && scrollingImageRendered)
{
animationMode = ANIMATION_MODE_NONE;
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.
2015-09-11 15:39:38 +00:00
this->sendAnimationCompleteEvent();
return;
}
scrollingImagePosition += scrollingImageStride;
scrollingImageRendered = true;
}
/**
* Internal animateImage update method.
* Paste the stored bitmap at the appropriate point and stop on the last frame.
*/
void MicroBitDisplay::updateAnimateImage()
{
//wait until we have rendered the last position to give a continuous animation.
if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered)
{
animationMode = ANIMATION_MODE_NONE;
this->clear();
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.
2015-09-11 15:39:38 +00:00
this->sendAnimationCompleteEvent();
return;
}
if(scrollingImagePosition > 0)
image.shiftLeft(-scrollingImageStride);
image.paste(scrollingImage, scrollingImagePosition, 0, 0);
scrollingImageRendered = true;
scrollingImagePosition += scrollingImageStride;
}
/**
* Resets the current given animation.
*/
void MicroBitDisplay::stopAnimation()
{
// Reset any ongoing animation.
if (animationMode != ANIMATION_MODE_NONE)
{
animationMode = ANIMATION_MODE_NONE;
// 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.
this->image.clear();
}
/**
* 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 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.
2015-11-01 18:22:27 +00:00
* @param delay Optional parameter - the time for which to show the character. Zero displays the character forever.
* @return MICROBIT_OK, MICROBIT_BUSY is the screen is in use, or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* uBit.display.printAsync('p');
* uBit.display.printAsync('p',100);
* @endcode
*/
2015-11-01 18:22:27 +00:00
int MicroBitDisplay::printAsync(char c, int delay)
{
2015-11-01 18:22:27 +00:00
//sanitise this value
if(delay < 0)
return MICROBIT_INVALID_PARAMETER;
// 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);
2015-11-01 18:22:27 +00:00
if (delay > 0)
{
animationDelay = delay;
animationTick = 0;
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
}
}
else
{
return MICROBIT_BUSY;
}
return MICROBIT_OK;
}
/**
* 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.
*
* @param s The string to display.
* @param delay The time to delay between characters, in milliseconds. Must be > 0.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* uBit.display.printAsync("abc123",400);
* @endcode
*/
int MicroBitDisplay::printAsync(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0 )
return MICROBIT_INVALID_PARAMETER;
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
{
printingChar = 0;
printingText = s;
animationDelay = delay;
animationTick = 0;
animationMode = ANIMATION_MODE_PRINT_TEXT;
2015-11-01 18:22:27 +00:00
}
else
{
return MICROBIT_BUSY;
}
2015-11-01 18:22:27 +00:00
return MICROBIT_OK;
2015-11-01 18:22:27 +00:00
}
/**
* 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)
2015-11-01 18:22:27 +00:00
* @param delay The time to delay between characters, in milliseconds. set to 0 to display forever. (default 0).
*
* Example:
* @code
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
* uBit.display.print(i,400);
* @endcode
*/
2015-11-01 18:22:27 +00:00
int MicroBitDisplay::printAsync(MicroBitImage i, int x, int y, int alpha, int delay)
{
2015-11-01 18:22:27 +00:00
if(delay < 0)
return MICROBIT_INVALID_PARAMETER;
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
{
image.paste(i, x, y, alpha);
2015-11-01 18:22:27 +00:00
if(delay > 0)
{
animationDelay = delay;
animationTick = 0;
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
}
}
else
{
return MICROBIT_BUSY;
}
2015-11-01 18:22:27 +00:00
return MICROBIT_OK;
}
/**
* Prints the given character to the display, and wait for it to complete.
*
* @param c The character to display.
* @param delay The time to delay between characters, in milliseconds. Must be > 0.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* uBit.display.print('p');
* uBit.display.print('p',100);
* @endcode
*/
int MicroBitDisplay::print(char c, int delay)
{
if (delay < 0)
return MICROBIT_INVALID_PARAMETER;
// 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 someone called stopAnimation(), then we simply skip...
if (animationMode == ANIMATION_MODE_NONE)
{
this->printAsync(c, delay);
if (delay > 0)
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
2015-11-01 18:22:27 +00:00
else
{
return MICROBIT_CANCELLED;
}
return MICROBIT_OK;
}
/**
* Prints the given string to the display, one character at a time.
* Uses the given delay between characters.
* Blocks the calling thread until all the text has been displayed.
*
* @param s The string to display.
* @param delay The time to delay between characters, in milliseconds. Must be > 0.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* uBit.display.print("abc123",400);
* @endcode
*/
int MicroBitDisplay::print(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0 )
return MICROBIT_INVALID_PARAMETER;
// 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 someone called stopAnimation(), then we simply skip...
if (animationMode == ANIMATION_MODE_NONE)
{
this->printAsync(s, delay);
2015-11-01 18:22:27 +00:00
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
2015-11-01 18:22:27 +00:00
else
{
return MICROBIT_CANCELLED;
}
return MICROBIT_OK;
}
/**
* Prints the given image to the display.
* Blocks the calling thread until all the text has been displayed.
*
* @param i The image to display.
* @param delay The time to display the image for, or zero to show the image forever. Must be >= 0.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
* uBit.display.print(i,400);
* @endcode
*/
int MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
{
if(delay < 0)
return MICROBIT_INVALID_PARAMETER;
// 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 someone called stopAnimation(), then we simply skip...
if (animationMode == ANIMATION_MODE_NONE)
{
this->printAsync(i, x, y, alpha, delay);
if (delay > 0)
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
2015-11-01 18:22:27 +00:00
else
{
return MICROBIT_CANCELLED;
}
return MICROBIT_OK;
}
/**
* Scrolls the given string to the display, from right to left.
* Uses the given delay between characters.
* Returns immediately, and executes the animation asynchronously.
*
* @param s The string to display.
* @param delay The time to delay between each update to the display, in milliseconds. Must be > 0.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* uBit.display.scrollAsync("abc123",100);
* @endcode
*/
int MicroBitDisplay::scrollAsync(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0)
return MICROBIT_INVALID_PARAMETER;
// If the display is free, it's our turn to display.
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
{
scrollingPosition = width-1;
scrollingChar = 0;
scrollingText = s;
animationDelay = delay;
animationTick = 0;
animationMode = ANIMATION_MODE_SCROLL_TEXT;
}
2015-11-01 18:22:27 +00:00
else
{
return MICROBIT_BUSY;
}
return MICROBIT_OK;
}
/**
* Scrolls the given image across the display, from right to left.
* Returns immediately, and executes the animation asynchronously.
*
* @param image The image to display.
* @param delay The time to delay between each update to the display, in milliseconds. Must be > 0.
* @param stride The number of pixels to move in each update. Default value is the screen width.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
* uBit.display.scrollAsync(i,100,1);
* @endcode
*/
int MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
{
//sanitise the delay value
if(delay <= 0)
return MICROBIT_INVALID_PARAMETER;
// If the display is free, it's our turn to display.
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
{
scrollingImagePosition = stride < 0 ? width : -image.getWidth();
scrollingImageStride = stride;
scrollingImage = image;
scrollingImageRendered = false;
animationDelay = delay;
animationTick = 0;
animationMode = ANIMATION_MODE_SCROLL_IMAGE;
}
2015-11-01 18:22:27 +00:00
else
{
return MICROBIT_BUSY;
}
return MICROBIT_OK;
}
/**
* Scrolls the given string to the display, from right to left.
* Uses the given delay between characters.
* Blocks the calling thread until all the text has been displayed.
*
* @param s The string to display.
* @param delay The time to delay between each update to the display, in milliseconds. Must be > 0.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* uBit.display.scrollString("abc123",100);
* @endcode
*/
int MicroBitDisplay::scroll(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0)
return MICROBIT_INVALID_PARAMETER;
// 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 someone called stopAnimation(), then we simply skip...
if (animationMode == ANIMATION_MODE_NONE)
{
// Start the effect.
this->scrollAsync(s, delay);
// Wait for completion.
2015-11-01 18:22:27 +00:00
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
else
{
return MICROBIT_CANCELLED;
}
return MICROBIT_OK;
}
/**
* Scrolls the given image across the display, from right to left.
* Blocks the calling thread until all the text has been displayed.
*
* @param image The image to display.
* @param delay The time to delay between each update to the display, in milliseconds. Must be > 0.
* @param stride The number of pixels to move in each update. Default value is the screen width.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
* uBit.display.scroll(i,100,1);
* @endcode
*/
int MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
{
//sanitise the delay value
if(delay <= 0)
return MICROBIT_INVALID_PARAMETER;
// 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 someone called stopAnimation(), then we simply skip...
if (animationMode == ANIMATION_MODE_NONE)
{
// Start the effect.
this->scrollAsync(image, delay, stride);
// Wait for completion.
2015-11-01 18:22:27 +00:00
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
else
{
return MICROBIT_CANCELLED;
}
return MICROBIT_OK;
}
/**
* "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
* Returns immediately.
*
* @param image The image to display.
* @param delay The time to delay between each update to the display, in milliseconds. Must be > 0.
* @param stride The number of pixels to move in each update. Default value is the screen width.
2015-11-01 18:22:27 +00:00
* @return MICROBIT_OK, MICROBIT_BUSY if the screen is in use, or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* const int heart_w = 10;
* const int heart_h = 5;
* const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, };
*
* MicroBitImage i(heart_w,heart_h,heart);
* uBit.display.animateAsync(i,100,5);
* @endcode
*/
int MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition)
{
//sanitise the delay value
if(delay <= 0)
return MICROBIT_INVALID_PARAMETER;
2015-11-01 18:22:27 +00:00
// If the display is free, we can display.
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
{
// Assume right to left functionality, to align with scrollString()
stride = -stride;
//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;