microbit-dal/source/MicroBitDisplay.cpp

913 lines
26 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
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;
}
if(image.bitmap[y*(width*2)+x])
coldata |= (1 << i);
}
//write the new bit pattern
//set port 0 4-7 and retain lower 4 bits
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;
}
if(min(image.bitmap[y * (width * 2) + x],brightness) & greyscaleBitMsk)
coldata |= (1 << i);
}
//write the new bit pattern
//set port 0 4-7 and retain lower 4 bits
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 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);
}
/**
* 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;
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.
* @param delay the delay after which the animation is reset. Must be > 0.
* @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
*/
int MicroBitDisplay::resetAnimation(uint16_t delay)
{
//sanitise this value
if(delay <= 0 )
return MICROBIT_INVALID_PARAMETER;
// Reset any ongoing animation.
if (animationMode != ANIMATION_MODE_NONE)
{
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();
}
// Clear the display and setup the animation timers.
this->image.clear();
this->animationDelay = delay;
this->animationTick = delay-1;
return MICROBIT_OK;
}
/**
* Prints the given string to the display, one character at a time.
* 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 characters, in milliseconds. Must be > 0.
* @return MICROBIT_OK, 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;
this->resetAnimation(delay);
this->printingChar = 0;
this->printingText = s;
animationMode = ANIMATION_MODE_PRINT_TEXT;
return MICROBIT_OK;
}
/**
* Prints the given character to the display.
*
* @param c The character to display.
* @param delay The time to delay between characters, in milliseconds. Must be > 0.
* @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
*
* Example:
* @code
* uBit.display.print('p');
* @endcode
*/
int MicroBitDisplay::print(char c, int delay)
{
if (delay < 0)
return MICROBIT_INVALID_PARAMETER;
image.print(c, 0, 0);
if (delay == 0)
return MICROBIT_OK;
this->animationDelay = delay;
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
// Wait for completion.
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
nonce = uBit.MessageBus.nonce();
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
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.
* @return MICROBIT_OK, 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;
// Start the effect.
this->printAsync(s, delay);
// Wait for completion.
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
nonce = uBit.MessageBus.nonce();
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
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.
* @return MICROBIT_OK, 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;
image.paste(i, x, y, alpha);
if(delay == 0)
return MICROBIT_OK;
this->animationDelay = delay;
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
// Wait for completion.
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
nonce = uBit.MessageBus.nonce();
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
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.
* @return MICROBIT_OK, 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;
this->resetAnimation(delay);
this->scrollingPosition = width-1;
this->scrollingChar = 0;
this->scrollingText = s;
animationMode = ANIMATION_MODE_SCROLL_TEXT;
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.
* @return MICROBIT_OK, 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;
this->resetAnimation(delay);
this->scrollingImagePosition = stride < 0 ? width : -image.getWidth();
this->scrollingImageStride = stride;
this->scrollingImage = image;
this->scrollingImageRendered = false;
animationMode = ANIMATION_MODE_SCROLL_IMAGE;
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.
* @return MICROBIT_OK, 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;
// Start the effect.
this->scrollAsync(s, delay);
// Wait for completion.
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
nonce = uBit.MessageBus.nonce();
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
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.
* @return MICROBIT_OK, 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;
// Start the effect.
this->scrollAsync(image, delay, stride);
// Wait for completion.
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
nonce = uBit.MessageBus.nonce();
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
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.
* @return MICROBIT_OK, 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;
// Assume right to left functionality, to align with scrollString()
stride = -stride;
// Reset any ongoing animation.
if (animationMode != ANIMATION_MODE_NONE)
{
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();
}
this->animationDelay = delay;
this->animationTick = delay-1;
//calculate starting position which is offset by the stride
this->scrollingImagePosition = (startingPosition == MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS)?MICROBIT_DISPLAY_WIDTH + stride:startingPosition;
this->scrollingImageStride = stride;
this->scrollingImage = image;
this->scrollingImageRendered = false;
animationMode = ANIMATION_MODE_ANIMATE_IMAGE;
return MICROBIT_OK;
}
/**
* "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
* Blocks the calling thread until the animation is complete.
*
* @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.
* @return MICROBIT_OK, 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.animate(i,100,5);
* @endcode
*/
int MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition)
{
//sanitise the delay value
if(delay <= 0)
return MICROBIT_INVALID_PARAMETER;
// Start the effect.
this->animateAsync(image, delay, stride, startingPosition);
// Wait for completion.
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
nonce = uBit.MessageBus.nonce();
fiber_wait_for_event(MICROBIT_ID_ALERT, nonce);
return MICROBIT_OK;
}
/**
* Sets the display brightness to the specified level.
* @param b The brightness to set the brightness to, in the range 0..255.
* @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER
*
* Example:
* @code
* uBit.display.setBrightness(255); //max brightness
* @endcode
*/
int MicroBitDisplay::setBrightness(int b)
{
//sanitise the brightness level
if(b < 0 || b > 255)
return MICROBIT_INVALID_PARAMETER;
this->brightness = b;
return MICROBIT_OK;
}
/**
* Sets the mode of the display.
* @param mode The mode to swap the display into. (can be either DISPLAY_MODE_GREYSCALE, or DISPLAY_MODE_NORMAL)
*
* Example:
* @code
* uBit.display.setDisplayMode(DISPLAY_MODE_GREYSCALE); //per pixel brightness
* @endcode
*/
void MicroBitDisplay::setDisplayMode(DisplayMode mode)
{
this->mode = mode;
}
/**
* Fetches the current brightness of this display.
* @return the brightness of this display, in the range 0..255.
*
* Example:
* @code
* uBit.display.getBrightness(); //the current brightness
* @endcode
*/
int MicroBitDisplay::getBrightness()
{
return this->brightness;
}
/**
* Rotates the display to the given position.
* Axis aligned values only.
*
* Example:
* @code
* uBit.display.rotateTo(MICROBIT_DISPLAY_ROTATION_180); //rotates 180 degrees from original orientation
* @endcode
*/
void MicroBitDisplay::rotateTo(DisplayRotation rotation)
{
this->rotation = rotation;
}
/**
* Enables the display, should only be called if the display is disabled.
*
* Example:
* @code
* uBit.display.enable(); //reenables the display mechanics
* @endcode
*/
void MicroBitDisplay::enable()
{
if(!(uBit.flags & MICROBIT_FLAG_DISPLAY_RUNNING))
{
setBrightness(brightness);
uBit.flags |= MICROBIT_FLAG_DISPLAY_RUNNING; //set the display running flag
}
}
/**
* Disables the display, should only be called if the display is enabled.
* Display must be disabled to avoid MUXing of edge connector pins.
*
* Example:
* @code
* uBit.display.disable(); //disables the display
* @endcode
*/
void MicroBitDisplay::disable()
{
if(uBit.flags & MICROBIT_FLAG_DISPLAY_RUNNING)
{
uBit.flags &= ~MICROBIT_FLAG_DISPLAY_RUNNING; //unset the display running flag
}
}
/**
* Clears the current image on the display.
* Simplifies the process, you can also use uBit.display.image.clear
*
* Example:
* @code
* uBit.display.clear(); //clears the display
* @endcode
*/
void MicroBitDisplay::clear()
{
image.clear();
}
/**
* Displays "=(" and an accompanying status code infinitely.
* @param statusCode the appropriate status code - 0 means no code will be displayed. Status codes must be in the range 0-255.
*
* Example:
* @code
* uBit.display.error(20);
* @endcode
*/
void MicroBitDisplay::error(int statusCode)
{
__disable_irq(); //stop ALL interrupts
if(statusCode < 0 || statusCode > 255)
statusCode = 0;
disable(); //relinquish PWMOut's control
uint8_t strobeRow = 0;
uint8_t strobeBitMsk = 0x20;
//point to the font stored in Flash
const unsigned char * fontLocation = MicroBitFont::defaultFont;
//get individual digits of status code, and place it into a single array/
const uint8_t* chars[MICROBIT_DISPLAY_ERROR_CHARS] = { panicFace, fontLocation+((((statusCode/100 % 10)+48)-MICROBIT_FONT_ASCII_START) * 5), fontLocation+((((statusCode/10 % 10)+48)-MICROBIT_FONT_ASCII_START) * 5), fontLocation+((((statusCode % 10)+48)-MICROBIT_FONT_ASCII_START) * 5)};
//enter infinite loop.
while(1)
{
//iterate through our chars :)
for(int characterCount = 0; characterCount < MICROBIT_DISPLAY_ERROR_CHARS; characterCount++)
{
int outerCount = 0;
//display the current character