Attempting to publish a tree
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
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 }
Generated on Tue Jul 12 2022 19:58:09 by 1.7.2