WebElektronika

FreeRTOS alapjai II. (taskok felügyelete)

person access_time 2015.06.07.
Megtanuljuk most a taskok létrehozását, használatát, felügyeletét. Létrehozunk task-ot, de megnézzük a törlését, a prioritásának a lekérdezésének a lehetőségét is. A kernel vezérlésére a sorozatunknak ebben a részében nem térünk ki.


Megnézzük most a task-ok létrehozásának és felügyeletének a lehetőségeit.

 

Szálak létrehozása, törlése (task.h)

A task-ok "C" függvényként kerülnek implementálásra, a visszatérési típus void, return-t nem tartalmazhat. Szálak létrehozására az xTaskCreate(. . .) használható. Ha a szál létrehozása sikeres volt, akkor pdTRUE értéket kapunk vissza. A szál kialakítása végtelen, nem érhet véget. Ezt elérhetjük akár while, akár for ciklus segítségével is.

xTaskCreate(
    pdTASK_CODE pvTaskCode,                          - pointer, ezen a néven kerül implementálásra
    const portCHAR * const pcName,                     - task neve, debug-nál használjuk
    unsigned portSHORT usStackDepth,                - stack mélysége 
    void *pvParameters,                                        - nem használjuk
    unsigned portBASE_TYPE uxPriority,               - prioritás
    xTaskHandle *pvCreatedTask                          - handler, nem kötelező használni
);

 

A következő példában a "pelda" nevű függvényből hozunk létre egy task-ot, amelyet "T0" nével találhatunk meg a debug során. A stack-nek a méret 256 lesz, a prioritása pedig 1. Minden szálhoz rendelhető prioritás, amelyek lekérdezhetők, módosíthatók. A prioritás 0-tól indul és a felső határt a FreeRTOSConfig.h file-ban adhatjuk meg a configMAX_PRIORITIES-1 beállításával.

A függvény tartalmaz egy végtelen ciklust, ezért a függvény soha nem érhet véget, viszont a később megemlítésre kerülő vTaskDelay() segítségével a szálat 350/portTICK_RATE_MS -ként altatjuk.

példa :

xTaskCreate(pelda,"T0",256,NULL,1,NULL);

.  .  .  .

static void pelda( void *pvParameters )
{
    while(1)
    {
        LATDbits.LATD0 = !LATDbits.LATD0;
        vTaskDelay(350/portTICK_RATE_MS);
    }
}

Ha egy szálat törölni szeretnénk, akkor azt a vTaskDelete(. . .)-tel tudjuk megtenni. Ekkor a kernel managmentből törlésre kerül a task, tehát a különböző listákból (esemény, blokkolt, futásra kész, felfüggesztett) is eltávolításra kerül.

A task létrehozásakor megadhatunk egy handler-t (xWE) is, amelynek segítségével később erre a szálra hivatkozni tudunk, például a törléskor.

példa a törlésre :

xTaskCreate(vTASK0,"T0",256,NULL,1,&xWE);

vTaskDelete(xWE);

 

Szálak vezérlése (task.h)

  • vTaskDelay  (adott ideig alszik a szál, "pontatlan") A késleltetés megadásakor a "tick"-ek számát kell megadni (portTICK_RATE_MS).
  • vTaskDelayUntil  (az utolsó felébredéstől adott ideig alszik a szál, "pontos")
  • vTaskPrioritySet  (a szál prioritása állítható be)
  • uxTaskPriorityGet  (visszaadja a task prioritását)
  • vTaskSuspend  (felfüggeszti a task-ot, ekkor a task nem érhető el az ütemezőnek)
  • vTaskResume  (újra futhat a felfüggesztett task)
  • xTaskResumeFromISR
  • vTaskSetApplicationTag
  • xTaskCallApplicationTaskHook

Ezek az API-k akkor használható, ha a FreeRTOSConfig.h file-ban engedélyezve vannak (1) (1. ábra)

kep
1. ábra   Az API-k engedélyezése (részlet)
 

A következő példánál már csak azt a függvényt látjuk, amelyből majd a task-ot tudjuk létrehozni. A végtelen ciklusban látjuk a D port 0. bitjének a negálását, illetve azt követően a szál felfüggesztésre kerül xDelay ideig. 

példa a szál altatására :

static void pelda( void *pvParameters )
{
    const portTickType xDelay = 500 / portTICK_RATE_MS;
    while(1)
    {
        LATDbits.LATD0 = !LATDbits.LATD0;
        vTaskDelay(xDelay);
    }
}

 

Ennél a példánál lekérdezzük a prioritást az uxTaskPriorityGet() API-val. Először létrehozunk egy xWE nevű handlert, amelyet a task létrehozásakor megadunk. A feltételes szerkezetben lekérdezzük az xWE-vel azonosított task prioritását, amely, ha nem egyenlő 3-al, akkor az if-es szerkezet magjában lévő kód kerül futtatásra.

példa a prioritás lekérdezésére :

void pelda( void )
{
     xTaskHandle xWE;

     xTaskCreate(Kod, "T0", 256, NULL, tskIDLE_PRIORITY, &xWE );

     if( uxTaskPriorityGet( xWE ) != 3 )
     {
         // kód
     }
}

 

Most azt nézzük meg egy példa segítségével, hogyan tudjuk egy task-nak a prioritását módosítani. Amikor az xTaskCreate()-tel létrehozunk egy szálat, akkor megadjuk a prioirtását, de ezt később a vTaskPrioritySet()-tel meg tudjuk változtatni.
Először egy handlert (xWE) hozunk létre, majd egy task-ot, amelynek a prioritása 1. Ezt azután módosítjuk 2-re. 

példa a prioritás megadására :

void pelda( void )
{
    xTaskHandle xWE;

    xTaskCreate(Kod, "T0", 256, NULL, 1, &xWE );
    vTaskPrioritySet( xWE, 2 );
}

 

Most azt nézzük meg egy példa segítségével, hogy a létrehozott task-ot hogyan tudjuk felfüggeszteni. Miután a Kod() függvényből elkészítünk egy task-ot, azután a vTaskSuspend() segítségével a szálat felfüggesztjük, nem fog kapni futási időt. A szál azonosításához a handlert használtuk fel.

példa a task felfüggesztésére :

void pelda( void )
{
     xTaskHandle xWE;
     xTaskCreate(Kod, "T0", 256, NULL, tskIDLE_PRIORITY, &xWE);

     vTaskSuspend( xWE);
}

 

Az előbbi példában felfüggesztett task a vTaskResume() API segítségével kaphat újra futási jogot.

példa a task futási állapotba hozatalára :

void pelda( void )
{
     xTaskHandle xWE;
     xTaskCreate(Kod, "T0", 256, NULL, tskIDLE_PRIORITY, &xWE);
     vTaskSuspend( xWE);
     vTaskResume(xWE);
}

 

Ha a magasabb prioritású szálat blokkoljuk, akkor az alacsonyabb kap futási jogot. De mi történik akkor, ha minden szál blokkolva van? "Valaminek" akkor is futnia kell..... Nos, ebben az esetben a pihenőszál (vApplicationIdleHook()) fog futni, ha ezt engedélyezzük (configUSE_IDLE_HOOK) a config file-ban (2. ábra). Fontos, hogy a pihenőszálnak a neve nem változtatható meg.

kep
2. ábra   FreeRTOSConfig.h file tartalma (részlet)
 

Ebben az ábrán találjuk a configUSE_PREEMPTION-t, amely szintén 0 vagy 1 értéket kaphat. Ha 0, akkor a kernel cooperatív, ha 1, akkor preemptív.

A pihenőszál prioritása a legkisebb, a háttérben fut. A példában látható, hogy a vApplicationIdleHook() függvény állandóan meghívásra kerül, és ekkor a D port 0. bitje negálásra kerül.

példa pihenőszálra:

void vApplicationIdleHook( void )
{
   LATDbits.LATD0 = !LATDbits.LATD0;
}

 

A kernel vezérlése (task.h)

  • vTaskStartScheduler  (elindítja az ütemezőt)
  • vTaskEndScheduler  (leállítja az ütemezőt)
  • vTaskSuspendAll  (felfüggeszti a kernel működését, interrupt engedélyezheti újra)
  • xTaskResumeAll