Megnézzük most a task-ok létrehozásának és felügyeletének a lehetőségeit.
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);
Ezek az API-k akkor használható, ha a FreeRTOSConfig.h file-ban engedélyezve vannak (1) (1. ábra)
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.
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;
}
Hát igen.... Elérkeztünk a 100. cikkhez. Köszönjük az érdeklődést, a visszajelzéseket. Ezért valami érdekessel, nem annyira ismert témával ünnepeljük ezt a kerek fordulót. A választás a FreeRTOS-ra, egy valósidejű operációs rendszerre esett. Ezt a be. . . .
Megismerkedünk most gyakorlati oldalról az USB-CDC használatával. Készítünk egy projektet az MpLab segítségével, amelynek segítségével LED-eket kapcsolunk be, illetve ki, de megnézzük azt is, hogy hogyan tudunk a mikrovezérlőből beolvasni string-et.. . . .
Folytatjuk tovább a FreeRTOS megismerését, de most a száraz elméletet félretéve, összeállítjuk az első projektünket.. . . .