This is a demonstration program which draws a keypad on the LCD and uses the touch screen to allow an operator to key-in an access code. Two possible codes are allowed to grant to different levels of access. Additionally the push button is used to allow an operator to send Moorse Code pulses in to the device which is checked against two characters to determine if there was a match, and if so, access is granted.
Dependencies: LCD_DISCO_F429ZI mbed TS_DISCO_F429ZI mbed-os BSP_DISCO_F429ZI
Also draws a mushroom on the screen and animates it.
Diff: SecurityUnlockDemo-Animation.cpp
- Revision:
- 1:316582aec4fb
- Child:
- 2:444eeedb41f0
diff -r 04d4cc695e56 -r 316582aec4fb SecurityUnlockDemo-Animation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SecurityUnlockDemo-Animation.cpp Sat Jun 01 20:43:11 2019 +0000 @@ -0,0 +1,438 @@ + +// ---------------------------------------------------------------------- +// SecurityUnlockDemo-Animation.cpp +// +// Fredric L. Rice, June 2019 +// +// ---------------------------------------------------------------------- + +#include "mbed.h" // The mbed operating system +#include "LCD_DISCO_F429ZI.h" // For controlling the LCD +#include "TS_DISCO_F429ZI.h" // For controlling the touch screen +#include "SecurityUnlockDemo-Main.h" // For the main module's prototypes +#include "SecurityUnlockDemo-Animation.h" // Always include our own header + +// ---------------------------------------------------------------------- +// Describe data which is defined externally that we may access +// +// ---------------------------------------------------------------------- + + // We may be accessing the LCD + extern LCD_DISCO_F429ZI st_lcd; + + // We may be accessing the touch screen + extern TS_DISCO_F429ZI st_touchScreen; + +// ---------------------------------------------------------------------- +// Define local data storage +// +// ---------------------------------------------------------------------- + + // We define a "sprite" like entity consisting of a bit map showing + // the colors of pixes that will display a mushroom which we will + // move around the screen. The sprite consists of 22 pixes across + // and 17 pixels down so it's a small graphic. The letters describe + // the color of each pixel. + static char * pau8_mushroomMap[SPRITE_HEIGHT_IN_PIXELS] = + { // 1234567890123456789012 + "wwwwwwwwwbbbbbbwwwwwww", // 1 + "wwwwwwwbbbwggwbbbwwwww", // 2 + "wwwwwwbbwwggwwwbbwwwww", // 3 + "wwwwwbbgwwggggwwbbwwww", // 4 + "wwwwwbwgggggggggggbwww", // 5 + "wwwwbbwwggwwwwggwwbbww", // 6 + "wwwwbwwwgwwwwwwgwwwbww", // 7 + "wwwwbwwwgwwwwwwgwwwbww", // 8 + "wwwwbwwggwwwwwwwggwwbw", // 9 + "wwwwbwwggwwwwwwwggwwbw", // 10 + "wwwwbgggggwwwwwgggggbw", // 11 + "wwwwbggbbbbbbbbbbbggbw", // 12 + "wwwwbbbbwwbwwbwwbbbbbw", // 13 + "wwwwwwbwwwbwwbwwwwbwww", // 14 + "wwwwwwbwwwwwwwwwwwbwww", // 15 + "wwwwwwbbwwwwwwwwwbbwww", // 16 + "wwwwwwwbbbbbbbbbbbwwww" + } ; + + // There are 8 different directions that this animation supports, + // the coordinate offsets to add/subtract from the current pixel + // position depending upon the direction is mapped here. + // + // Directions: + // 1 2 3 Left up, Up, Right Up + // 4 6 Left, N/A, Right + // 7 8 9 Right Down, Down, Right Down + static int8_t i8_movementMatrix[3][6] = + { + -1, -1, -1, 0, -1, 1, + -1, 0, 0, 0, 1, 0, + -1, 1, 0, 1, 1, 1 + } ; + + // We maintain local data indicating where the sprite was last drawn + static uint16_t u16_spriteCurrentX; + static uint16_t u16_spriteCurrentY; + static uint8_t u8_spriteCurrentDirection; + + // When we are first asked to start animation, we launch + // a thread to continue the animation forever + static Thread st_animationThread; + + // To know whether animation is running, we maintain this boolean + static bool b_animationThreadRunning; + + // After every 10 movements along a given direction, rather than + // pick a direction and then move toward it we will "teleport" + // the sprite to a "random" location on the screen and maintain + // the direction -- unless we're up against a boundary, then a + // new direction will be selected + static uint8_t u8_teleportCounter; + + // When we teleport, we display a message indicating that/ + // After about 1 second we want to remove that message from + // the screen so we maintain this count down timer + static uint16_t u16_teleportMessageRemovalCountDown; + +// ---------------------------------------------------------------------- +// AnimationDrawSpriteAtThisLocation() +// +// +// ---------------------------------------------------------------------- +static void AnimationDrawOrEraseSpriteAtThisLocation(uint16_t u16_thisX, uint16_t u16_thisY, bool b_drawSprite) +{ + uint8_t u8_lineCount = 0; + uint16_t u16_currentX = u16_thisX; + uint16_t u16_currentY = u16_thisY; + uint8_t u8_currentPixel = 0; + uint32_t u32thisColor = LCD_COLOR_BLUE; + + // Record where we are placing the sprite this time + u16_spriteCurrentX = u16_thisX; + u16_spriteCurrentY = u16_thisY; + + // Place the sprite on the screen pixel by pixel + for (u8_lineCount = 0; u8_lineCount < SPRITE_HEIGHT_IN_PIXELS; u8_lineCount++) + { + for (u8_currentPixel = 0; + u8_currentPixel < strlen(pau8_mushroomMap[u8_lineCount]); + u8_currentPixel++) + { + // Are we drawing the sprite rather than erasing it? + if (true == b_drawSprite) + { + // Find out what the color of this pixel should be + switch(pau8_mushroomMap[u8_lineCount][u8_currentPixel]) + { + case 'w': + case 'W': + { + u32thisColor = LCD_COLOR_WHITE; + break; + } + case 'b': + case 'B': + { + u32thisColor = LCD_COLOR_BLUE; + break; + } + case 'g': + case 'G': + { + u32thisColor = LCD_COLOR_GREEN; + break; + } + case 'r': + case 'R': + { + u32thisColor = LCD_COLOR_RED; + break; + } + case 'k': + case 'K': + { + u32thisColor = LCD_COLOR_BLACK; + break; + } + case 'y': + case 'Y': + { + u32thisColor = LCD_COLOR_YELLOW; + break; + } + } + } + else + { + // Since we are erasing the sprite, the color is white + u32thisColor = LCD_COLOR_WHITE; + } + + // Place the pixel on the screen with the correct color + st_lcd.DrawPixel(u16_currentX, u16_currentY, u32thisColor); + + // Next pixel + u16_currentX++; + } + + // Start working on the next line + u16_currentY++; + + u16_currentX = u16_thisX; + } +} + +// ---------------------------------------------------------------------- +// AnimationGetANewDirectionToTravel() +// +// We acquire a "random" direction to travel. Note that the mbed random +// number generator is highly flawed, it produces the same numbers +// despite how hard one seeds it. We could utilize an ununsed analog +// input and use the noise that might be found there, but for purposes +// of demonstrating animation, we'll just use what mben provided. +// +// ---------------------------------------------------------------------- +static uint8_t AnimationGetANewDirectionToTravel(void) +{ + uint8_t u8_newDirection = 5; + + // Keep looking for a "random" number until one is valid + while(5 == u8_newDirection || + u8_newDirection < SPRITE_DIRECTION_LEFT_UP || + u8_newDirection > SPRITE_DIRECTION_RIGHT_DOWN) + { + // Get a new direction to tavel + u8_newDirection = (rand() % SPRITE_DIRECTION_RIGHT_DOWN); + } + + // Return the new direction of travel + return u8_newDirection; +} + +// ---------------------------------------------------------------------- +// AnimationInit() +// +// Initializes any locally-held data and performs all other start-up +// functionality required for the animated functionality, if any. +// +// ---------------------------------------------------------------------- +void AnimationInit(void) +{ + // Initialize locally-held data + u16_spriteCurrentX = 0; + u16_spriteCurrentY = 0; + b_animationThreadRunning = false; + u8_spriteCurrentDirection = AnimationGetANewDirectionToTravel(); + u8_teleportCounter = 0; + u16_teleportMessageRemovalCountDown = 0; +} + +// ---------------------------------------------------------------------- +// AnimationMoveSprite() +// +// +// ---------------------------------------------------------------------- +static void AnimationMoveSprite(void) +{ + uint16_t u16_proposedX = u16_spriteCurrentX; + uint16_t u16_proposedY = u16_spriteCurrentY; + int8_t i8_proposedOffsetX = 0; + int8_t i8_proposedOffsetY = 0; + bool b_haveNewLocation = false; + + // Did we teleport and display a message indicating that we did? + if (u16_teleportMessageRemovalCountDown > 0) + { + // Yes, so count down the timer / counter and see if it expired + if (0 == --u16_teleportMessageRemovalCountDown) + { + // Clear the message line + st_lcd.ClearStringLine(4); + } + } + + // Erase the entire sprite from where it currently exists + AnimationDrawOrEraseSpriteAtThisLocation(u16_spriteCurrentX, u16_spriteCurrentY, false); + + // We loop until we have a new location to move the sprite to. + // We look for a valid location more than once since we may + // run in to a boundary and have to change our direction of travel + while(false == b_haveNewLocation) + { + // Compute the new location of the sprite + switch(u8_spriteCurrentDirection) + { + // Extract the proposed offsets for X and Y + case SPRITE_DIRECTION_LEFT_UP: + { + i8_proposedOffsetX = i8_movementMatrix[0][0]; + i8_proposedOffsetY = i8_movementMatrix[0][1]; + break; + } + case SPRITE_DIRECTION_UP: + { + i8_proposedOffsetX = i8_movementMatrix[0][2]; + i8_proposedOffsetY = i8_movementMatrix[0][3]; + break; + } + case SPRITE_DIRECTION_RIGHT_UP: + { + i8_proposedOffsetX = i8_movementMatrix[0][4]; + i8_proposedOffsetY = i8_movementMatrix[0][5]; + break; + } + case SPRITE_DIRECTION_LEFT: + { + i8_proposedOffsetX = i8_movementMatrix[1][0]; + i8_proposedOffsetY = i8_movementMatrix[1][1]; + break; + } + case SPRITE_DIRECTION_RIGHT: + { + i8_proposedOffsetX = i8_movementMatrix[1][4]; + i8_proposedOffsetY = i8_movementMatrix[1][5]; + break; + } + default: + case SPRITE_DIRECTION_NOT_VALID: + { + // We should never be able to get here + return; + } + case SPRITE_DIRECTION_LEFT_DOWN: + { + i8_proposedOffsetX = i8_movementMatrix[2][0]; + i8_proposedOffsetY = i8_movementMatrix[2][1]; + break; + } + case SPRITE_DIRECTION_DOWN: + { + i8_proposedOffsetX = i8_movementMatrix[2][2]; + i8_proposedOffsetY = i8_movementMatrix[2][3]; + break; + } + case SPRITE_DIRECTION_RIGHT_DOWN: + { + i8_proposedOffsetX = i8_movementMatrix[2][4]; + i8_proposedOffsetY = i8_movementMatrix[2][5]; + break; + } + } + + // Apply the proposed offsets to the proposed new coordinates + u16_proposedX += i8_proposedOffsetX; + u16_proposedY += i8_proposedOffsetY; + + // Are the proposed coordinates within the bounds of where we + // want the sprite to move within? + if (u16_proposedX < SPRITE_MINIMUM_X || u16_proposedX > SPRITE_MAXIMUM_X || + u16_proposedY < SPRITE_MINIMUM_Y || u16_proposedY > SPRITE_MAXIMUM_Y) + { + // We have encountered a boundary so we must choose + // a new location and then try again + u8_spriteCurrentDirection = AnimationGetANewDirectionToTravel(); + + // Increment the teleport counter and see if it's time to teleport + if (++u8_teleportCounter == 10) + { + // It is time to teleport, pick a new "random" location + u16_proposedX = (rand() % LCD_WIDTH); + u16_proposedY = (rand() % LCD_HEIGHT); + + // Start counting for the next teleport + u8_teleportCounter = 0; + + // Report that we teleported! + st_lcd.DisplayStringAt(1, LINE(4), (uint8_t *)"Teleported!", CENTER_MODE); + + // After a while we will remove that message so this is a timer/counter + u16_teleportMessageRemovalCountDown = 40; + } + else + { + // Start over from where we currently are + u16_proposedX = u16_spriteCurrentX; + u16_proposedY = u16_spriteCurrentY; + } + + // Try for a new location + continue; + } + + // We have a new proposed location + b_haveNewLocation = true; + } + + // Place the sprite at the new location + AnimationDrawOrEraseSpriteAtThisLocation(u16_proposedX, u16_proposedY, true); +} + +// ---------------------------------------------------------------------- +// AnimationThread() +// +// +// ---------------------------------------------------------------------- +void AnimationThread(void) +{ + // This thread will run until it gets terminated upon request + while(true) + { + // Wake up 40 times a second + wait(0.025); + + // Move ths sprite in the cirrent direction of travel + AnimationMoveSprite(); + } +} + +// ---------------------------------------------------------------------- +// AnimationPerformAnimation() +// +// +// ---------------------------------------------------------------------- +void AnimationPerformAnimation(uint32_t u32_randomSeeder) +{ + // Are we currently not running the animation? + if (false == b_animationThreadRunning) + { + // Place the sprite on to the screen + AnimationDrawOrEraseSpriteAtThisLocation(LCD_WIDTH / 2, LCD_HEIGHT / 2, true); + + // Start the animation thread + st_animationThread.start(AnimationThread); + + // Flag the fact that the thread is running + b_animationThreadRunning = true; + + // In order to "seed" the random number generator, we acquire + // and discard up to 2000 random numbers + u32_randomSeeder %= 2000; + + // Acquire up to 2000 random numbers + while (u32_randomSeeder-- > 0) + { + // Acquire the number and then discard it + (void)AnimationGetANewDirectionToTravel(); + } + } +} + +// ---------------------------------------------------------------------- +// AnimationStopAnimation() +// +// +// ---------------------------------------------------------------------- +void AnimationStopAnimation(void) +{ + // Is the animation thread running? + if (true == b_animationThreadRunning) + { + // Stop the animation thread + st_animationThread.terminate(); + + // Flag the fact that animation is no longer running + b_animationThreadRunning = false; + } +} + +// End of file +