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.

SecurityUnlockDemo-Animation.cpp

Committer:
Damotclese
Date:
2019-06-05
Revision:
5:5c507689ae66
Parent:
4:5c70a30d6757

File content as of revision 5:5c507689ae66:


// ----------------------------------------------------------------------
// SecurityUnlockDemo-Animation.cpp
//
// Fredric L. Rice, June 2019
//
// This module draws a mushroom on the display and then animates it as
// the mushroom moves around "randomly." Every 10 moves or so it will
// "teleport" to a "random" place on the screen.
//
// The random number generator provided by mbed is not very good
// so we attempt to use the analog inputs on PF_3, PF_4, PF_5 to help
// drive some randomness.
//
// Conceivably more than one sprite could be animated merely by
// making some of the data elements arrays -- and of course using C++
// to instantiate objects and then animating them is the correct way
// to do such things. This is a concept demonstration projjct, so we
// do not expect to make a proper class and allow any number of them
// to be instantiated and get automated.
//
// That would be neat but that's something for another project.
//
// ----------------------------------------------------------------------

#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;
    
    // In an effort to assist the random number generator we will read
    // vaues from some of the analog inputs which should be near zero
    // but perhaps not entirely
    static AnalogIn st_analog1(PF_3);
    static AnalogIn st_analog2(PF_4);
    static AnalogIn st_analog3(PF_5);

// ----------------------------------------------------------------------
// AnimationDrawSpriteAtThisLocation()
//
// This function will draw or erase the sprite at the X and Y 
// coordinate position passed to it, or it will erase the sprite
// depending upon the argument passed to it.
//
// The colors that are supported are described in the sprite's
// bit mapping.
//
// Note that we do not attempt to store the original pixel's color
// value before writing it with a new value, so we can not restore
// the pixel once the sprite moves off of it. This is a stark 
// divergence from real spite automation which restores the original,
// but we are demo code, not a real project, so we don't even try.
//
// ----------------------------------------------------------------------
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()
//
// This function is the one that performs the actual move by 1 pixel
// of the sprite. The sprite is erased from the screen and then the
// new location is computed. If that places the sprite outside of the
// boundaries, a new direction is selected and then the proposed
// new location is selected.
//
// Once there is a new sprite location proposed, it gets re-drawn.
//
// ----------------------------------------------------------------------
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()
//
// This is the main thread for the animation functionality. The thread
// usually runs forever however there is a mechanism for terminating
// it provided in this module.
//
// The thread wakes up about 40 times a second so that the animation
// can move fairly quickly and smoothly.
//
// ----------------------------------------------------------------------
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()
//
// This function is called when it is time to start animating the
// sprite. It checks to make sure that the animation thread is not
// yet started before it starts the animation.
//
// ----------------------------------------------------------------------
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 an effort to add more "randomness," read some of the
        // analog inputs and add their values, if any, to the seed
        u32_randomSeeder = (st_analog1 * 100) + (st_analog2 * 100) + (st_analog3 * 100);
    
        // 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()
//
// In the event the animation thread needs to be stopped, this function
// may be called to terminate the animation thread.
//
// ----------------------------------------------------------------------
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