Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob

Fun with the TFT – Fixing Mark’s buggy code

lock attach
Attachments are accessible only for community members.

Fun with the TFT – Fixing Mark’s buggy code

markgsaunders
Employee
Employee
50 sign-ins 10 solutions authored 5 solutions authored

Did you find my bug? Make these changes to your program and it’s really obvious. First change the background color to GUI_BLACK in the GUI_Init_Task() function (as I suggested in my last post). Then create a bunch of bouncing balls. I have six of them and some are bouncing at near full speed.

 

    /* Turn on the OLED and set the background color */

    GUI_Init();

    GUI_SetBkColor( GUI_BLACK );

    GUI_Clear();

 

    /* Define the ball initial conditions */

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

    static ball_t b2 = { GUI_BLUE,    5, 35, -2 /*LEFT*/,  +1 /*DOWN*/ };

    static ball_t b3 = { GUI_GREEN,   4, 47, -1 /*LEFT*/,  +1 /*DOWN*/ };

    static ball_t b4 = { GUI_CYAN,    6, 10, -2 /*LEFT*/,  -1 /*UP*/   };

    static ball_t b5 = { GUI_YELLOW,  7, 50, -1 /*LEFT*/,  +1 /*DOWN*/ };

    static ball_t b6 = { GUI_MAGENTA, 7, 50, +1 /*RIGHT*/, -1 /*UP*/   };

 

    /* Create ball tasks */

    xTaskCreate( bounce, "b1", configMINIMAL_STACK_SIZE*2, &b1, pri, NULL );

    xTaskCreate( bounce, "b2", configMINIMAL_STACK_SIZE*2, &b2, pri, NULL );

    xTaskCreate( bounce, "b3", configMINIMAL_STACK_SIZE*2, &b3, pri, NULL );

    xTaskCreate( bounce, "b4", configMINIMAL_STACK_SIZE*2, &b4, pri, NULL );

    xTaskCreate( bounce, "b5", configMINIMAL_STACK_SIZE*2, &b5, pri, NULL );

    xTaskCreate( bounce, "b6", configMINIMAL_STACK_SIZE*2, &b6, pri, NULL );

 

Having six balls bouncing is cool but, if you run them too fast then you can get ball clippings left on the screen. Every now and again you see little crescents appearing. Grrrrr…..

The issue manifests itself very clearly when you have balls running at speed == 50. This is because there is no delay in the loop and the OS is more likely to time slice that task at some other part of the loop (during the delays in the TFT library). The result is a jumbled order of screen updates causing balls to be drawn and cleared in other ball’s colors.

Note that the problem is not just because of speed. It is just more common at high speed. The lazy boy fix is to not allow the delay to be zero. That would greatly reduce the occurrence of crescents… but the defect is still there and GOSH DARN I am going to fix it!

What we need to do is make sure that task switching only happens during the vTaskDelay() call. What we need is a simple binary semaphore. One of those bad boys will let me implement a resource protection scheme that ensures the OS will not stop my tasks in the middle of a screen update.

I will have just one semaphore and six tasks will be giving and taking it. The lazy boy solution to that is to create a global variable for the semaphore. But globals make me a little itchy. They are not baaaaad. But they are not gooood either. I just only want to use them when I really have to. And, today, I do not. Cue my friend the “static” keyword. Inside bounce() I use it to create a single instance of the semaphore in static memory, not on the stack.

 

       static SemaphoreHandle_t screen = NULL;

 

This is now a shared variable but it is protected from being messed with by another function (it is a “static automatic” if you want to get all fancy pants about it… but we don’t) and I’m not itchy any longer. But I have a new problem – how to initialize the variable once and only once? I need to make sure the semaphore is initialized before any task uses it but not reinitialized by every task. The trick is to check it is NULL and, if so, initialize it. Otherwise just continue into the drawing loop.

 

       if( NULL == screen )

             { screen = xSemaphoreCreateBinary(); }

 

I do not know which task the OS will run first but I do not care. Whichever one runs will create the mutex and the rest will not.

All we need now is to wrap the call to vTaskDelay() with calls to give and then take the semaphore. When a task owns the semaphore it will prevent the others from running, so the ball gets moved properly before the next one starts to move.

 

    xSemaphoreGive( screen );

    vTaskDelay( speed );

    xSemaphoreTake( screen, portMAX_DELAY );

 

Ready to go now? Nearly. Usually, when I write these how-to articles, I start by telling you to include the header file. But forgetting to add header files is just something engineers do! So I thought I’d wait until the end and tell you about it. Tell the compiler about semaphores by adding this line after the includes for FreeRTOS.h and task.h.

 

#include "semphr.h"

 

I do not know why the file name is abbreviated… maybe semphr is easier to type than semphaore, semapahore, shampemore, semifour…oh yeah, it is!

Anyway, program that and you should be fully rid of crescent shapes on your screen!

419 Views
Authors