Attempting to publish a tree

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

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 << (microbitMatrixMap.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 << (microbitMatrixMap.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)
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         animationMode = ANIMATION_MODE_NONE;
00388         this->clear();
00389         this->sendAnimationCompleteEvent();
00390         return;
00391     }
00392 
00393     if(scrollingImagePosition > 0)
00394         image.shiftLeft(-scrollingImageStride);
00395 
00396     image.paste(scrollingImage, scrollingImagePosition, 0, 0);
00397 
00398     if(scrollingImageStride == 0)
00399     {
00400         animationMode = ANIMATION_MODE_NONE;
00401         this->sendAnimationCompleteEvent();
00402     }
00403 
00404     scrollingImageRendered = true;
00405 
00406     scrollingImagePosition += scrollingImageStride;
00407 }
00408 
00409 /**
00410   * Resets the current given animation.
00411   */
00412 void MicroBitDisplay::stopAnimation()
00413 {
00414     // Reset any ongoing animation.
00415     if (animationMode != ANIMATION_MODE_NONE)
00416     {
00417         animationMode = ANIMATION_MODE_NONE;
00418 
00419         // Indicate that we've completed an animation.
00420         MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
00421 
00422         // Wake up aall fibers that may blocked on the animation (if any).
00423         MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
00424     }
00425 
00426     // Clear the display and setup the animation timers.
00427     this->image.clear();
00428 }
00429 
00430 /**
00431   * Blocks the current fiber until the display is available (i.e. does not effect is being displayed).
00432   * Animations are queued until their time to display.
00433   */
00434 void MicroBitDisplay::waitForFreeDisplay()
00435 {
00436     // If there's an ongoing animation, wait for our turn to display.
00437     if (animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED)
00438         fiber_wait_for_event(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
00439 }
00440 
00441 /**
00442   * Blocks the current fiber until the current animation has finished.
00443   * If the scheduler is not running, this call will essentially perform a spinning wait.
00444   */
00445 void MicroBitDisplay::fiberWait()
00446 {
00447     if (fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE) == MICROBIT_NOT_SUPPORTED)
00448         while(animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED)
00449             __WFE();
00450 }
00451 
00452 /**
00453   * Prints the given character to the display, if it is not in use.
00454   *
00455   * @param c The character to display.
00456   *
00457   * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
00458   *              or until the Displays next use.
00459   *
00460   * @return MICROBIT_OK, MICROBIT_BUSY is the screen is in use, or MICROBIT_INVALID_PARAMETER.
00461   *
00462   * @code
00463   * display.printAsync('p');
00464   * display.printAsync('p',100);
00465   * @endcode
00466   */
00467 int MicroBitDisplay::printCharAsync(char c, int delay)
00468 {
00469     //sanitise this value
00470     if(delay < 0)
00471         return MICROBIT_INVALID_PARAMETER;
00472 
00473     // If the display is free, it's our turn to display.
00474     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00475     {
00476         image.print(c, 0, 0);
00477 
00478         if (delay > 0)
00479         {
00480             animationDelay = delay;
00481             animationTick = 0;
00482             animationMode = ANIMATION_MODE_PRINT_CHARACTER;
00483         }
00484     }
00485     else
00486     {
00487         return MICROBIT_BUSY;
00488     }
00489 
00490     return MICROBIT_OK;
00491 }
00492 
00493 /**
00494   * Prints the given ManagedString to the display, one character at a time.
00495   * Returns immediately, and executes the animation asynchronously.
00496   *
00497   * @param s The string to display.
00498   *
00499   * @param delay The time to delay between characters, in milliseconds. Must be > 0.
00500   *              Defaults to: MICROBIT_DEFAULT_PRINT_SPEED.
00501   *
00502   * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
00503   *
00504   * @code
00505   * display.printAsync("abc123",400);
00506   * @endcode
00507   */
00508 int MicroBitDisplay::printAsync(ManagedString s, int delay)
00509 {
00510     if (s.length() == 1)
00511         return printCharAsync(s.charAt(0));
00512 
00513     //sanitise this value
00514     if (delay <= 0 )
00515         return MICROBIT_INVALID_PARAMETER;
00516 
00517     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00518     {
00519         printingChar = 0;
00520         printingText = s;
00521         animationDelay = delay;
00522         animationTick = 0;
00523 
00524         animationMode = ANIMATION_MODE_PRINT_TEXT;
00525     }
00526     else
00527     {
00528         return MICROBIT_BUSY;
00529     }
00530 
00531     return MICROBIT_OK;
00532 }
00533 
00534 /**
00535   * Prints the given image to the display, if the display is not in use.
00536   * Returns immediately, and executes the animation asynchronously.
00537   *
00538   * @param i The image to display.
00539   *
00540   * @param x The horizontal position on the screen to display the image. Defaults to 0.
00541   *
00542   * @param y The vertical position on the screen to display the image. Defaults to 0.
00543   *
00544   * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
00545   *
00546   * @param delay The time to delay between characters, in milliseconds. Defaults to 0.
00547   *
00548   * @code
00549   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00550   * display.print(i,400);
00551   * @endcode
00552   */
00553 int MicroBitDisplay::printAsync(MicroBitImage i, int x, int y, int alpha, int delay)
00554 {
00555     if(delay < 0)
00556         return MICROBIT_INVALID_PARAMETER;
00557 
00558     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00559     {
00560         image.paste(i, x, y, alpha);
00561 
00562         if(delay > 0)
00563         {
00564             animationDelay = delay;
00565             animationTick = 0;
00566             animationMode = ANIMATION_MODE_PRINT_CHARACTER;
00567         }
00568     }
00569     else
00570     {
00571         return MICROBIT_BUSY;
00572     }
00573 
00574     return MICROBIT_OK;
00575 }
00576 
00577 /**
00578   * Prints the given character to the display.
00579   *
00580   * @param c The character to display.
00581   *
00582   * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
00583   *              or until the Displays next use.
00584   *
00585   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00586   *
00587   * @code
00588   * display.printAsync('p');
00589   * display.printAsync('p',100);
00590   * @endcode
00591   */
00592 int MicroBitDisplay::printChar(char c, int delay)
00593 {
00594     if (delay < 0)
00595         return MICROBIT_INVALID_PARAMETER;
00596 
00597     // If there's an ongoing animation, wait for our turn to display.
00598     this->waitForFreeDisplay();
00599 
00600     // If the display is free, it's our turn to display.
00601     // If someone called stopAnimation(), then we simply skip...
00602     if (animationMode == ANIMATION_MODE_NONE)
00603     {
00604         this->printCharAsync(c, delay);
00605 
00606         if (delay > 0)
00607             fiberWait();
00608     }
00609     else
00610     {
00611         return MICROBIT_CANCELLED;
00612     }
00613 
00614     return MICROBIT_OK;
00615 }
00616 
00617 /**
00618   * Prints the given string to the display, one character at a time.
00619   *
00620   * Blocks the calling thread until all the text has been displayed.
00621   *
00622   * @param s The string to display.
00623   *
00624   * @param delay The time to delay between characters, in milliseconds. Defaults
00625   *              to: MICROBIT_DEFAULT_PRINT_SPEED.
00626   *
00627   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00628   *
00629   * @code
00630   * display.print("abc123",400);
00631   * @endcode
00632   */
00633 int MicroBitDisplay::print(ManagedString s, int delay)
00634 {
00635     //sanitise this value
00636     if(delay <= 0 )
00637         return MICROBIT_INVALID_PARAMETER;
00638 
00639     // If there's an ongoing animation, wait for our turn to display.
00640     this->waitForFreeDisplay();
00641 
00642     // If the display is free, it's our turn to display.
00643     // If someone called stopAnimation(), then we simply skip...
00644     if (animationMode == ANIMATION_MODE_NONE)
00645     {
00646         if (s.length() == 1)
00647         {
00648             return printCharAsync(s.charAt(0));
00649         }
00650         else
00651         {
00652             this->printAsync(s, delay);
00653             fiberWait();
00654         }
00655     }
00656     else
00657     {
00658         return MICROBIT_CANCELLED;
00659     }
00660 
00661     return MICROBIT_OK;
00662 }
00663 
00664 /**
00665   * Prints the given image to the display.
00666   * Blocks the calling thread until all the image has been displayed.
00667   *
00668   * @param i The image to display.
00669   *
00670   * @param x The horizontal position on the screen to display the image. Defaults to 0.
00671   *
00672   * @param y The vertical position on the screen to display the image. Defaults to 0.
00673   *
00674   * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
00675   *
00676   * @param delay The time to display the image for, or zero to show the image forever. Defaults to 0.
00677   *
00678   * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
00679   *
00680   * @code
00681   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00682   * display.print(i,400);
00683   * @endcode
00684   */
00685 int MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
00686 {
00687     if(delay < 0)
00688         return MICROBIT_INVALID_PARAMETER;
00689 
00690     // If there's an ongoing animation, wait for our turn to display.
00691     this->waitForFreeDisplay();
00692 
00693     // If the display is free, it's our turn to display.
00694     // If someone called stopAnimation(), then we simply skip...
00695     if (animationMode == ANIMATION_MODE_NONE)
00696     {
00697         this->printAsync(i, x, y, alpha, delay);
00698 
00699         if (delay > 0)
00700             fiberWait();
00701     }
00702     else
00703     {
00704         return MICROBIT_CANCELLED;
00705     }
00706 
00707     return MICROBIT_OK;
00708 }
00709 
00710 /**
00711   * Scrolls the given string to the display, from right to left.
00712   * Returns immediately, and executes the animation asynchronously.
00713   *
00714   * @param s The string to display.
00715   *
00716   * @param delay The time to delay between characters, in milliseconds. Defaults
00717   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00718   *
00719   * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
00720   *
00721   * @code
00722   * display.scrollAsync("abc123",100);
00723   * @endcode
00724   */
00725 int MicroBitDisplay::scrollAsync(ManagedString s, int delay)
00726 {
00727     //sanitise this value
00728     if(delay <= 0)
00729         return MICROBIT_INVALID_PARAMETER;
00730 
00731     // If the display is free, it's our turn to display.
00732     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00733     {
00734         scrollingPosition = width-1;
00735         scrollingChar = 0;
00736         scrollingText = s;
00737 
00738         animationDelay = delay;
00739         animationTick = 0;
00740         animationMode = ANIMATION_MODE_SCROLL_TEXT;
00741     }
00742     else
00743     {
00744         return MICROBIT_BUSY;
00745     }
00746 
00747     return MICROBIT_OK;
00748 }
00749 
00750 /**
00751   * Scrolls the given image across the display, from right to left.
00752   * Returns immediately, and executes the animation asynchronously.
00753   *
00754   * @param image The image to display.
00755   *
00756   * @param delay The time between updates, in milliseconds. Defaults
00757   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00758   *
00759   * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
00760   *
00761   * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
00762   *
00763   * @code
00764   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00765   * display.scrollAsync(i,100,1);
00766   * @endcode
00767   */
00768 int MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
00769 {
00770     //sanitise the delay value
00771     if(delay <= 0)
00772         return MICROBIT_INVALID_PARAMETER;
00773 
00774     // If the display is free, it's our turn to display.
00775     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00776     {
00777         scrollingImagePosition = stride < 0 ? width : -image.getWidth();
00778         scrollingImageStride = stride;
00779         scrollingImage = image;
00780         scrollingImageRendered = false;
00781 
00782         animationDelay = stride == 0 ? 0 : delay;
00783         animationTick = 0;
00784         animationMode = ANIMATION_MODE_SCROLL_IMAGE;
00785     }
00786     else
00787     {
00788         return MICROBIT_BUSY;
00789     }
00790 
00791     return MICROBIT_OK;
00792 }
00793 
00794 /**
00795   * Scrolls the given string across the display, from right to left.
00796   * Blocks the calling thread until all text has been displayed.
00797   *
00798   * @param s The string to display.
00799   *
00800   * @param delay The time to delay between characters, in milliseconds. Defaults
00801   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00802   *
00803   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00804   *
00805   * @code
00806   * display.scroll("abc123",100);
00807   * @endcode
00808   */
00809 int MicroBitDisplay::scroll(ManagedString s, int delay)
00810 {
00811     //sanitise this value
00812     if(delay <= 0)
00813         return MICROBIT_INVALID_PARAMETER;
00814 
00815     // If there's an ongoing animation, wait for our turn to display.
00816     this->waitForFreeDisplay();
00817 
00818     // If the display is free, it's our turn to display.
00819     // If someone called stopAnimation(), then we simply skip...
00820     if (animationMode == ANIMATION_MODE_NONE)
00821     {
00822         // Start the effect.
00823         this->scrollAsync(s, delay);
00824 
00825         // Wait for completion.
00826         fiberWait();
00827     }
00828     else
00829     {
00830         return MICROBIT_CANCELLED;
00831     }
00832 
00833     return MICROBIT_OK;
00834 }
00835 
00836 /**
00837   * Scrolls the given image across the display, from right to left.
00838   * Blocks the calling thread until all the text has been displayed.
00839   *
00840   * @param image The image to display.
00841   *
00842   * @param delay The time between updates, in milliseconds. Defaults
00843   *              to: MICROBIT_DEFAULT_SCROLL_SPEED.
00844   *
00845   * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
00846   *
00847   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00848   *
00849   * @code
00850   * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
00851   * display.scroll(i,100,1);
00852   * @endcode
00853   */
00854 int MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
00855 {
00856     //sanitise the delay value
00857     if(delay <= 0)
00858         return MICROBIT_INVALID_PARAMETER;
00859 
00860     // If there's an ongoing animation, wait for our turn to display.
00861     this->waitForFreeDisplay();
00862 
00863     // If the display is free, it's our turn to display.
00864     // If someone called stopAnimation(), then we simply skip...
00865     if (animationMode == ANIMATION_MODE_NONE)
00866     {
00867         // Start the effect.
00868         this->scrollAsync(image, delay, stride);
00869 
00870         // Wait for completion.
00871         fiberWait();
00872     }
00873     else
00874     {
00875         return MICROBIT_CANCELLED;
00876     }
00877 
00878     return MICROBIT_OK;
00879 }
00880 
00881 /**
00882   * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
00883   * Returns immediately.
00884   *
00885   * @param image The image to display.
00886   *
00887   * @param delay The time to delay between each update of the display, in milliseconds.
00888   *
00889   * @param stride The number of pixels to shift by in each update.
00890   *
00891   * @param startingPosition the starting position on the display for the animation
00892   *                         to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
00893   *
00894   * @return MICROBIT_OK, MICROBIT_BUSY if the screen is in use, or MICROBIT_INVALID_PARAMETER.
00895   *
00896   * @code
00897   * const int heart_w = 10;
00898   * const int heart_h = 5;
00899   * 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, };
00900   *
00901   * MicroBitImage i(heart_w,heart_h,heart);
00902   * display.animateAsync(i,100,5);
00903   * @endcode
00904   */
00905 int MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition)
00906 {
00907     //sanitise the delay value
00908     if(delay <= 0)
00909         return MICROBIT_INVALID_PARAMETER;
00910 
00911     // If the display is free, we can display.
00912     if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
00913     {
00914         // Assume right to left functionality, to align with scrollString()
00915         stride = -stride;
00916 
00917         //calculate starting position which is offset by the stride
00918         scrollingImagePosition = (startingPosition == MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS) ? MICROBIT_DISPLAY_WIDTH + stride : startingPosition;
00919         scrollingImageStride = stride;
00920         scrollingImage = image;
00921         scrollingImageRendered = false;
00922 
00923         animationDelay = stride == 0 ? 0 : delay;
00924         animationTick = delay-1;
00925         animationMode = ANIMATION_MODE_ANIMATE_IMAGE;
00926     }
00927     else
00928     {
00929         return MICROBIT_BUSY;
00930     }
00931 
00932     return MICROBIT_OK;
00933 }
00934 
00935 /**
00936   * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
00937   * Blocks the calling thread until the animation is complete.
00938   *
00939   *
00940   * @param delay The time to delay between each update of the display, in milliseconds.
00941   *
00942   * @param stride The number of pixels to shift by in each update.
00943   *
00944   * @param startingPosition the starting position on the display for the animation
00945   *                         to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
00946   *
00947   * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
00948   *
00949   * @code
00950   * const int heart_w = 10;
00951   * const int heart_h = 5;
00952   * 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, };
00953   *
00954   * MicroBitImage i(heart_w,heart_h,heart);
00955   * display.animate(i,100,5);
00956   * @endcode
00957   */
00958 int MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition)
00959 {
00960     //sanitise the delay value
00961     if(delay <= 0)
00962         return MICROBIT_INVALID_PARAMETER;
00963 
00964     // If there's an ongoing animation, wait for our turn to display.
00965     this->waitForFreeDisplay();
00966 
00967     // If the display is free, it's our turn to display.
00968     // If someone called stopAnimation(), then we simply skip...
00969     if (animationMode == ANIMATION_MODE_NONE)
00970     {
00971         // Start the effect.
00972         this->animateAsync(image, delay, stride, startingPosition);
00973 
00974         // Wait for completion.
00975         //TODO: Put this in when we merge tight-validation
00976         //if (delay > 0)
00977             fiberWait();
00978     }
00979     else
00980     {
00981         return MICROBIT_CANCELLED;
00982     }
00983 
00984     return MICROBIT_OK;
00985 }
00986 
00987 
00988 /**
00989   * Configures the brightness of the display.
00990   *
00991   * @param b The brightness to set the brightness to, in the range 0 - 255.
00992   *
00993   * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER
00994   *
00995   * @code
00996   * display.setBrightness(255); //max brightness
00997   * @endcode
00998   */
00999 int MicroBitDisplay::setBrightness(int b)
01000 {
01001     //sanitise the brightness level
01002     if(b < 0 || b > 255)
01003         return MICROBIT_INVALID_PARAMETER;
01004 
01005     this->brightness = b;
01006 
01007     return MICROBIT_OK;
01008 }
01009 
01010 /**
01011   * Configures the mode of the display.
01012   *
01013   * @param mode The mode to swap the display into. One of: DISPLAY_MODE_GREYSCALE,
01014   *             DISPLAY_MODE_BLACK_AND_WHITE, DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE
01015   *
01016   * @code
01017   * display.setDisplayMode(DISPLAY_MODE_GREYSCALE); //per pixel brightness
01018   * @endcode
01019   */
01020 void MicroBitDisplay::setDisplayMode(DisplayMode mode)
01021 {
01022     if(mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
01023     {
01024         //to reduce the artifacts on the display - increase the tick
01025         if(system_timer_get_period() != MICROBIT_LIGHT_SENSOR_TICK_PERIOD)
01026             system_timer_set_period(MICROBIT_LIGHT_SENSOR_TICK_PERIOD);
01027     }
01028 
01029     if(this->mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE && mode != DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
01030     {
01031         delete this->lightSensor;
01032 
01033         this->lightSensor = NULL;
01034     }
01035 
01036     this->mode = mode;
01037 }
01038 
01039 /**
01040   * Retrieves the mode of the display.
01041   *
01042   * @return the current mode of the display
01043   */
01044 int MicroBitDisplay::getDisplayMode()
01045 {
01046     return this->mode;
01047 }
01048 
01049 /**
01050   * Fetches the current brightness of this display.
01051   *
01052   * @return the brightness of this display, in the range 0..255.
01053   *
01054   * @code
01055   * display.getBrightness(); //the current brightness
01056   * @endcode
01057   */
01058 int MicroBitDisplay::getBrightness()
01059 {
01060     return this->brightness;
01061 }
01062 
01063 /**
01064   * Rotates the display to the given position.
01065   *
01066   * Axis aligned values only.
01067   *
01068   * @code
01069   * display.rotateTo(MICROBIT_DISPLAY_ROTATION_180); //rotates 180 degrees from original orientation
01070   * @endcode
01071   */
01072 void MicroBitDisplay::rotateTo(DisplayRotation rotation)
01073 {
01074     this->rotation = rotation;
01075 }
01076 
01077 /**
01078  * Enables or disables the display entirely, and releases the pins for other uses.
01079  *
01080  * @param enableDisplay true to enabled the display, or false to disable it.
01081  */
01082 void MicroBitDisplay::setEnable(bool enableDisplay)
01083 {
01084     // If we're already in the correct state, then there's nothing to do.
01085     if(((status & MICROBIT_COMPONENT_RUNNING) && enableDisplay) || (!(status & MICROBIT_COMPONENT_RUNNING) && !enableDisplay))
01086         return;
01087 
01088     uint32_t rmask = 0;
01089     uint32_t cmask = 0;
01090 
01091     for (int i = matrixMap.rowStart; i < matrixMap.rowStart + matrixMap.rows; i++)
01092         rmask |= 0x01 << i;
01093 
01094     for (int i = matrixMap.columnStart; i < matrixMap.columnStart + matrixMap.columns; i++)
01095         cmask |= 0x01 << i;
01096 
01097     if (enableDisplay)
01098     {
01099         PortOut p(Port0, rmask | cmask);
01100         status |= MICROBIT_COMPONENT_RUNNING;
01101     }
01102     else
01103     {
01104         PortIn p(Port0, rmask | cmask);
01105         p.mode(PullNone);
01106         status &= ~MICROBIT_COMPONENT_RUNNING;
01107     }
01108 }
01109 
01110 /**
01111   * Enables the display, should only be called if the display is disabled.
01112   *
01113   * @code
01114   * display.enable(); //Enables the display mechanics
01115   * @endcode
01116   *
01117   * @note Only enables the display if the display is currently disabled.
01118   */
01119 void MicroBitDisplay::enable()
01120 {
01121     setEnable(true);
01122 }
01123 
01124 /**
01125   * Disables the display, which releases control of the GPIO pins used by the display,
01126   * which are exposed on the edge connector.
01127   *
01128   * @code
01129   * display.disable(); //disables the display
01130   * @endcode
01131   *
01132   * @note Only disables the display if the display is currently enabled.
01133   */
01134 void MicroBitDisplay::disable()
01135 {
01136     setEnable(false);
01137 }
01138 
01139 /**
01140   * Clears the display of any remaining pixels.
01141   *
01142   * `display.image.clear()` can also be used!
01143   *
01144   * @code
01145   * display.clear(); //clears the display
01146   * @endcode
01147   */
01148 void MicroBitDisplay::clear()
01149 {
01150     image.clear();
01151 }
01152 
01153 /**
01154   * Updates the font that will be used for display operations.
01155   *
01156   * @param font the new font that will be used to render characters.
01157   *
01158   * @note DEPRECATED! Please use MicroBitFont::setSystemFont() instead.
01159   */
01160 void MicroBitDisplay::setFont(MicroBitFont font)
01161 {
01162     MicroBitFont::setSystemFont(font);
01163 }
01164 
01165 /**
01166   * Retrieves the font object used for rendering characters on the display.
01167   *
01168   * @note DEPRECATED! Please use MicroBitFont::getSystemFont() instead.
01169   */
01170 MicroBitFont MicroBitDisplay::getFont()
01171 {
01172     return MicroBitFont::getSystemFont();
01173 }
01174 
01175 /**
01176   * Captures the bitmap currently being rendered on the display.
01177   *
01178   * @return a MicroBitImage containing the captured data.
01179   */
01180 MicroBitImage MicroBitDisplay::screenShot()
01181 {
01182     return image.crop(0,0,MICROBIT_DISPLAY_WIDTH,MICROBIT_DISPLAY_HEIGHT);
01183 }
01184 
01185 /**
01186   * Gives a representative figure of the light level in the current environment
01187   * where are micro:bit is situated.
01188   *
01189   * Internally, it constructs an instance of a MicroBitLightSensor if not already configured
01190   * and sets the display mode to DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE.
01191   *
01192   * This also changes the tickPeriod to MICROBIT_LIGHT_SENSOR_TICK_SPEED so
01193   * that the display does not suffer from artifacts.
01194   *
01195   * @return an indicative light level in the range 0 - 255.
01196   *
01197   * @note this will return 0 on the first call to this method, a light reading
01198   * will be available after the display has activated the light sensor for the
01199   * first time.
01200   */
01201 int MicroBitDisplay::readLightLevel()
01202 {
01203     if(mode != DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
01204     {
01205         setDisplayMode(DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE);
01206         this->lightSensor = new MicroBitLightSensor(matrixMap);
01207     }
01208 
01209     return this->lightSensor->read();
01210 }
01211 
01212 /**
01213   * Destructor for MicroBitDisplay, where we deregister this instance from the array of system components.
01214   */
01215 MicroBitDisplay::~MicroBitDisplay()
01216 {
01217     system_timer_remove_component(this);
01218 }