mbed.org local branch of microbit-dal. The real version lives in git at https://github.com/lancaster-university/microbit-dal

Dependencies:   BLE_API nRF51822 mbed-dev-bin

Dependents:   microbit Microbit IoTChallenge1 microbit ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MicroBitDisplay.cpp Source File

MicroBitDisplay.cpp

00001 /*
00002 The MIT License (MIT)
00003 
00004 Copyright (c) 2016 British Broadcasting Corporation.
00005 This software is provided by Lancaster University by arrangement with the BBC.
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a
00008 copy of this software and associated documentation files (the "Software"),
00009 to deal in the Software without restriction, including without limitation
00010 the rights to use, copy, modify, merge, publish, distribute, sublicense,
00011 and/or sell copies of the Software, and to permit persons to whom the
00012 Software is furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00023 DEALINGS IN THE SOFTWARE.
00024 */
00025 
00026 /**
00027   * Class definition for MicroBitDisplay.
00028   *
00029   * A MicroBitDisplay represents the LED matrix array on the micro:bit.
00030   */
00031 #include "MicroBitConfig.h"
00032 #include "MicroBitDisplay.h"
00033 #include "MicroBitSystemTimer.h"
00034 #include "MicroBitFiber.h"
00035 #include "ErrorNo.h"
00036 #include "NotifyEvents.h"
00037 
00038 const int greyScaleTimings[MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH] = {1, 23, 70, 163, 351, 726, 1476, 2976};
00039 
00040 /**
00041   * Constructor.
00042   *
00043   * Create a software representation the micro:bit's 5x5 LED matrix.
00044   * The display is initially blank.
00045   *
00046   * @param id The id the display should use when sending events on the MessageBus. Defaults to MICROBIT_ID_DISPLAY.
00047   *
00048   * @param map The mapping information that relates pin inputs/outputs to physical screen coordinates.
00049   *            Defaults to microbitMatrixMap, defined in MicroBitMatrixMaps.h.
00050   *
00051   * @code
00052   * MicroBitDisplay display;
00053   * @endcode
00054   */
00055 MicroBitDisplay::MicroBitDisplay(uint16_t id, const MatrixMap &map) :
00056     matrixMap(map),
00057     image(map.width*2,map.height)
00058 {
00059     uint32_t row_mask;
00060 
00061     this->id = id;
00062     this->width = map.width;
00063     this->height = map.height;
00064     this->rotation = MICROBIT_DISPLAY_ROTATION_0;
00065 
00066     row_mask = 0;
00067     col_mask = 0;
00068     strobeRow = 0;
00069     row_mask = 0;
00070 
00071     for (int i = matrixMap.rowStart; i < matrixMap.rowStart + matrixMap.rows; i++)
00072         row_mask |= 0x01 << i;
00073 
00074     for (int i = matrixMap.columnStart; i < matrixMap.columnStart + matrixMap.columns; i++)
00075         col_mask |= 0x01 << i;
00076 
00077     LEDMatrix = new PortOut(Port0, row_mask | col_mask);
00078 
00079     this->greyscaleBitMsk = 0x01;
00080     this->timingCount = 0;
00081     this->setBrightness(MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS);
00082     this->mode = DISPLAY_MODE_BLACK_AND_WHITE;
00083     this->animationMode = ANIMATION_MODE_NONE;
00084     this->lightSensor = NULL;
00085 
00086     system_timer_add_component(this);
00087 
00088     status |= MICROBIT_COMPONENT_RUNNING;
00089 }
00090 
00091 /**
00092   * Internal frame update method, used to strobe the display.
00093   *
00094   * TODO: Write a more efficient, complementary variation of this method for the case where
00095   * MICROBIT_DISPLAY_ROW_COUNT > MICROBIT_DISPLAY_COLUMN_COUNT.
00096   */
00097 void MicroBitDisplay::systemTick()
00098 {
00099     if(!(status & MICROBIT_COMPONENT_RUNNING))
00100         return;
00101 
00102     if(mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
00103     {
00104         renderWithLightSense();
00105         return;
00106     }
00107 
00108     // Move on to the next row.
00109     strobeRow++;
00110 
00111     //reset the row counts and bit mask when we have hit the max.
00112     if(strobeRow == matrixMap.rows)
00113         strobeRow = 0;
00114 
00115     if(mode == DISPLAY_MODE_BLACK_AND_WHITE)
00116         render();
00117 
00118     if(mode == DISPLAY_MODE_GREYSCALE)
00119     {
00120         greyscaleBitMsk = 0x01;
00121         timingCount = 0;
00122         renderGreyscale();
00123     }
00124 
00125     // Update text and image animations if we need to.
00126     this->animationUpdate();
00127 }
00128 
00129 void MicroBitDisplay::renderFinish()
00130 {
00131     *LEDMatrix = 0;
00132 }
00133 
00134 void MicroBitDisplay::render()
00135 {
00136     // Simple optimisation.
00137     // If display is at zero brightness, there's nothing to do.
00138     if(brightness == 0)
00139         return;
00140 
00141     // Calculate the bitpattern to write.
00142     uint32_t row_data = 0x01 << (matrixMap.rowStart + strobeRow);
00143     uint32_t col_data = 0;
00144 
00145     for (int i = 0; i < matrixMap.columns; i++)
00146     {
00147         int index = (i * matrixMap.rows) + strobeRow;
00148 
00149         int x = matrixMap.map[index].x;
00150         int y = matrixMap.map[index].y;
00151         int t = x;
00152 
00153         if(rotation == MICROBIT_DISPLAY_ROTATION_90)
00154         {
00155                 x = width - 1 - y;
00156                 y = t;
00157         }
00158 
00159         if(rotation == MICROBIT_DISPLAY_ROTATION_180)
00160         {
00161                 x = width - 1 - x;
00162                 y = height - 1 - y;
00163         }
00164 
00165         if(rotation == MICROBIT_DISPLAY_ROTATION_270)
00166         {
00167                 x = y;
00168                 y = height - 1 - t;
00169         }
00170 
00171         if(image.getBitmap()[y*(width*2)+x])
00172             col_data |= (1 << i);
00173     }
00174 
00175     // Invert column bits (as we're sinking not sourcing power), and mask off any unused bits.
00176     col_data = ~col_data << matrixMap.columnStart & col_mask;
00177 
00178     // Write the new bit pattern
00179     *LEDMatrix = col_data | row_data;
00180 
00181     //timer does not have enough resolution for brightness of 1. 23.53 us
00182     if(brightness != MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS && brightness > MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS)
00183         renderTimer.attach_us(this, &MicroBitDisplay::renderFinish, (((brightness * 950) / (MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS)) * system_timer_get_period()));
00184 
00185     //this will take around 23us to execute
00186     if(brightness <= MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS)
00187         renderFinish();
00188 }
00189 
00190 void MicroBitDisplay::renderWithLightSense()
00191 {
00192     //reset the row counts and bit mask when we have hit the max.
00193     if(strobeRow == matrixMap.rows + 1)
00194     {
00195         MicroBitEvent(id, MICROBIT_DISPLAY_EVT_LIGHT_SENSE);
00196         strobeRow = 0;
00197     }
00198     else
00199     {
00200         render();
00201         this->animationUpdate();
00202 
00203         // Move on to the next row.
00204         strobeRow++;
00205     }
00206 
00207 }
00208 
00209 void MicroBitDisplay::renderGreyscale()
00210 {
00211     uint32_t row_data = 0x01 << (matrixMap.rowStart + strobeRow);
00212     uint32_t col_data = 0;
00213 
00214     // Calculate the bitpattern to write.
00215     for (int i = 0; i < matrixMap.columns; i++)
00216     {
00217         int index = (i * matrixMap.rows) + strobeRow;
00218 
00219         int x = matrixMap.map[index].x;
00220         int y = matrixMap.map[index].y;
00221         int t = x;
00222 
00223         if(rotation == MICROBIT_DISPLAY_ROTATION_90)
00224         {
00225                 x = width - 1 - y;
00226                 y = t;
00227         }
00228 
00229         if(rotation == MICROBIT_DISPLAY_ROTATION_180)
00230         {
00231                 x = width - 1 - x;
00232                 y = height - 1 - y;
00233         }
00234 
00235         if(rotation == MICROBIT_DISPLAY_ROTATION_270)
00236         {
00237                 x = y;
00238                 y = height - 1 - t;
00239         }
00240 
00241         if(min(image.getBitmap()[y * (width * 2) + x],brightness) & greyscaleBitMsk)
00242             col_data |= (1 << i);
00243     }
00244 
00245     // Invert column bits (as we're sinking not sourcing power), and mask off any unused bits.
00246     col_data = ~col_data << matrixMap.columnStart & col_mask;
00247 
00248     // Write the new bit pattern
00249     *LEDMatrix = col_data | row_data;
00250 
00251     if(timingCount > MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH-1)
00252         return;
00253 
00254     greyscaleBitMsk <<= 1;
00255 
00256     if(timingCount < 3)
00257     {
00258         wait_us(greyScaleTimings[timingCount++]);
00259         renderGreyscale();
00260         return;
00261     }
00262     renderTimer.attach_us(this,&MicroBitDisplay::renderGreyscale, greyScaleTimings[timingCount++]);
00263 }
00264 
00265 /**
00266   * Periodic callback, that we use to perform any animations we have running.
00267   */
00268 void
00269 MicroBitDisplay::animationUpdate()
00270 {
00271     // If there's no ongoing animation, then nothing to do.
00272     if (animationMode == ANIMATION_MODE_NONE)
00273         return;
00274 
00275     animationTick += system_timer_get_period();
00276 
00277     if(animationTick >= animationDelay)
00278     {
00279         animationTick = 0;
00280 
00281         if (animationMode == ANIMATION_MODE_SCROLL_TEXT)
00282             this->updateScrollText();
00283 
00284         if (animationMode == ANIMATION_MODE_PRINT_TEXT)
00285             this->updatePrintText();
00286 
00287         if (animationMode == ANIMATION_MODE_SCROLL_IMAGE)
00288             this->updateScrollImage();
00289 
00290         if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE || animationMode == ANIMATION_MODE_ANIMATE_IMAGE_WITH_CLEAR)
00291             this->updateAnimateImage();
00292 
00293         if(animationMode == ANIMATION_MODE_PRINT_CHARACTER)
00294         {
00295             animationMode = ANIMATION_MODE_NONE;
00296             this->sendAnimationCompleteEvent();
00297         }
00298     }
00299 }
00300 
00301 /**
00302   * Broadcasts an event onto the defult EventModel indicating that the
00303   * current animation has completed.
00304   */
00305 void MicroBitDisplay::sendAnimationCompleteEvent()
00306 {
00307     // Signal that we've completed an animation.
00308     MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
00309 
00310     // Wake up a fiber that was blocked on the animation (if any).
00311     MicroBitEvent(MICROBIT_ID_NOTIFY_ONE, MICROBIT_DISPLAY_EVT_FREE);
00312 }
00313 
00314 /**
00315   * Internal scrollText update method.
00316   * Shift the screen image by one pixel to the left. If necessary, paste in the next char.
00317   */
00318 void MicroBitDisplay::updateScrollText()
00319 {
00320     image.shiftLeft(1);
00321     scrollingPosition++;
00322 
00323     if (scrollingPosition == width + MICROBIT_DISPLAY_SPACING)
00324     {
00325         scrollingPosition = 0;
00326 
00327         image.print(scrollingChar < scrollingText.length() ? scrollingText.charAt(scrollingChar) : ' ',width,0);
00328 
00329         if (scrollingChar > scrollingText.length())
00330         {
00331             animationMode = ANIMATION_MODE_NONE;
00332             this->sendAnimationCompleteEvent();
00333             return;
00334         }
00335         scrollingChar++;
00336    }
00337 }
00338 
00339 /**
00340   * Internal printText update method.
00341   * Paste the next character in the string.
00342   */
00343 void MicroBitDisplay::updatePrintText()
00344 {
00345     image.print(printingChar < printingText.length() ? printingText.charAt(printingChar) : ' ',0,0);
00346 
00347     if (printingChar > printingText.length())
00348     {
00349         animationMode = ANIMATION_MODE_NONE;
00350 
00351         this->sendAnimationCompleteEvent();
00352         return;
00353     }
00354 
00355     printingChar++;
00356 }
00357 
00358 /**
00359   * Internal scrollImage update method.
00360   * Paste the stored bitmap at the appropriate point.
00361   */
00362 void MicroBitDisplay::updateScrollImage()
00363 {
00364     image.clear();
00365 
00366     if (((image.paste(scrollingImage, scrollingImagePosition, 0, 0) == 0) && scrollingImageRendered) || scrollingImageStride == 0)
00367     {
00368         animationMode = ANIMATION_MODE_NONE;
00369         this->sendAnimationCompleteEvent();
00370 
00371         return;
00372     }
00373 
00374     scrollingImagePosition += scrollingImageStride;
00375     scrollingImageRendered = true;
00376 }
00377 
00378 /**
00379   * Internal animateImage update method.
00380   * Paste the stored bitmap at the appropriate point and stop on the last frame.
00381   */
00382 void MicroBitDisplay::updateAnimateImage()
00383 {
00384     //wait until we have rendered the last position to give a continuous animation.
00385     if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered)
00386     {
00387         if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE_WITH_CLEAR)
00388             this->clear();
00389 
00390         animationMode = ANIMATION_MODE_NONE;
00391 
00392         this->sendAnimationCompleteEvent();
00393         return;
00394     }
00395 
00396     if(scrollingImagePosition > 0)
00397         image.shiftLeft(-scrollingImageStride);
00398 
00399     image.paste(scrollingImage, scrollingImagePosition, 0, 0);
00400 
00401     if(scrollingImageStride == 0)
00402     {
00403         animationMode = ANIMATION_MODE_NONE;
00404         this->sendAnimationCompleteEvent();
00405     }
00406 
00407     scrollingImageRendered = true;
00408 
00409     scrollingImagePosition += scrollingImageStride;
00410 }
00411 
00412 /**
00413   * Resets the current given animation.
00414   */
00415 void MicroBitDisplay::stopAnimation()
00416 {
00417     // Reset any ongoing animation.
00418     if (animationMode != ANIMATION_MODE_NONE)
00419     {
00420         animationMode = ANIMATION_MODE_NONE;
00421 
00422         // Indicate that we've completed an animation.
00423         MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
00424 
00425         // Wake up aall fibers that may blocked on the animation (if any).
00426         MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
00427     }
00428 
00429     // Clear the display and setup the animation timers.
00430     this->image.clear();
00431 }
00432 
00433 /**
00434   * Blocks the current fiber until the display is available (i.e. does not effect is being displayed).
00435   * Animations are queued until their time to display.
00436   */
00437 void MicroBitDisplay::waitForFreeDisplay()
00438 {
00439     // If there's an ongoing animation, wait for our turn to display.
00440     if (animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED)
00441         fiber_wait_for_event(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
00442 }
00443 
00444 /**
00445   * Blocks the current fiber until the current animation has finished.
00446   * If the scheduler is not running, this call will essentially perform a spinning wait.
00447   */
00448 void MicroBitDisplay::fiberWait()
00449 {
00450     if (fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE) == MICROBIT_NOT_SUPPORTED)
00451         while(animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED)
00452             __WFE();
00453 }
00454 
00455 /**
00456   * Prints the given character to the display, if it is not in use.
00457   *
00458   * @param c The character to display.
00459   *
00460   * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
00461   *              or until the Displays next use.
00462   *
00463   * @return MICROBIT_OK, MICROBIT_BUSY is the screen is in use, or MICROBIT_INVALID_PARAMETER.
00464   *
00465   * @code
00466   * display.printAsync('p');
00467   * display.printAsync('p',100);
00468   * @endcode
00469   */
00470 int MicroBitDisplay::printCharAsync(char c, int delay)
00471 {
00472     //sanitise this value
00473     if(delay < 0)
00474         return MICROBIT_INVALID_PARAMETER;
00475 
00476     // If the display is free, it's our turn to display.
00477     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00478     {
00479         image.print(c, 0, 0);
00480 
00481         if (delay > 0)
00482         {
00483             animationDelay = delay;
00484             animationTick = 0;
00485             animationMode = ANIMATION_MODE_PRINT_CHARACTER;
00486         }
00487     }
00488     else
00489     {
00490         return MICROBIT_BUSY;
00491     }
00492 
00493     return MICROBIT_OK;
00494 }
00495 
00496 /**
00497   * Prints the given ManagedString to the display, one character at a time.
00498   * Returns immediately, and executes the animation asynchronously.
00499   *
00500   * @param s The string to display.
00501   *
00502   * @param delay The time to delay between characters, in milliseconds. Must be > 0.
00503   *              Defaults to: MICROBIT_DEFAULT_PRINT_SPEED.
00504   *
00505   * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
00506   *
00507   * @code
00508   * display.printAsync("abc123",400);
00509   * @endcode
00510   */
00511 int MicroBitDisplay::printAsync(ManagedString s, int delay)
00512 {
00513     if (s.length() == 1)
00514         return printCharAsync(s.charAt(0));
00515 
00516     //sanitise this value
00517     if (delay <= 0 )
00518         return MICROBIT_INVALID_PARAMETER;
00519 
00520     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00521     {
00522         printingChar = 0;
00523         printingText = s;
00524         animationDelay = delay;
00525         animationTick = 0;
00526 
00527         animationMode = ANIMATION_MODE_PRINT_TEXT;
00528     }
00529     else
00530     {
00531         return MICROBIT_BUSY;
00532     }
00533 
00534     return MICROBIT_OK;
00535 }
00536 
00537 /**
00538   * Prints the given image to the display, if the display is not in use.
00539   * Returns immediately, and executes the animation asynchronously.
00540   *
00541   * @param i The image to display.
00542   *
00543   * @param x The horizontal position on the screen to display the image. Defaults to 0.
00544   *
00545   * @param y The vertical position on the screen to display the image. Defaults to 0.
00546   *
00547   * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
00548   *
00549   * @param delay The time to delay between characters, in milliseconds. Defaults to 0.
00550   *
00551   * @code
00552   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00553   * display.print(i,400);
00554   * @endcode
00555   */
00556 int MicroBitDisplay::printAsync(MicroBitImage i, int x, int y, int alpha, int delay)
00557 {
00558     if(delay < 0)
00559         return MICROBIT_INVALID_PARAMETER;
00560 
00561     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00562     {
00563         image.paste(i, x, y, alpha);
00564 
00565         if(delay > 0)
00566         {
00567             animationDelay = delay;
00568             animationTick = 0;
00569             animationMode = ANIMATION_MODE_PRINT_CHARACTER;
00570         }
00571     }
00572     else
00573     {
00574         return MICROBIT_BUSY;
00575     }
00576 
00577     return MICROBIT_OK;
00578 }
00579 
00580 /**
00581   * Prints the given character to the display.
00582   *
00583   * @param c The character to display.
00584   *
00585   * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
00586   *              or until the Displays next use.
00587   *
00588   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00589   *
00590   * @code
00591   * display.printAsync('p');
00592   * display.printAsync('p',100);
00593   * @endcode
00594   */
00595 int MicroBitDisplay::printChar(char c, int delay)
00596 {
00597     if (delay < 0)
00598         return MICROBIT_INVALID_PARAMETER;
00599 
00600     // If there's an ongoing animation, wait for our turn to display.
00601     this->waitForFreeDisplay();
00602 
00603     // If the display is free, it's our turn to display.
00604     // If someone called stopAnimation(), then we simply skip...
00605     if (animationMode == ANIMATION_MODE_NONE)
00606     {
00607         this->printCharAsync(c, delay);
00608 
00609         if (delay > 0)
00610             fiberWait();
00611     }
00612     else
00613     {
00614         return MICROBIT_CANCELLED;
00615     }
00616 
00617     return MICROBIT_OK;
00618 }
00619 
00620 /**
00621   * Prints the given string to the display, one character at a time.
00622   *
00623   * Blocks the calling thread until all the text has been displayed.
00624   *
00625   * @param s The string to display.
00626   *
00627   * @param delay The time to delay between characters, in milliseconds. Defaults
00628   *              to: MICROBIT_DEFAULT_PRINT_SPEED.
00629   *
00630   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00631   *
00632   * @code
00633   * display.print("abc123",400);
00634   * @endcode
00635   */
00636 int MicroBitDisplay::print(ManagedString s, int delay)
00637 {
00638     //sanitise this value
00639     if(delay <= 0 )
00640         return MICROBIT_INVALID_PARAMETER;
00641 
00642     // If there's an ongoing animation, wait for our turn to display.
00643     this->waitForFreeDisplay();
00644 
00645     // If the display is free, it's our turn to display.
00646     // If someone called stopAnimation(), then we simply skip...
00647     if (animationMode == ANIMATION_MODE_NONE)
00648     {
00649         if (s.length() == 1)
00650         {
00651             return printCharAsync(s.charAt(0));
00652         }
00653         else
00654         {
00655             this->printAsync(s, delay);
00656             fiberWait();
00657         }
00658     }
00659     else
00660     {
00661         return MICROBIT_CANCELLED;
00662     }
00663 
00664     return MICROBIT_OK;
00665 }
00666 
00667 /**
00668   * Prints the given image to the display.
00669   * Blocks the calling thread until all the image has been displayed.
00670   *
00671   * @param i The image to display.
00672   *
00673   * @param x The horizontal position on the screen to display the image. Defaults to 0.
00674   *
00675   * @param y The vertical position on the screen to display the image. Defaults to 0.
00676   *
00677   * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
00678   *
00679   * @param delay The time to display the image for, or zero to show the image forever. Defaults to 0.
00680   *
00681   * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
00682   *
00683   * @code
00684   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00685   * display.print(i,400);
00686   * @endcode
00687   */
00688 int MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
00689 {
00690     if(delay < 0)
00691         return MICROBIT_INVALID_PARAMETER;
00692 
00693     // If there's an ongoing animation, wait for our turn to display.
00694     this->waitForFreeDisplay();
00695 
00696     // If the display is free, it's our turn to display.
00697     // If someone called stopAnimation(), then we simply skip...
00698     if (animationMode == ANIMATION_MODE_NONE)
00699     {
00700         this->printAsync(i, x, y, alpha, delay);
00701 
00702         if (delay > 0)
00703             fiberWait();
00704     }
00705     else
00706     {
00707         return MICROBIT_CANCELLED;
00708     }
00709 
00710     return MICROBIT_OK;
00711 }
00712 
00713 /**
00714   * Scrolls the given string to the display, from right to left.
00715   * Returns immediately, and executes the animation asynchronously.
00716   *
00717   * @param s The string to display.
00718   *
00719   * @param delay The time to delay between characters, in milliseconds. Defaults
00720   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00721   *
00722   * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
00723   *
00724   * @code
00725   * display.scrollAsync("abc123",100);
00726   * @endcode
00727   */
00728 int MicroBitDisplay::scrollAsync(ManagedString s, int delay)
00729 {
00730     //sanitise this value
00731     if(delay <= 0)
00732         return MICROBIT_INVALID_PARAMETER;
00733 
00734     // If the display is free, it's our turn to display.
00735     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00736     {
00737         scrollingPosition = width-1;
00738         scrollingChar = 0;
00739         scrollingText = s;
00740 
00741         animationDelay = delay;
00742         animationTick = 0;
00743         animationMode = ANIMATION_MODE_SCROLL_TEXT;
00744     }
00745     else
00746     {
00747         return MICROBIT_BUSY;
00748     }
00749 
00750     return MICROBIT_OK;
00751 }
00752 
00753 /**
00754   * Scrolls the given image across the display, from right to left.
00755   * Returns immediately, and executes the animation asynchronously.
00756   *
00757   * @param image The image to display.
00758   *
00759   * @param delay The time between updates, in milliseconds. Defaults
00760   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00761   *
00762   * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
00763   *
00764   * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
00765   *
00766   * @code
00767   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00768   * display.scrollAsync(i,100,1);
00769   * @endcode
00770   */
00771 int MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
00772 {
00773     //sanitise the delay value
00774     if(delay <= 0)
00775         return MICROBIT_INVALID_PARAMETER;
00776 
00777     // If the display is free, it's our turn to display.
00778     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00779     {
00780         scrollingImagePosition = stride < 0 ? width : -image.getWidth();
00781         scrollingImageStride = stride;
00782         scrollingImage = image;
00783         scrollingImageRendered = false;
00784 
00785         animationDelay = stride == 0 ? 0 : delay;
00786         animationTick = 0;
00787         animationMode = ANIMATION_MODE_SCROLL_IMAGE;
00788     }
00789     else
00790     {
00791         return MICROBIT_BUSY;
00792     }
00793 
00794     return MICROBIT_OK;
00795 }
00796 
00797 /**
00798   * Scrolls the given string across the display, from right to left.
00799   * Blocks the calling thread until all text has been displayed.
00800   *
00801   * @param s The string to display.
00802   *
00803   * @param delay The time to delay between characters, in milliseconds. Defaults
00804   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00805   *
00806   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00807   *
00808   * @code
00809   * display.scroll("abc123",100);
00810   * @endcode
00811   */
00812 int MicroBitDisplay::scroll(ManagedString s, int delay)
00813 {
00814     //sanitise this value
00815     if(delay <= 0)
00816         return MICROBIT_INVALID_PARAMETER;
00817 
00818     // If there's an ongoing animation, wait for our turn to display.
00819     this->waitForFreeDisplay();
00820 
00821     // If the display is free, it's our turn to display.
00822     // If someone called stopAnimation(), then we simply skip...
00823     if (animationMode == ANIMATION_MODE_NONE)
00824     {
00825         // Start the effect.
00826         this->scrollAsync(s, delay);
00827 
00828         // Wait for completion.
00829         fiberWait();
00830     }
00831     else
00832     {
00833         return MICROBIT_CANCELLED;
00834     }
00835 
00836     return MICROBIT_OK;
00837 }
00838 
00839 /**
00840   * Scrolls the given image across the display, from right to left.
00841   * Blocks the calling thread until all the text has been displayed.
00842   *
00843   * @param image The image to display.
00844   *
00845   * @param delay The time between updates, in milliseconds. Defaults
00846   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00847   *
00848   * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
00849   *
00850   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00851   *
00852   * @code
00853   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00854   * display.scroll(i,100,1);
00855   * @endcode
00856   */
00857 int MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
00858 {
00859     //sanitise the delay value
00860     if(delay <= 0)
00861         return MICROBIT_INVALID_PARAMETER;
00862 
00863     // If there's an ongoing animation, wait for our turn to display.
00864     this->waitForFreeDisplay();
00865 
00866     // If the display is free, it's our turn to display.
00867     // If someone called stopAnimation(), then we simply skip...
00868     if (animationMode == ANIMATION_MODE_NONE)
00869     {
00870         // Start the effect.
00871         this->scrollAsync(image, delay, stride);
00872 
00873         // Wait for completion.
00874         fiberWait();
00875     }
00876     else
00877     {
00878         return MICROBIT_CANCELLED;
00879     }
00880 
00881     return MICROBIT_OK;
00882 }
00883 
00884 /**
00885   * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
00886   * Returns immediately.
00887   *
00888   * @param image The image to display.
00889   *
00890   * @param delay The time to delay between each update of the display, in milliseconds.
00891   *
00892   * @param stride The number of pixels to shift by in each update.
00893   *
00894   * @param startingPosition the starting position on the display for the animation
00895   *                         to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
00896   *
00897   * @param autoClear defines whether or not the display is automatically cleared once the animation is complete. By default, the display is cleared. Set this parameter to zero to disable the autoClear operation.
00898   *
00899   * @return MICROBIT_OK, MICROBIT_BUSY if the screen is in use, or MICROBIT_INVALID_PARAMETER.
00900   *
00901   * @code
00902   * const int heart_w = 10;
00903   * const int heart_h = 5;
00904   * 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, };
00905   *
00906   * MicroBitImage i(heart_w,heart_h,heart);
00907   * display.animateAsync(i,100,5);
00908   * @endcode
00909   */
00910 int MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition, int autoClear)
00911 {
00912     //sanitise the delay value
00913     if(delay <= 0)
00914         return MICROBIT_INVALID_PARAMETER;
00915 
00916     // If the display is free, we can display.
00917     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00918     {
00919         // Assume right to left functionality, to align with scrollString()
00920         stride = -stride;
00921 
00922         //calculate starting position which is offset by the stride
00923         scrollingImagePosition = (startingPosition == MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS) ? MICROBIT_DISPLAY_WIDTH + stride : startingPosition;
00924         scrollingImageStride = stride;
00925         scrollingImage = image;
00926         scrollingImageRendered = false;
00927 
00928         animationDelay = stride == 0 ? 0 : delay;
00929         animationTick = delay-1;
00930         animationMode = autoClear ? ANIMATION_MODE_ANIMATE_IMAGE_WITH_CLEAR : ANIMATION_MODE_ANIMATE_IMAGE;
00931     }
00932     else
00933     {
00934         return MICROBIT_BUSY;
00935     }
00936 
00937     return MICROBIT_OK;
00938 }
00939 
00940 /**
00941   * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
00942   * Blocks the calling thread until the animation is complete.
00943   *
00944   *
00945   * @param delay The time to delay between each update of the display, in milliseconds.
00946   *
00947   * @param stride The number of pixels to shift by in each update.
00948   *
00949   * @param startingPosition the starting position on the display for the animation
00950   *                         to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
00951   *
00952   * @param autoClear defines whether or not the display is automatically cleared once the animation is complete. By default, the display is cleared. Set this parameter to zero to disable the autoClear operation.
00953   *
00954   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00955   *
00956   * @code
00957   * const int heart_w = 10;
00958   * const int heart_h = 5;
00959   * 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, };
00960   *
00961   * MicroBitImage i(heart_w,heart_h,heart);
00962   * display.animate(i,100,5);
00963   * @endcode
00964   */
00965 int MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition, int autoClear)
00966 {
00967     //sanitise the delay value
00968     if(delay <= 0)
00969         return MICROBIT_INVALID_PARAMETER;
00970 
00971     // If there's an ongoing animation, wait for our turn to display.
00972     this->waitForFreeDisplay();
00973 
00974     // If the display is free, it's our turn to display.
00975     // If someone called stopAnimation(), then we simply skip...
00976     if (animationMode == ANIMATION_MODE_NONE)
00977     {
00978         // Start the effect.
00979         this->animateAsync(image, delay, stride, startingPosition, autoClear);
00980 
00981         // Wait for completion.
00982         //TODO: Put this in when we merge tight-validation
00983         //if (delay > 0)
00984             fiberWait();
00985     }
00986     else
00987     {
00988         return MICROBIT_CANCELLED;
00989     }
00990 
00991     return MICROBIT_OK;
00992 }
00993 
00994 
00995 /**
00996   * Configures the brightness of the display.
00997   *
00998   * @param b The brightness to set the brightness to, in the range 0 - 255.
00999   *
01000   * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER
01001   *
01002   * @code
01003   * display.setBrightness(255); //max brightness
01004   * @endcode
01005   */
01006 int MicroBitDisplay::setBrightness(int b)
01007 {
01008     //sanitise the brightness level
01009     if(b < 0 || b > 255)
01010         return MICROBIT_INVALID_PARAMETER;
01011 
01012     this->brightness = b;
01013 
01014     return MICROBIT_OK;
01015 }
01016 
01017 /**
01018   * Configures the mode of the display.
01019   *
01020   * @param mode The mode to swap the display into. One of: DISPLAY_MODE_GREYSCALE,
01021   *             DISPLAY_MODE_BLACK_AND_WHITE, DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE
01022   *
01023   * @code
01024   * display.setDisplayMode(DISPLAY_MODE_GREYSCALE); //per pixel brightness
01025   * @endcode
01026   */
01027 void MicroBitDisplay::setDisplayMode(DisplayMode mode)
01028 {
01029     if(mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
01030     {
01031         //to reduce the artifacts on the display - increase the tick
01032         if(system_timer_get_period() != MICROBIT_LIGHT_SENSOR_TICK_PERIOD)
01033             system_timer_set_period(MICROBIT_LIGHT_SENSOR_TICK_PERIOD);
01034     }
01035 
01036     if(this->mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE && mode != DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
01037     {
01038         delete this->lightSensor;
01039 
01040         this->lightSensor = NULL;
01041     }
01042 
01043     this->mode = mode;
01044 }
01045 
01046 /**
01047   * Retrieves the mode of the display.
01048   *
01049   * @return the current mode of the display
01050   */
01051 int MicroBitDisplay::getDisplayMode()
01052 {
01053     return this->mode;
01054 }
01055 
01056 /**
01057   * Fetches the current brightness of this display.
01058   *
01059   * @return the brightness of this display, in the range 0..255.
01060   *
01061   * @code
01062   * display.getBrightness(); //the current brightness
01063   * @endcode
01064   */
01065 int MicroBitDisplay::getBrightness()
01066 {
01067     return this->brightness;
01068 }
01069 
01070 /**
01071   * Rotates the display to the given position.
01072   *
01073   * Axis aligned values only.
01074   *
01075   * @code
01076   * display.rotateTo(MICROBIT_DISPLAY_ROTATION_180); //rotates 180 degrees from original orientation
01077   * @endcode
01078   */
01079 void MicroBitDisplay::rotateTo(DisplayRotation rotation)
01080 {
01081     this->rotation = rotation;
01082 }
01083 
01084 /**
01085  * Enables or disables the display entirely, and releases the pins for other uses.
01086  *
01087  * @param enableDisplay true to enabled the display, or false to disable it.
01088  */
01089 void MicroBitDisplay::setEnable(bool enableDisplay)
01090 {
01091     // If we're already in the correct state, then there's nothing to do.
01092     if(((status & MICROBIT_COMPONENT_RUNNING) && enableDisplay) || (!(status & MICROBIT_COMPONENT_RUNNING) && !enableDisplay))
01093         return;
01094 
01095     uint32_t rmask = 0;
01096     uint32_t cmask = 0;
01097 
01098     for (int i = matrixMap.rowStart; i < matrixMap.rowStart + matrixMap.rows; i++)
01099         rmask |= 0x01 << i;
01100 
01101     for (int i = matrixMap.columnStart; i < matrixMap.columnStart + matrixMap.columns; i++)
01102         cmask |= 0x01 << i;
01103 
01104     if (enableDisplay)
01105     {
01106         PortOut p(Port0, rmask | cmask);
01107         status |= MICROBIT_COMPONENT_RUNNING;
01108     }
01109     else
01110     {
01111         PortIn p(Port0, rmask | cmask);
01112         p.mode(PullNone);
01113         status &= ~MICROBIT_COMPONENT_RUNNING;
01114     }
01115 }
01116 
01117 /**
01118   * Enables the display, should only be called if the display is disabled.
01119   *
01120   * @code
01121   * display.enable(); //Enables the display mechanics
01122   * @endcode
01123   *
01124   * @note Only enables the display if the display is currently disabled.
01125   */
01126 void MicroBitDisplay::enable()
01127 {
01128     setEnable(true);
01129 }
01130 
01131 /**
01132   * Disables the display, which releases control of the GPIO pins used by the display,
01133   * which are exposed on the edge connector.
01134   *
01135   * @code
01136   * display.disable(); //disables the display
01137   * @endcode
01138   *
01139   * @note Only disables the display if the display is currently enabled.
01140   */
01141 void MicroBitDisplay::disable()
01142 {
01143     setEnable(false);
01144 }
01145 
01146 /**
01147   * Clears the display of any remaining pixels.
01148   *
01149   * `display.image.clear()` can also be used!
01150   *
01151   * @code
01152   * display.clear(); //clears the display
01153   * @endcode
01154   */
01155 void MicroBitDisplay::clear()
01156 {
01157     image.clear();
01158 }
01159 
01160 /**
01161   * Updates the font that will be used for display operations.
01162   *
01163   * @param font the new font that will be used to render characters.
01164   *
01165   * @note DEPRECATED! Please use MicroBitFont::setSystemFont() instead.
01166   */
01167 void MicroBitDisplay::setFont(MicroBitFont font)
01168 {
01169     MicroBitFont::setSystemFont(font);
01170 }
01171 
01172 /**
01173   * Retrieves the font object used for rendering characters on the display.
01174   *
01175   * @note DEPRECATED! Please use MicroBitFont::getSystemFont() instead.
01176   */
01177 MicroBitFont MicroBitDisplay::getFont()
01178 {
01179     return MicroBitFont::getSystemFont();
01180 }
01181 
01182 /**
01183   * Captures the bitmap currently being rendered on the display.
01184   *
01185   * @return a MicroBitImage containing the captured data.
01186   */
01187 MicroBitImage MicroBitDisplay::screenShot()
01188 {
01189     return image.crop(0,0,MICROBIT_DISPLAY_WIDTH,MICROBIT_DISPLAY_HEIGHT);
01190 }
01191 
01192 /**
01193   * Gives a representative figure of the light level in the current environment
01194   * where are micro:bit is situated.
01195   *
01196   * Internally, it constructs an instance of a MicroBitLightSensor if not already configured
01197   * and sets the display mode to DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE.
01198   *
01199   * This also changes the tickPeriod to MICROBIT_LIGHT_SENSOR_TICK_SPEED so
01200   * that the display does not suffer from artifacts.
01201   *
01202   * @return an indicative light level in the range 0 - 255.
01203   *
01204   * @note this will return 0 on the first call to this method, a light reading
01205   * will be available after the display has activated the light sensor for the
01206   * first time.
01207   */
01208 int MicroBitDisplay::readLightLevel()
01209 {
01210     if(mode != DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
01211     {
01212         setDisplayMode(DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE);
01213         this->lightSensor = new MicroBitLightSensor(matrixMap);
01214     }
01215 
01216     return this->lightSensor->read();
01217 }
01218 
01219 /**
01220   * Destructor for MicroBitDisplay, where we deregister this instance from the array of system components.
01221   */
01222 MicroBitDisplay::~MicroBitDisplay()
01223 {
01224     system_timer_remove_component(this);
01225 }