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"
|
|
|
|
|
2016-02-02 17:05:59 +00:00
|
|
|
const int timings[MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH] = {1, 23, 70, 163, 351, 726, 1476, 2976};
|
2015-11-23 19:46:20 +00:00
|
|
|
MicroBitDisplay *MicroBitDisplay::defaultDisplay = NULL;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
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;
|
2016-01-29 14:03:24 +00:00
|
|
|
this->strobeBitMsk = MICROBIT_DISPLAY_ROW_RESET;
|
2015-08-12 10:53:41 +00:00
|
|
|
this->rotation = MICROBIT_DISPLAY_ROTATION_0;
|
|
|
|
this->greyscaleBitMsk = 0x01;
|
|
|
|
this->timingCount = 0;
|
2016-02-08 14:37:43 +00:00
|
|
|
this->errorTimeout = 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
|
|
|
|
2016-01-29 14:13:41 +00:00
|
|
|
this->lightSensor = NULL;
|
|
|
|
|
2015-11-23 19:46:20 +00:00
|
|
|
if (!this->defaultDisplay)
|
|
|
|
this->defaultDisplay = this;
|
2015-11-24 09:21:46 +00:00
|
|
|
|
|
|
|
fiber_add_system_component(this);
|
|
|
|
|
2015-11-23 19:46:20 +00:00
|
|
|
status |= MICROBIT_COMPONENT_RUNNING;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-11-23 19:46:20 +00:00
|
|
|
if(!(status & MICROBIT_COMPONENT_RUNNING))
|
2015-08-12 10:53:41 +00:00
|
|
|
return;
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2016-01-29 14:03:24 +00:00
|
|
|
if(mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
|
|
|
|
{
|
|
|
|
renderWithLightSense();
|
|
|
|
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-29 14:03:24 +00:00
|
|
|
strobeBitMsk = MICROBIT_DISPLAY_ROW_RESET;
|
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)
|
2015-11-23 19:46:20 +00:00
|
|
|
renderTimer.attach_us(this, &MicroBitDisplay::renderFinish, (((brightness * 950) / (MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS)) * scheduler_get_tick_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();
|
|
|
|
}
|
|
|
|
|
2016-01-29 14:03:24 +00:00
|
|
|
void MicroBitDisplay::renderWithLightSense()
|
|
|
|
{
|
|
|
|
//reset the row counts and bit mask when we have hit the max.
|
|
|
|
if(strobeRow == MICROBIT_DISPLAY_ROW_COUNT + 1)
|
|
|
|
{
|
|
|
|
|
|
|
|
MicroBitEvent(id, MICROBIT_DISPLAY_EVT_LIGHT_SENSE);
|
|
|
|
|
|
|
|
strobeRow = 0;
|
|
|
|
strobeBitMsk = MICROBIT_DISPLAY_ROW_RESET;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
render();
|
|
|
|
this->animationUpdate();
|
|
|
|
|
|
|
|
// Move on to the next row.
|
|
|
|
strobeBitMsk <<= 1;
|
|
|
|
strobeRow++;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
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
|
|
|
|
2016-02-02 17:05:59 +00:00
|
|
|
if(timingCount < 3)
|
|
|
|
{
|
|
|
|
wait_us(timings[timingCount++]);
|
|
|
|
renderGreyscale();
|
|
|
|
return;
|
|
|
|
}
|
2016-02-02 16:36:09 +00:00
|
|
|
renderTimer.attach_us(this,&MicroBitDisplay::renderGreyscale, timings[timingCount++]);
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
2016-01-14 12:56:58 +00:00
|
|
|
if (((image.paste(scrollingImage, scrollingImagePosition, 0, 0) == 0) && scrollingImageRendered) || scrollingImageStride == 0)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
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
|
|
|
|
2016-01-14 12:56:58 +00:00
|
|
|
if(scrollingImageStride == 0)
|
|
|
|
{
|
|
|
|
animationMode = ANIMATION_MODE_NONE;
|
|
|
|
this->sendAnimationCompleteEvent();
|
|
|
|
}
|
|
|
|
|
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;
|
|