fsdfds
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 << (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 }
Generated on Wed Jul 13 2022 00:58:03 by 1.7.2