I have never used the TFT to do anything other than draw some shapes and print text. Of course, that’s not too surprising really... since that’s what they are designed to do! But I am going to make some simple animated applications now and that should force me to dig a little deeper into what the screen can do and how emWin works. Should be fun!

I am going to use the function void GUI_FillCircle(int x0, int y0, int r) to draw a ball and move it around the screen. I’ll be doing this from the TFT_Play project I created last time so I do not need to bother adding the libraries and setting the COMPONENT in the Makefile again. I’ll start by defining the screen dimensions and a box inside that. The idea is that the ball shall bounce around inside the box so the following allows me to define a box of any shape and position.

#define SCREEN_WIDTH    (320)

#define SCREEN_HEIGHT   (240)

 

#define SCREEN_LEFT     (0)

#define SCREEN_RIGHT    (SCREEN_WIDTH-1)

#define SCREEN_TOP      (0)

#define SCREEN_BOTTOM   (SCREEN_HEIGHT-1)

 

/* Define the box in which to bounce */

#define BOX_HMARGIN     (10)

#define BOX_VMARGIN     (10)

#define BOX_LEFT        (SCREEN_LEFT+BOX_HMARGIN)

#define BOX_RIGHT       (SCREEN_RIGHT-BOX_HMARGIN)

#define BOX_TOP         (SCREEN_TOP+BOX_VMARGIN)

#define BOX_BOTTOM      (SCREEN_BOTTOM-BOX_VMARGIN)

The FillCircle function takes a radius as the third argument and so whenever the ball is drawn such that its edge touches a side, we should bounce. Here are some handy functions that figure out if the ball is touching

inline int isBallTouchingLeft(   int x, int radius) { return ( x <= ( BOX_LEFT   + radius ) ); }

inline int isBallTouchingRightint x, int radius) { return ( x >= ( BOX_RIGHT  - radius ) ); }

inline int isBallTouchingTop(    int y, int radius) { return ( y <= ( BOX_TOP    + radius ) ); }

inline int isBallTouchingBottom( int y, int radius) { return ( y >= ( BOX_BOTTOM - radius ) ); }

Next, here is a type for the ball. It may seem unnecessary to create this right now but, in my next blogs, I intend to create multiple balls and bounce them all at the same time.

typedef struct

{

    GUI_COLOR   color;

    uint8_t    radius;

    uint8_t    speed;

    int8_t     xDir;

    int8_t        yDir;

} ball_t;

The color and radius should be pretty self-explanatory. The speed is going to control the time between moves and has a range between 0 and 50 (#define MAX_SPEED (50)). A speed of 0 is the slowest (boring) and a speed of 50 hurtles around the screen with wild abandon! The direction arguments indicate the number of pixels to move every time, which provides a lot of freedom to alter the angle of travel. Note that they are signed quantities so you can start the ball in either a left/right or north/south.

The meat of the program is going to be in the void bounce( ball_t* ball ) function, below. It takes a pointer to the ball as an argument. Again, this is a little overkill for a single ball, but it will be really useful in my next few blogs. It is good form to make local copies of the ball_t values because it is pointing to memory outside of the function. Making copies of the values helps avoid very bad results if you make the mistake of passing a pointer to local (non-static) data. You will see a little later that I explicitly define the ball in static memory but constrain its scope to the main() function. I could have just created a global but then I would be tempted to access it directly inside bounce(), which is lazy and would have to be re-written in my next blog anyway.

Once the color, speed, size and direction are set, the forever loop draws the ball (in red), delays a while based on the speed, then clears the ball (in black), changes the position, and repeats.  You can also see how those helper functions, above, are used to make the ball actually bounce of the edges.

void bounce( ball_t* ball )

{

    /* Set up local variables to control the ball */

    GUI_COLOR color = ball->color;

    uint32_t speed = ( MAX_SPEED < ball->speed ) ? 0 : MAX_SPEED - ball->speed;

    uint8_t radius = ball->radius;

 

    /* Extract the up/down and left/right increments */

    int8_t xDir = ball->xDir;

    int8_t yDir = ball->yDir;

 

    /* Start the ball at a fixed position */

    uint16_t xPos = BOX_LEFT + ( ( BOX_RIGHT - BOX_LEFT ) / 2 );

    uint16_t yPos = BOX_TOP + ( ( BOX_BOTTOM - BOX_TOP ) / 2 );

 

    /* Draw - wait - clear - move */

    for(;;)

        {

        /* Draw the ball */

        GUI_SetColor( color );

        GUI_FillCircle( xPos, yPos, radius );

 

        /* Moderate the speed of the ball */

        Cy_SysLib_Delay( speed );

 

        /* Clear the previously drawn ball */

        GUI_SetColor( GUI_BLACK );

        GUI_FillCircle( xPos, yPos, radius );

 

        /* If the ball touches an edge, change direction */

        if( isBallTouchingLeft( xPos, radius ) || isBallTouchingRight( xPos, radius ) )

            { xDir = -xDir; }

        if( isBallTouchingTop( yPos, radius ) || isBallTouchingBottom( yPos, radius ) )

            { yDir = -yDir; }

 

        /* Move the ball in both axes */

        xPos += xDir;

        yPos += yDir;

    }

}

All that is needed now is to show you the code in main() that sets everything up. I created a small, speedy red ball (in permanent memory) that moves one pixel left-right and 3 pixels up-down on every cycle, then I turned on the display with a gray background, and finally called my bounce() function (which never returns).

    static ball_t b1 = { GUI_RED, 3, 45, +1 /*RIGHT*/, -3 /*UP*/ };

 

    GUI_Init();

    GUI_SetBkColor( GUI_GRAY );

    GUI_Clear();

 

    bounce( &b1 );

I’ve attached a completed main.c file to this blog so, if you run into problems following my ramblings, you have a good reference. Try out a few different values in the ball_t struct and you’ll find out that making them too big and too fast creates display problems because you start pushing the limits of the OLED update rate. Or maybe it is my eyes???

Here is a picture of the screen after a few seconds. Notice how the background is gray and the ball is leaving a black trail behind it. I did that because I like the way it looks but you should be able to turn the effect off if you do not like it. Try out the program with a few different box sizes too – and let me know if you find a bug!!!

 

bounce1.jpg