Fredric Rice / Mbed 2 deprecated DemoKeypadMoorseCodeUnlockTouchAndButton

Dependencies:   LCD_DISCO_F429ZI mbed TS_DISCO_F429ZI mbed-os BSP_DISCO_F429ZI

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SecurityUnlockDemo-Animation.cpp Source File

SecurityUnlockDemo-Animation.cpp

00001 
00002 // ----------------------------------------------------------------------
00003 // SecurityUnlockDemo-Animation.cpp
00004 //
00005 // Fredric L. Rice, June 2019
00006 //
00007 // This module draws a mushroom on the display and then animates it as
00008 // the mushroom moves around "randomly." Every 10 moves or so it will
00009 // "teleport" to a "random" place on the screen.
00010 //
00011 // The random number generator provided by mbed is not very good
00012 // so we attempt to use the analog inputs on PF_3, PF_4, PF_5 to help
00013 // drive some randomness.
00014 //
00015 // Conceivably more than one sprite could be animated merely by
00016 // making some of the data elements arrays -- and of course using C++
00017 // to instantiate objects and then animating them is the correct way
00018 // to do such things. This is a concept demonstration projjct, so we
00019 // do not expect to make a proper class and allow any number of them
00020 // to be instantiated and get automated.
00021 //
00022 // That would be neat but that's something for another project.
00023 //
00024 // ----------------------------------------------------------------------
00025 
00026 #include "mbed.h"                           // The mbed operating system
00027 #include "LCD_DISCO_F429ZI.h"               // For controlling the LCD
00028 #include "TS_DISCO_F429ZI.h"                // For controlling the touch screen
00029 #include "SecurityUnlockDemo-Main.h"        // For the main module's prototypes
00030 #include "SecurityUnlockDemo-Animation.h"   // Always include our own header
00031 
00032 // ----------------------------------------------------------------------
00033 // Describe data which is defined externally that we may access
00034 //
00035 // ----------------------------------------------------------------------
00036 
00037     // We may be accessing the LCD
00038     extern LCD_DISCO_F429ZI st_lcd;
00039 
00040     // We may be accessing the touch screen
00041     extern TS_DISCO_F429ZI st_touchScreen;
00042 
00043 // ----------------------------------------------------------------------
00044 // Define local data storage
00045 //
00046 // ----------------------------------------------------------------------
00047 
00048     // We define a "sprite" like entity consisting of a bit map showing
00049     // the colors of pixes that will display a mushroom which we will
00050     // move around the screen. The sprite consists of 22 pixes across
00051     // and 17 pixels down so it's a small graphic. The letters describe
00052     // the color of each pixel.
00053     static char * pau8_mushroomMap[SPRITE_HEIGHT_IN_PIXELS] =
00054     { // 1234567890123456789012
00055         "wwwwwwwwwbbbbbbwwwwwww",   // 1
00056         "wwwwwwwbbbwggwbbbwwwww",   // 2
00057         "wwwwwwbbwwggwwwbbwwwww",   // 3
00058         "wwwwwbbgwwggggwwbbwwww",   // 4
00059         "wwwwwbwgggggggggggbwww",   // 5
00060         "wwwwbbwwggwwwwggwwbbww",   // 6
00061         "wwwwbwwwgwwwwwwgwwwbww",   // 7
00062         "wwwwbwwwgwwwwwwgwwwbww",   // 8
00063         "wwwwbwwggwwwwwwwggwwbw",   // 9
00064         "wwwwbwwggwwwwwwwggwwbw",   // 10
00065         "wwwwbgggggwwwwwgggggbw",   // 11
00066         "wwwwbggbbbbbbbbbbbggbw",   // 12
00067         "wwwwbbbbwwbwwbwwbbbbbw",   // 13
00068         "wwwwwwbwwwbwwbwwwwbwww",   // 14
00069         "wwwwwwbwwwwwwwwwwwbwww",   // 15
00070         "wwwwwwbbwwwwwwwwwbbwww",   // 16
00071         "wwwwwwwbbbbbbbbbbbwwww" 
00072     } ;
00073     
00074     // There are 8 different directions that this animation supports,
00075     // the coordinate offsets to add/subtract from the current pixel
00076     // position depending upon the direction is mapped here.
00077     //
00078     // Directions:
00079     //      1  2  3     Left up,    Up,     Right Up
00080     //      4     6     Left,       N/A,    Right
00081     //      7  8  9     Right Down, Down,   Right Down
00082     static int8_t i8_movementMatrix[3][6] =
00083     {
00084         -1, -1,    -1, 0,   -1, 1,
00085         -1,  0,     0, 0,    1, 0,
00086         -1,  1,     0, 1,    1, 1
00087     } ;
00088     
00089     // We maintain local data indicating where the sprite was last drawn
00090     static uint16_t u16_spriteCurrentX;
00091     static uint16_t u16_spriteCurrentY;
00092     static uint8_t  u8_spriteCurrentDirection;
00093 
00094     // When we are first asked to start animation, we launch
00095     // a thread to continue the animation forever
00096     static Thread st_animationThread;
00097     
00098     // To know whether animation is running, we maintain this boolean
00099     static bool b_animationThreadRunning;
00100     
00101     // After every 10 movements along a given direction, rather than
00102     // pick a direction and then move toward it we will "teleport"
00103     // the sprite to a "random" location on the screen and maintain
00104     // the direction -- unless we're up against a boundary, then a
00105     // new direction will be selected
00106     static uint8_t u8_teleportCounter;
00107     
00108     // When we teleport, we display a message indicating that/
00109     // After about 1 second we want to remove that message from
00110     // the screen so we maintain this count down timer
00111     static uint16_t u16_teleportMessageRemovalCountDown;
00112     
00113     // In an effort to assist the random number generator we will read
00114     // vaues from some of the analog inputs which should be near zero
00115     // but perhaps not entirely
00116     static AnalogIn st_analog1(PF_3);
00117     static AnalogIn st_analog2(PF_4);
00118     static AnalogIn st_analog3(PF_5);
00119 
00120 // ----------------------------------------------------------------------
00121 // AnimationDrawSpriteAtThisLocation()
00122 //
00123 // This function will draw or erase the sprite at the X and Y 
00124 // coordinate position passed to it, or it will erase the sprite
00125 // depending upon the argument passed to it.
00126 //
00127 // The colors that are supported are described in the sprite's
00128 // bit mapping.
00129 //
00130 // Note that we do not attempt to store the original pixel's color
00131 // value before writing it with a new value, so we can not restore
00132 // the pixel once the sprite moves off of it. This is a stark 
00133 // divergence from real spite automation which restores the original,
00134 // but we are demo code, not a real project, so we don't even try.
00135 //
00136 // ----------------------------------------------------------------------
00137 static void AnimationDrawOrEraseSpriteAtThisLocation(uint16_t u16_thisX, uint16_t u16_thisY, bool b_drawSprite)
00138 {
00139     uint8_t  u8_lineCount    = 0;
00140     uint16_t u16_currentX    = u16_thisX;
00141     uint16_t u16_currentY    = u16_thisY;
00142     uint8_t  u8_currentPixel = 0;
00143     uint32_t u32thisColor    = LCD_COLOR_BLUE;
00144     
00145     // Record where we are placing the sprite this time
00146     u16_spriteCurrentX = u16_thisX;
00147     u16_spriteCurrentY = u16_thisY;
00148     
00149     // Place the sprite on the screen pixel by pixel
00150     for (u8_lineCount = 0; u8_lineCount < SPRITE_HEIGHT_IN_PIXELS; u8_lineCount++)
00151     {
00152         for (u8_currentPixel = 0; 
00153             u8_currentPixel < strlen(pau8_mushroomMap[u8_lineCount]); 
00154                 u8_currentPixel++)
00155         {
00156             // Are we drawing the sprite rather than erasing it?
00157             if (true == b_drawSprite)
00158             {
00159                 // Find out what the color of this pixel should be
00160                 switch(pau8_mushroomMap[u8_lineCount][u8_currentPixel])
00161                 {
00162                     case 'w':
00163                     case 'W':
00164                     {
00165                         u32thisColor = LCD_COLOR_WHITE;
00166                         break;
00167                     }
00168                     case 'b':
00169                     case 'B':
00170                     {
00171                         u32thisColor = LCD_COLOR_BLUE;
00172                         break;
00173                     }
00174                     case 'g':
00175                     case 'G':
00176                     {
00177                         u32thisColor = LCD_COLOR_GREEN;
00178                         break;
00179                     }
00180                     case 'r':
00181                     case 'R':
00182                     {
00183                         u32thisColor = LCD_COLOR_RED;
00184                         break;
00185                     }
00186                     case 'k':
00187                     case 'K':
00188                     {
00189                         u32thisColor = LCD_COLOR_BLACK;
00190                         break;
00191                     }
00192                     case 'y':
00193                     case 'Y':
00194                     {
00195                         u32thisColor = LCD_COLOR_YELLOW;
00196                         break;
00197                     }
00198                 }
00199             }
00200             else
00201             {
00202                 // Since we are erasing the sprite, the color is white
00203                 u32thisColor = LCD_COLOR_WHITE;                
00204             }
00205             
00206             // Place the pixel on the screen with the correct color
00207             st_lcd.DrawPixel(u16_currentX, u16_currentY, u32thisColor);
00208             
00209             // Next pixel
00210             u16_currentX++;
00211         }
00212         
00213         // Start working on the next line
00214         u16_currentY++;
00215         
00216         u16_currentX = u16_thisX;
00217     }
00218 }
00219 
00220 // ----------------------------------------------------------------------
00221 // AnimationGetANewDirectionToTravel()
00222 //
00223 // We acquire a "random" direction to travel. Note that the mbed random
00224 // number generator is highly flawed, it produces the same numbers
00225 // despite how hard one seeds it. We could utilize an ununsed analog
00226 // input and use the noise that might be found there, but for purposes
00227 // of demonstrating animation, we'll just use what mben provided.
00228 //
00229 // ----------------------------------------------------------------------
00230 static uint8_t AnimationGetANewDirectionToTravel(void)
00231 {
00232     uint8_t u8_newDirection = 5;
00233     
00234     // Keep looking for a "random" number until one is valid
00235     while(5 == u8_newDirection ||
00236         u8_newDirection < SPRITE_DIRECTION_LEFT_UP ||
00237         u8_newDirection > SPRITE_DIRECTION_RIGHT_DOWN)
00238     {
00239         // Get a new direction to tavel
00240         u8_newDirection = (rand() % SPRITE_DIRECTION_RIGHT_DOWN);
00241     }
00242     
00243     // Return the new direction of travel
00244     return u8_newDirection;
00245 }
00246 
00247 // ----------------------------------------------------------------------
00248 // AnimationInit()
00249 //
00250 // Initializes any locally-held data and performs all other start-up
00251 // functionality required for the animated functionality, if any.
00252 //
00253 // ----------------------------------------------------------------------
00254 void AnimationInit(void)
00255 {
00256     // Initialize locally-held data
00257     u16_spriteCurrentX                  = 0;
00258     u16_spriteCurrentY                  = 0;
00259     b_animationThreadRunning            = false;
00260     u8_spriteCurrentDirection           = AnimationGetANewDirectionToTravel();
00261     u8_teleportCounter                  = 0;
00262     u16_teleportMessageRemovalCountDown = 0;
00263 }
00264 
00265 // ----------------------------------------------------------------------
00266 // AnimationMoveSprite()
00267 //
00268 // This function is the one that performs the actual move by 1 pixel
00269 // of the sprite. The sprite is erased from the screen and then the
00270 // new location is computed. If that places the sprite outside of the
00271 // boundaries, a new direction is selected and then the proposed
00272 // new location is selected.
00273 //
00274 // Once there is a new sprite location proposed, it gets re-drawn.
00275 //
00276 // ----------------------------------------------------------------------
00277 static void AnimationMoveSprite(void)
00278 {
00279     uint16_t u16_proposedX      = u16_spriteCurrentX;
00280     uint16_t u16_proposedY      = u16_spriteCurrentY;
00281     int8_t   i8_proposedOffsetX = 0;
00282     int8_t   i8_proposedOffsetY = 0;
00283     bool     b_haveNewLocation  = false;
00284     
00285     // Did we teleport and display a message indicating that we did?
00286     if (u16_teleportMessageRemovalCountDown > 0)
00287     {
00288         // Yes, so count down the timer / counter and see if it expired
00289         if (0 == --u16_teleportMessageRemovalCountDown)
00290         {
00291             // Clear the message line
00292             st_lcd.ClearStringLine(4);
00293         }
00294     }
00295     
00296     // Erase the entire sprite from where it currently exists
00297     AnimationDrawOrEraseSpriteAtThisLocation(u16_spriteCurrentX, u16_spriteCurrentY, false);
00298     
00299     // We loop until we have a new location to move the sprite to.
00300     // We look for a valid location more than once since we may
00301     // run in to a boundary and have to change our direction of travel
00302     while(false == b_haveNewLocation)
00303     {        
00304         // Compute the new location of the sprite
00305         switch(u8_spriteCurrentDirection)
00306         {
00307             // Extract the proposed offsets for X and Y
00308             case SPRITE_DIRECTION_LEFT_UP:
00309             {
00310                 i8_proposedOffsetX = i8_movementMatrix[0][0];
00311                 i8_proposedOffsetY = i8_movementMatrix[0][1];
00312                 break;
00313             }
00314             case SPRITE_DIRECTION_UP:
00315             {
00316                 i8_proposedOffsetX = i8_movementMatrix[0][2];
00317                 i8_proposedOffsetY = i8_movementMatrix[0][3];
00318                 break;
00319             }
00320             case SPRITE_DIRECTION_RIGHT_UP:
00321             {
00322                 i8_proposedOffsetX = i8_movementMatrix[0][4];
00323                 i8_proposedOffsetY = i8_movementMatrix[0][5];
00324                 break;
00325             }
00326             case SPRITE_DIRECTION_LEFT:
00327             {
00328                 i8_proposedOffsetX = i8_movementMatrix[1][0];
00329                 i8_proposedOffsetY = i8_movementMatrix[1][1];
00330                 break;
00331             }
00332             case SPRITE_DIRECTION_RIGHT:
00333             {
00334                 i8_proposedOffsetX = i8_movementMatrix[1][4];
00335                 i8_proposedOffsetY = i8_movementMatrix[1][5];
00336                 break;
00337             }
00338             default:
00339             case SPRITE_DIRECTION_NOT_VALID:
00340             {
00341                 // We should never be able to get here
00342                 return;
00343             }
00344             case SPRITE_DIRECTION_LEFT_DOWN:
00345             {
00346                 i8_proposedOffsetX = i8_movementMatrix[2][0];
00347                 i8_proposedOffsetY = i8_movementMatrix[2][1];
00348                 break;
00349             }
00350             case SPRITE_DIRECTION_DOWN:
00351             {
00352                 i8_proposedOffsetX = i8_movementMatrix[2][2];
00353                 i8_proposedOffsetY = i8_movementMatrix[2][3];
00354                 break;
00355             }
00356             case SPRITE_DIRECTION_RIGHT_DOWN:
00357             {
00358                 i8_proposedOffsetX = i8_movementMatrix[2][4];
00359                 i8_proposedOffsetY = i8_movementMatrix[2][5];
00360                 break;
00361             }
00362         }
00363 
00364         // Apply the proposed offsets to the proposed new coordinates
00365         u16_proposedX += i8_proposedOffsetX;
00366         u16_proposedY += i8_proposedOffsetY;
00367     
00368         // Are the proposed coordinates within the bounds of where we 
00369         // want the sprite to move within?
00370         if (u16_proposedX < SPRITE_MINIMUM_X || u16_proposedX > SPRITE_MAXIMUM_X ||
00371             u16_proposedY < SPRITE_MINIMUM_Y || u16_proposedY > SPRITE_MAXIMUM_Y)
00372         {
00373             // We have encountered a boundary so we must choose
00374             // a new location and then try again
00375             u8_spriteCurrentDirection = AnimationGetANewDirectionToTravel();
00376             
00377             // Increment the teleport counter and see if it's time to teleport
00378             if (++u8_teleportCounter == 10)
00379             {
00380                 // It is time to teleport, pick a new "random" location
00381                 u16_proposedX = (rand() % LCD_WIDTH);
00382                 u16_proposedY = (rand() % LCD_HEIGHT);
00383         
00384                 // Start counting for the next teleport
00385                 u8_teleportCounter = 0;
00386                 
00387                 // Report that we teleported!
00388                 st_lcd.DisplayStringAt(1, LINE(4), (uint8_t *)"Teleported!", CENTER_MODE);
00389                 
00390                 // After a while we will remove that message so this is a timer/counter
00391                 u16_teleportMessageRemovalCountDown = 40;
00392             }
00393             else
00394             {
00395                 // Start over from where we currently are
00396                 u16_proposedX = u16_spriteCurrentX;
00397                 u16_proposedY = u16_spriteCurrentY;                
00398             }
00399 
00400             // Try for a new location
00401             continue;
00402         }
00403         
00404         // We have a new proposed location
00405         b_haveNewLocation = true;
00406     }
00407     
00408     // Place the sprite at the new location
00409     AnimationDrawOrEraseSpriteAtThisLocation(u16_proposedX, u16_proposedY, true);
00410 }
00411 
00412 // ----------------------------------------------------------------------
00413 // AnimationThread()
00414 //
00415 // This is the main thread for the animation functionality. The thread
00416 // usually runs forever however there is a mechanism for terminating
00417 // it provided in this module.
00418 //
00419 // The thread wakes up about 40 times a second so that the animation
00420 // can move fairly quickly and smoothly.
00421 //
00422 // ----------------------------------------------------------------------
00423 void AnimationThread(void)
00424 {
00425     // This thread will run until it gets terminated upon request
00426     while(true)
00427     {
00428         // Wake up 40 times a second
00429         wait(0.025);
00430         
00431         // Move ths sprite in the cirrent direction of travel
00432         AnimationMoveSprite();
00433     }
00434 }
00435 
00436 // ----------------------------------------------------------------------
00437 // AnimationPerformAnimation()
00438 //
00439 // This function is called when it is time to start animating the
00440 // sprite. It checks to make sure that the animation thread is not
00441 // yet started before it starts the animation.
00442 //
00443 // ----------------------------------------------------------------------
00444 void AnimationPerformAnimation(uint32_t u32_randomSeeder)
00445 {
00446     // Are we currently not running the animation?
00447     if (false == b_animationThreadRunning)
00448     {
00449         // Place the sprite on to the screen
00450         AnimationDrawOrEraseSpriteAtThisLocation(LCD_WIDTH / 2, LCD_HEIGHT / 2, true);
00451     
00452         // Start the animation thread
00453         st_animationThread.start(AnimationThread);
00454     
00455         // Flag the fact that the thread is running
00456         b_animationThreadRunning = true;
00457         
00458         // In an effort to add more "randomness," read some of the
00459         // analog inputs and add their values, if any, to the seed
00460         u32_randomSeeder = (st_analog1 * 100) + (st_analog2 * 100) + (st_analog3 * 100);
00461     
00462         // In order to "seed" the random number generator, we acquire
00463         // and discard up to 2000 random numbers
00464         u32_randomSeeder %= 2000;
00465     
00466         // Acquire up to 2000 random numbers
00467         while (u32_randomSeeder-- > 0)
00468         {
00469             // Acquire the number and then discard it
00470             (void)AnimationGetANewDirectionToTravel();
00471         }
00472     }
00473 }
00474 
00475 // ----------------------------------------------------------------------
00476 // AnimationStopAnimation()
00477 //
00478 // In the event the animation thread needs to be stopped, this function
00479 // may be called to terminate the animation thread.
00480 //
00481 // ----------------------------------------------------------------------
00482 void AnimationStopAnimation(void)
00483 {
00484     // Is the animation thread running?
00485     if (true == b_animationThreadRunning)
00486     {
00487         // Stop the animation thread
00488         st_animationThread.terminate();
00489         
00490         // Flag the fact that animation is no longer running
00491         b_animationThreadRunning = false;
00492     }
00493 }
00494 
00495 // End of file
00496