2015-08-12 10:53:41 +00:00
|
|
|
/**
|
|
|
|
* Class definition for a MicroBitDisplay.
|
|
|
|
*
|
|
|
|
* A MicroBitDisplay represents the LED matrix array on the MicroBit device.
|
|
|
|
*/
|
2015-09-11 15:39:38 +00:00
|
|
|
#include "mbed.h"
|
2015-08-12 10:53:41 +00:00
|
|
|
#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.
|
2016-01-13 16:16:18 +00:00
|
|
|
*
|
2015-08-12 10:53:41 +00:00
|
|
|
* Example:
|
2016-01-13 16:16:18 +00:00
|
|
|
* @code
|
2015-08-12 10:53:41 +00:00
|
|
|
* MicroBitDisplay display(MICROBIT_ID_DISPLAY, 5, 5),
|
|
|
|
* @endcode
|
|
|
|
*/
|
2016-01-13 16:16:18 +00:00
|
|
|
MicroBitDisplay::MicroBitDisplay(uint16_t id, uint8_t x, uint8_t y) :
|
2015-08-12 10:53:41 +00:00
|
|
|
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);
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
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;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-09-02 11:42:24 +00:00
|
|
|
this->setBrightness(MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS);
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
this->mode = DISPLAY_MODE_BLACK_AND_WHITE;
|
|
|
|
this->animationMode = ANIMATION_MODE_NONE;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
uBit.flags |= MICROBIT_FLAG_DISPLAY_RUNNING;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal frame update method, used to strobe the display.
|
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* TODO: Write a more efficient, complementary variation of this method for the case where
|
2015-08-12 10:53:41 +00:00
|
|
|
* MICROBIT_DISPLAY_ROW_COUNT > MICROBIT_DISPLAY_COLUMN_COUNT.
|
2016-01-13 16:16:18 +00:00
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
void MicroBitDisplay::systemTick()
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
if(!(uBit.flags & MICROBIT_FLAG_DISPLAY_RUNNING))
|
|
|
|
return;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
|
|
|
// Move on to the next row.
|
2015-08-12 10:53:41 +00:00
|
|
|
strobeBitMsk <<= 1;
|
|
|
|
strobeRow++;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
//reset the row counts and bit mask when we have hit the max.
|
|
|
|
if(strobeRow == MICROBIT_DISPLAY_ROW_COUNT){
|
|
|
|
strobeRow = 0;
|
2016-01-13 16:16:18 +00:00
|
|
|
strobeBitMsk = 0x20;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(mode == DISPLAY_MODE_BLACK_AND_WHITE)
|
|
|
|
render();
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(mode == DISPLAY_MODE_GREYSCALE)
|
|
|
|
{
|
|
|
|
greyscaleBitMsk = 0x01;
|
|
|
|
timingCount = 0;
|
|
|
|
renderGreyscale();
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
// 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
|
2016-01-13 16:16:18 +00:00
|
|
|
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, 0xF0 | (nrf_gpio_port_read(NRF_GPIO_PORT_SELECT_PORT0) & 0x0F));
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
// clear port 1 8-12 for the current row
|
2016-01-13 16:16:18 +00:00
|
|
|
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | 0x1F);
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MicroBitDisplay::render()
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-10-25 21:51:33 +00:00
|
|
|
// Simple optimisation. If display is at zero brightness, there's nothign to do.
|
2015-08-12 10:53:41 +00:00
|
|
|
if(brightness == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int coldata = 0;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
// 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;
|
2016-01-13 16:16:18 +00:00
|
|
|
int t = x;
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(rotation == MICROBIT_DISPLAY_ROTATION_90)
|
|
|
|
{
|
|
|
|
x = width - 1 - y;
|
|
|
|
y = t;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(rotation == MICROBIT_DISPLAY_ROTATION_180)
|
|
|
|
{
|
|
|
|
x = width - 1 - x;
|
|
|
|
y = height - 1 - y;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(rotation == MICROBIT_DISPLAY_ROTATION_270)
|
|
|
|
{
|
|
|
|
x = y;
|
|
|
|
y = height - 1 - t;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-10-25 03:58:03 +00:00
|
|
|
if(image.getBitmap()[y*(width*2)+x])
|
2015-08-12 10:53:41 +00:00
|
|
|
coldata |= (1 << i);
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
//write the new bit pattern
|
|
|
|
//set port 0 4-7 and retain lower 4 bits
|
2016-01-13 16:16:18 +00:00
|
|
|
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, (~coldata<<4 & 0xF0) | (nrf_gpio_port_read(NRF_GPIO_PORT_SELECT_PORT0) & 0x0F));
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
//set port 1 8-12 for the current row
|
2016-01-13 16:16:18 +00:00
|
|
|
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | (~coldata>>4 & 0x1F));
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
//timer does not have enough resolution for brightness of 1. 23.53 us
|
2015-09-02 11:42:24 +00:00
|
|
|
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);
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
//this will take around 23us to execute
|
2015-09-02 11:42:24 +00:00
|
|
|
if(brightness <= MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS)
|
2015-08-12 10:53:41 +00:00
|
|
|
renderFinish();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MicroBitDisplay::renderGreyscale()
|
|
|
|
{
|
|
|
|
int coldata = 0;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
// 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;
|
2016-01-13 16:16:18 +00:00
|
|
|
int t = x;
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(rotation == MICROBIT_DISPLAY_ROTATION_90)
|
|
|
|
{
|
|
|
|
x = width - 1 - y;
|
|
|
|
y = t;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(rotation == MICROBIT_DISPLAY_ROTATION_180)
|
|
|
|
{
|
|
|
|
x = width - 1 - x;
|
|
|
|
y = height - 1 - y;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(rotation == MICROBIT_DISPLAY_ROTATION_270)
|
|
|
|
{
|
|
|
|
x = y;
|
|
|
|
y = height - 1 - t;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-10-25 03:58:03 +00:00
|
|
|
if(min(image.getBitmap()[y * (width * 2) + x],brightness) & greyscaleBitMsk)
|
2015-08-12 10:53:41 +00:00
|
|
|
coldata |= (1 << i);
|
2016-01-13 16:16:18 +00:00
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
//write the new bit pattern
|
|
|
|
//set port 0 4-7 and retain lower 4 bits
|
2016-01-13 16:16:18 +00:00
|
|
|
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, (~coldata<<4 & 0xF0) | (nrf_gpio_port_read(NRF_GPIO_PORT_SELECT_PORT0) & 0x0F));
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
//set port 1 8-12 for the current row
|
2016-01-13 16:16:18 +00:00
|
|
|
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | (~coldata>>4 & 0x1F));
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
if(timingCount > MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH-1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
greyscaleBitMsk <<= 1;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
renderTimer.attach(this,&MicroBitDisplay::renderGreyscale, timings[timingCount++]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Periodic callback, that we use to perform any animations we have running.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
MicroBitDisplay::animationUpdate()
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-10-25 21:51:33 +00:00
|
|
|
// If there's no ongoing animation, then nothing to do.
|
2015-08-12 10:53:41 +00:00
|
|
|
if (animationMode == ANIMATION_MODE_NONE)
|
|
|
|
return;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
|
|
|
animationTick += FIBER_TICK_PERIOD_MS;
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(animationTick >= animationDelay)
|
|
|
|
{
|
|
|
|
animationTick = 0;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if (animationMode == ANIMATION_MODE_SCROLL_TEXT)
|
|
|
|
this->updateScrollText();
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if (animationMode == ANIMATION_MODE_PRINT_TEXT)
|
|
|
|
this->updatePrintText();
|
|
|
|
|
|
|
|
if (animationMode == ANIMATION_MODE_SCROLL_IMAGE)
|
|
|
|
this->updateScrollImage();
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE)
|
|
|
|
this->updateAnimateImage();
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(animationMode == ANIMATION_MODE_PRINT_CHARACTER)
|
2015-08-14 00:06:41 +00:00
|
|
|
{
|
|
|
|
animationMode = ANIMATION_MODE_NONE;
|
2015-09-11 15:39:38 +00:00
|
|
|
this->sendAnimationCompleteEvent();
|
2015-08-14 00:06:41 +00:00
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Broadcasts an event onto the shared MessageBus
|
|
|
|
* @param eventCode The ID of the event that has occurred.
|
|
|
|
*/
|
2015-09-11 15:39:38 +00:00
|
|
|
void MicroBitDisplay::sendAnimationCompleteEvent()
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-09-11 15:39:38 +00:00
|
|
|
// Signal that we've completed an animation.
|
2015-10-17 19:35:16 +00:00
|
|
|
MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
2015-09-11 15:39:38 +00:00
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
// Wake up a fiber that was blocked on the animation (if any).
|
|
|
|
MicroBitEvent(MICROBIT_ID_NOTIFY_ONE, MICROBIT_DISPLAY_EVT_FREE);
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-13 16:16:18 +00:00
|
|
|
* Internal scrollText update method.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Shift the screen image by one pixel to the left. If necessary, paste in the next char.
|
2016-01-13 16:16:18 +00:00
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
void MicroBitDisplay::updateScrollText()
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
image.shiftLeft(1);
|
|
|
|
scrollingPosition++;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if (scrollingPosition == width + MICROBIT_DISPLAY_SPACING)
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
scrollingPosition = 0;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
image.print(scrollingChar < scrollingText.length() ? scrollingText.charAt(scrollingChar) : ' ',width,0);
|
|
|
|
|
|
|
|
if (scrollingChar > scrollingText.length())
|
|
|
|
{
|
|
|
|
animationMode = ANIMATION_MODE_NONE;
|
2015-09-11 15:39:38 +00:00
|
|
|
this->sendAnimationCompleteEvent();
|
2015-08-12 10:53:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
scrollingChar++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-13 16:16:18 +00:00
|
|
|
* Internal printText update method.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Paste in the next char in the string.
|
2016-01-13 16:16:18 +00:00
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
void MicroBitDisplay::updatePrintText()
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
image.print(printingChar < printingText.length() ? printingText.charAt(printingChar) : ' ',0,0);
|
|
|
|
|
|
|
|
if (printingChar > printingText.length())
|
|
|
|
{
|
2016-01-13 16:16:18 +00:00
|
|
|
animationMode = ANIMATION_MODE_NONE;
|
2015-09-11 15:39:38 +00:00
|
|
|
|
|
|
|
this->sendAnimationCompleteEvent();
|
2015-08-12 10:53:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
printingChar++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-13 16:16:18 +00:00
|
|
|
* Internal scrollImage update method.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Paste the stored bitmap at the appropriate point.
|
2016-01-13 16:16:18 +00:00
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
void MicroBitDisplay::updateScrollImage()
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
|
|
|
image.clear();
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
if ((image.paste(scrollingImage, scrollingImagePosition, 0, 0) == 0) && scrollingImageRendered)
|
|
|
|
{
|
2016-01-13 16:16:18 +00:00
|
|
|
animationMode = ANIMATION_MODE_NONE;
|
|
|
|
this->sendAnimationCompleteEvent();
|
2015-09-11 15:39:38 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scrollingImagePosition += scrollingImageStride;
|
|
|
|
scrollingImageRendered = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-01-13 16:16:18 +00:00
|
|
|
* Internal animateImage update method.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Paste the stored bitmap at the appropriate point and stop on the last frame.
|
2016-01-13 16:16:18 +00:00
|
|
|
*/
|
2015-08-12 10:53:41 +00:00
|
|
|
void MicroBitDisplay::updateAnimateImage()
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
//wait until we have rendered the last position to give a continuous animation.
|
|
|
|
if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered)
|
|
|
|
{
|
2016-01-13 16:16:18 +00:00
|
|
|
animationMode = ANIMATION_MODE_NONE;
|
2015-10-18 18:09:54 +00:00
|
|
|
this->clear();
|
2016-01-13 16:16:18 +00:00
|
|
|
this->sendAnimationCompleteEvent();
|
2015-08-12 10:53:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
if(scrollingImagePosition > 0)
|
|
|
|
image.shiftLeft(-scrollingImageStride);
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
image.paste(scrollingImage, scrollingImagePosition, 0, 0);
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
scrollingImageRendered = true;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
scrollingImagePosition += scrollingImageStride;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resets the current given animation.
|
|
|
|
*/
|
2015-10-17 19:35:16 +00:00
|
|
|
void MicroBitDisplay::stopAnimation()
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
// Reset any ongoing animation.
|
|
|
|
if (animationMode != ANIMATION_MODE_NONE)
|
|
|
|
{
|
|
|
|
animationMode = ANIMATION_MODE_NONE;
|
2015-10-17 19:35:16 +00:00
|
|
|
|
|
|
|
// 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);
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
// Clear the display and setup the animation timers.
|
|
|
|
this->image.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-17 19:35:16 +00:00
|
|
|
* Blocks the current fiber until the display is available (i.e. not effect is being displayed).
|
|
|
|
* Animations are queued until their time to display.
|
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
*/
|
2015-10-17 19:35:16 +00:00
|
|
|
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.
|
2016-01-13 16:16:18 +00:00
|
|
|
*
|
2015-10-17 19:35:16 +00:00
|
|
|
* Example:
|
2016-01-13 16:16:18 +00:00
|
|
|
* @code
|
2015-10-17 19:35:16 +00:00
|
|
|
* 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-10-17 19:35:16 +00:00
|
|
|
{
|
2015-11-01 18:22:27 +00:00
|
|
|
//sanitise this value
|
|
|
|
if(delay < 0)
|
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-10-17 19:35:16 +00:00
|
|
|
|
|
|
|
// 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;
|
2015-10-17 19:35:16 +00:00
|
|
|
}
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-17 19:35:16 +00:00
|
|
|
* Prints the given string to the display, one character at a time, if the display is not in use.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Returns immediately, and executes the animation asynchronously.
|
|
|
|
*
|
|
|
|
* @param s The string to display.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @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.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.display.printAsync("abc123",400);
|
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitDisplay::printAsync(ManagedString s, int delay)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
//sanitise this value
|
|
|
|
if(delay <= 0 )
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
|
|
|
{
|
|
|
|
printingChar = 0;
|
|
|
|
printingText = s;
|
|
|
|
animationDelay = delay;
|
|
|
|
animationTick = 0;
|
|
|
|
|
|
|
|
animationMode = ANIMATION_MODE_PRINT_TEXT;
|
2016-01-13 16:16:18 +00:00
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return MICROBIT_BUSY;
|
2015-10-17 19:35:16 +00:00
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_OK;
|
2015-11-01 18:22:27 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-17 19:35:16 +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)
|
2016-01-13 16:16:18 +00:00
|
|
|
* @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).
|
2015-10-17 19:35:16 +00:00
|
|
|
*
|
|
|
|
* 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-10-17 19:35:16 +00:00
|
|
|
{
|
2015-11-01 18:22:27 +00:00
|
|
|
if(delay < 0)
|
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
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-10-17 19:35:16 +00:00
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-17 19:35:16 +00:00
|
|
|
* Prints the given character to the display, and wait for it to complete.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* @param c The character to display.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @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.
|
2016-01-13 16:16:18 +00:00
|
|
|
*
|
2015-08-12 10:53:41 +00:00
|
|
|
* Example:
|
2016-01-13 16:16:18 +00:00
|
|
|
* @code
|
2015-08-12 10:53:41 +00:00
|
|
|
* uBit.display.print('p');
|
2015-10-17 19:35:16 +00:00
|
|
|
* uBit.display.print('p',100);
|
2015-08-12 10:53:41 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitDisplay::print(char c, int delay)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-10-25 21:51:33 +00:00
|
|
|
if (delay < 0)
|
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
// 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);
|
2015-10-31 10:27:38 +00:00
|
|
|
if (delay > 0)
|
|
|
|
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
2015-10-17 19:35:16 +00:00
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return MICROBIT_CANCELLED;
|
|
|
|
}
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @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.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.display.print("abc123",400);
|
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitDisplay::print(ManagedString s, int delay)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
//sanitise this value
|
|
|
|
if(delay <= 0 )
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
// 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-10-17 19:35:16 +00:00
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return MICROBIT_CANCELLED;
|
|
|
|
}
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prints the given image to the display.
|
|
|
|
* Blocks the calling thread until all the text has been displayed.
|
|
|
|
*
|
|
|
|
* @param i The image to display.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @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.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
|
|
|
|
* uBit.display.print(i,400);
|
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-10-25 21:51:33 +00:00
|
|
|
if(delay < 0)
|
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
// 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);
|
2015-10-31 10:27:38 +00:00
|
|
|
if (delay > 0)
|
|
|
|
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
|
2015-10-17 19:35:16 +00:00
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return MICROBIT_CANCELLED;
|
|
|
|
}
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @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.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* uBit.display.scrollAsync("abc123",100);
|
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitDisplay::scrollAsync(ManagedString s, int delay)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
//sanitise this value
|
2015-10-25 21:51:33 +00:00
|
|
|
if(delay <= 0)
|
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
// 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;
|
|
|
|
|
2015-10-18 18:09:54 +00:00
|
|
|
animationDelay = delay;
|
|
|
|
animationTick = 0;
|
2015-10-17 19:35:16 +00:00
|
|
|
animationMode = ANIMATION_MODE_SCROLL_TEXT;
|
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return MICROBIT_BUSY;
|
|
|
|
}
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scrolls the given image across the display, from right to left.
|
|
|
|
* Returns immediately, and executes the animation asynchronously.
|
2015-10-25 21:51:33 +00:00
|
|
|
*
|
2015-08-12 10:53:41 +00:00
|
|
|
* @param image The image to display.
|
2015-10-25 21:51:33 +00:00
|
|
|
* @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.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* @code
|
|
|
|
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
|
|
|
|
* uBit.display.scrollAsync(i,100,1);
|
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
//sanitise the delay value
|
2015-10-25 21:51:33 +00:00
|
|
|
if(delay <= 0)
|
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-10-17 19:35:16 +00:00
|
|
|
// If the display is free, it's our turn to display.
|
|
|
|
if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
|
|
|
|
{
|
2015-10-18 18:09:54 +00:00
|
|
|
scrollingImagePosition = stride < 0 ? width : -image.getWidth();
|
|
|
|
scrollingImageStride = stride;
|
|
|
|
scrollingImage = image;
|
|
|
|
scrollingImageRendered = false;
|
2015-10-17 19:35:16 +00:00
|
|
|
|
2015-10-18 18:09:54 +00:00
|
|
|
animationDelay = delay;
|
|
|
|
animationTick = 0;
|
2015-10-17 19:35:16 +00:00
|
|
|
animationMode = ANIMATION_MODE_SCROLL_IMAGE;
|
|
|
|
}
|
2015-11-01 18:22:27 +00:00
|
|
|
else
|
|
|
|
{
|