You are here

Fun with the TFT – Fixing Mark’s buggy code | サイプレス セミコンダクタ

Fun with the TFT – Fixing Mark’s buggy code

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!

Blog: 

このサイトに掲示されているすべてのコンテンツと資料は、「そのままの状態」で提供されます。サイプレス セミコンダクタとその関連サプライヤは、これらの資料について、いかなる目的への適合性をも表明することはありません。また、これらの資料について、すべての保証や条件を放棄します。これには、暗示的な保証および条件、商用性、特定の目的への適合性、すべてのサードパーティの知的財産権に対する権利と非侵害などが含まれますが、これらに制限されることはありません。サイプレス セミコンダクタにより、明示または暗示にかかわらず、禁反言などによるライセンスは、付与されないものとします。このサイトに掲示されている情報の使用には、サードパーティまたはサイプレス セミコンダクタからのライセンスが必要となる場合があります。

このサイトのコンテンツには、特定のガイドラインや使用制限が含まれている場合があります。このサイトにおけるすべての掲示やコンテンツの使用は、サイトの利用規約に準じて行われるものとします。このコンテンツを使用するサードパーティは、制限やガイドラインに従い、このサイトの利用規約を遵守するものとします。サイプレス セミコンダクタとそのサプライヤは、コンテンツや資料、その製品、プログラム、サービスに対し、いつでも修正、削除、変更、改善、向上、その他の変更を加える権利を有します。また、いかなるコンテンツ、製品、プログラム、サービスを予告なく変更または閉鎖する権利を有します。