microbit-dal/source/MicroBitDisplay.cpp

884 lines
25 KiB
C++
Raw Normal View History

/**
* Class definition for a MicroBitDisplay.
*
* A MicroBitDisplay represents the LED matrix array on the MicroBit device.
*/
#include "MicroBit.h"
#include "MicroBitMatrixMaps.h"
#include <new>
#include "nrf_gpio.h"
#include "mbed.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 Point representation of an LED on a matrix
* Used to handle non-linear matrix layouts.
*/
MatrixPoint::MatrixPoint(uint8_t x, uint8_t y)
{
this->x = x;
this->y = y;
}
/**
* 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_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()
{
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_MAX_BRIGHTNESS && brightness > MICROBIT_DISPLAY_MIN_BRIGHTNESS)
renderTimer.attach(this, &MicroBitDisplay::renderFinish, (((float)brightness) / ((float)MICROBIT_DISPLAY_MAX_BRIGHTNESS)) * (float)MICROBIT_DISPLAY_REFRESH_PERIOD);
//this will take around 23us to execute
if(brightness <= MICROBIT_DISPLAY_MIN_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 (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;
this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
}
}
/**
* Broadcasts an event onto the shared MessageBus
* @param eventCode The ID of the event that has occurred.
*/
void MicroBitDisplay::sendEvent(uint16_t eventCode)
{
MicroBitEvent evt(id,eventCode);
}
/**
* 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;
this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
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;
this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
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;
this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
return;
}
scrollingImagePosition += scrollingImageStride;
scrollingImageRendered = true;
}
/**
* Internal animateImage update method.
* Paste the stored bitmap at the appropriate point and stop on the last frame.
*/
void MicroBitDisplay::updateAnimateImage()
{
//wait until we have rendered the last position to give a continuous animation.
if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered)
{
animationMode = ANIMATION_MODE_NONE;
this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
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.
*/
void MicroBitDisplay::resetAnimation(uint16_t delay)
{
//sanitise this value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
// Reset any ongoing animation.
if (animationMode != ANIMATION_MODE_NONE)
{
animationMode = ANIMATION_MODE_NONE;
this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
// Clear the display and setup the animation timers.
this->image.clear();
this->animationDelay = delay;
this->animationTick = delay-1;
}
/**
* 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 timer ticks.
*
* Example:
* @code
* uBit.display.printAsync("abc123",400);
* @endcode
*/
void MicroBitDisplay::printAsync(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
this->resetAnimation(delay);
this->printingChar = 0;
this->printingText = s;
animationMode = ANIMATION_MODE_PRINT_TEXT;
}
/**
* Prints the given character to the display.
*
* @param c The character to display.
*
* Example:
* @code
* uBit.display.print('p');
* @endcode
*/
void MicroBitDisplay::print(char c, int delay)
{
image.print(c, 0, 0);
if(delay <= 0)
return;
this->animationDelay = delay;
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
// Wait for completion.
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
/**
* 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 timer ticks.
*
* Example:
* @code
* uBit.display.print("abc123",400);
* @endcode
*/
void MicroBitDisplay::print(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
// Start the effect.
this->printAsync(s, delay);
// Wait for completion.
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
/**
* 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 delay between characters, in timer ticks.
*
* Example:
* @code
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
* uBit.display.print(i,400);
* @endcode
*/
void MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
{
image.paste(i, x, y, alpha);
if(delay <= 0)
return;
this->animationDelay = delay;
animationMode = ANIMATION_MODE_PRINT_CHARACTER;
// Wait for completion.
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
/**
* 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 characters, in timer ticks.
*
* Example:
* @code
* uBit.display.scrollAsync("abc123",100);
* @endcode
*/
void MicroBitDisplay::scrollAsync(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
this->resetAnimation(delay);
this->scrollingPosition = width-1;
this->scrollingChar = 0;
this->scrollingText = s;
animationMode = ANIMATION_MODE_SCROLL_TEXT;
}
/**
* 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 scroll update, in timer ticks. Has a default.
* @param stride The number of pixels to move in each quantum. Has a default.
*
* Example:
* @code
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
* uBit.display.scrollAsync(i,100,1);
* @endcode
*/
void MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
{
//sanitise the delay value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
this->resetAnimation(delay);
this->scrollingImagePosition = stride < 0 ? width : -image.getWidth();
this->scrollingImageStride = stride;
this->scrollingImage = image;
this->scrollingImageRendered = false;
animationMode = ANIMATION_MODE_SCROLL_IMAGE;
}
/**
* 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 characters, in timer ticks.
*
* Example:
* @code
* uBit.display.scrollString("abc123",100);
* @endcode
*/
void MicroBitDisplay::scroll(ManagedString s, int delay)
{
//sanitise this value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
// Start the effect.
this->scrollAsync(s, delay);
// Wait for completion.
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
/**
* 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 scroll update, in timer ticks. Has a default.
* @param stride The number of pixels to move in each quantum. Has a default.
*
* Example:
* @code
* MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
* uBit.display.scroll(i,100,1);
* @endcode
*/
void MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
{
//sanitise the delay value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
// Start the effect.
this->scrollAsync(image, delay, stride);
// Wait for completion.
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
/**
* "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 animation update, in timer ticks. Has a default.
* @param stride The number of pixels to move in each quantum. Has a default.
*
* 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
*/
void MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition)
{
// Assume right to left functionality, to align with scrollString()
stride = -stride;
//sanitise the delay value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
// Reset any ongoing animation.
if (animationMode != ANIMATION_MODE_NONE)
{
animationMode = ANIMATION_MODE_NONE;
this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
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;
}
/**
* "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 animation update, in timer ticks. Has a default.
* @param stride The number of pixels to move in each quantum. Has a default.
*
* 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
*/
void MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition)
{
//sanitise the delay value
if(delay <= 0 )
delay = MICROBIT_DEFAULT_SCROLL_SPEED;
// Start the effect.
this->animateAsync(image, delay, stride, startingPosition);
// Wait for completion.
fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
}
/**
* Sets the display brightness to the specified level.
* @param b The brightness to set the brightness to, in the range 0..255.
*
* Example:
* @code
* uBit.display.setBrightness(255); //max brightness
* @endcode
*/
void MicroBitDisplay::setBrightness(int b)
{
//sanitise the brightness level
if(b < 0 || b > 255)
return;
this->brightness = b;
}
/**
* 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(uint8_t position)
{
//perform a switch on position to restrict range to distinct values
switch(position){
case MICROBIT_DISPLAY_ROTATION_0:
this->rotation = MICROBIT_DISPLAY_ROTATION_0;
break;
case MICROBIT_DISPLAY_ROTATION_90:
this->rotation = MICROBIT_DISPLAY_ROTATION_90;
break;
case MICROBIT_DISPLAY_ROTATION_180:
this->rotation = MICROBIT_DISPLAY_ROTATION_180;
break;
case MICROBIT_DISPLAY_ROTATION_270:
this->rotation = MICROBIT_DISPLAY_ROTATION_270;
break;
}
}
/**
* 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
while( outerCount < 100000)
{
int coldata = 0;
int i = 0;
//if we have hit the row limit - reset both the bit mask and the row variable
if(strobeRow == 3)
{
strobeRow = 0;
strobeBitMsk = 0x20;
}
// Calculate the bitpattern to write.
for (i = 0; i<MICROBIT_DISPLAY_COLUMN_COUNT; i++)
{
int bitMsk = 0x10 >> matrixMap[i][strobeRow].x; //chars are right aligned but read left to right
int y = matrixMap[i][strobeRow].y;
if(chars[characterCount][y] & bitMsk)
coldata |= (1 << i);
}
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, 0xF0); //clear port 0 4-7
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | 0x1F); // clear port 1 8-12
//write the new bit pattern
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT0, ~coldata<<4 & 0xF0); //set port 0 4-7
nrf_gpio_port_write(NRF_GPIO_PORT_SELECT_PORT1, strobeBitMsk | (~coldata>>4 & 0x1F)); //set port 1 8-12
//set i to an obscene number.
i = 100000;
//burn cycles
while(i>0)
i--;
//update the bit mask and row count
strobeBitMsk <<= 1;
strobeRow++;
outerCount++;
}
}
}
}
/**
* Updates the font property of this object with the new font.
* @param font the new font that will be used to render characters..
*/
void MicroBitDisplay::setFont(MicroBitFont font)
{
this->font = font;
}
/**
* Retreives the font object used for rendering characters on the display.
*/
MicroBitFont MicroBitDisplay::getFont()
{
return this->font;
}
/**
* Captures the bitmap currently being rendered on the display.
*/
MicroBitImage MicroBitDisplay::screenShot()
{
return image.crop(0,0,MICROBIT_DISPLAY_WIDTH,MICROBIT_DISPLAY_HEIGHT);
}