This shows you the differences between two versions of the page.
patr:laboratoare:07 [2022/01/09 15:56] alexandru.ionita99 [Crearea unui Task] |
patr:laboratoare:07 [2022/02/14 15:16] (current) alexandru.ionita99 [Task-uri FreeRTOS] |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Laboratorul 07 - Programare în timp-real pe Arduino ===== | ===== Laboratorul 07 - Programare în timp-real pe Arduino ===== | ||
- | ===== Fire de execuție în Ardunio ===== | + | ==== Fire de execuție în Ardunio ==== |
Întrucât microcontroller-ele prezente pe Arduino sunt single-chip și single core, un singur fir de execuție poate rula simultan. Mai mult, Arduino este construit în jurul unui singur fir de execuție principal, ce rulează în interiorul funcției **void loop()** | Întrucât microcontroller-ele prezente pe Arduino sunt single-chip și single core, un singur fir de execuție poate rula simultan. Mai mult, Arduino este construit în jurul unui singur fir de execuție principal, ce rulează în interiorul funcției **void loop()** | ||
Line 18: | Line 18: | ||
Pentru a putea diviza programul în mai multe fire de execuție și a avea o rulare cvasi-paralelă a acestora, utilizăm biblioteca FreeRTOS, pe care am instalat-o în cadrul laboratorului trecut. | Pentru a putea diviza programul în mai multe fire de execuție și a avea o rulare cvasi-paralelă a acestora, utilizăm biblioteca FreeRTOS, pe care am instalat-o în cadrul laboratorului trecut. | ||
- | ===== Ce este FreeRTOS? ===== | + | ==== Ce este FreeRTOS? ==== |
În general, un sistem de operare controlează procesele și resursele hardware ale unui calculator, definind regulile care permit unui program să acceseze servicii și să interacționeze cu sistemul de calcul. \\ | În general, un sistem de operare controlează procesele și resursele hardware ale unui calculator, definind regulile care permit unui program să acceseze servicii și să interacționeze cu sistemul de calcul. \\ | ||
Line 45: | Line 45: | ||
modificarea acestora se realizează cu ajutorul Semaphore API. Toate aceste funcții sunt definite și prezentate detaliat în documentația FreeRTOS, disponibilă pe site-ul https://www.freertos.org/. | modificarea acestora se realizează cu ajutorul Semaphore API. Toate aceste funcții sunt definite și prezentate detaliat în documentația FreeRTOS, disponibilă pe site-ul https://www.freertos.org/. | ||
- | ===== FreeRTOS Scheduler ===== | + | ==== FreeRTOS Scheduler ==== |
Tot mecanismul de funcționare FreeRTOS are la bază un scheduler, care gestionează task-urile și alte elemente real-time și realizează preempțiunea, atunci când este cazul. Funcția de pornire a scheduler-ului este **vTaskStartScheduler()** și, în mod normal, este apleată la sfârșitul funcției **void setup()**, pentru a începe gestionarea elementelor de programare real-time. | Tot mecanismul de funcționare FreeRTOS are la bază un scheduler, care gestionează task-urile și alte elemente real-time și realizează preempțiunea, atunci când este cazul. Funcția de pornire a scheduler-ului este **vTaskStartScheduler()** și, în mod normal, este apleată la sfârșitul funcției **void setup()**, pentru a începe gestionarea elementelor de programare real-time. | ||
- | ===== Task-uri FreeRTOS ===== | + | ==== Task-uri FreeRTOS ==== |
- | Crearea, ștergerea și gestionarea task-urilor se realizează cu ajutorul **Task and Scheduler API**. | + | Crearea, ștergerea și gestionarea task-urilor se realizează cu ajutorul **Task and Scheduler API**. Pentru a utiliza task-uri, trebuie inclusă și biblioteca //task.h//, alături de //FreeRTOS.h//. |
- | ==== Crearea unui Task ==== | + | |
+ | |||
+ | <note warning>Utilizarea FreeRTOS face ca funcția clasică **void loop()** să nu mai aibă nicio utilitate, întrucât toată logica programului se desfășoară în task-uri separate. Astfel, funcția va fi doar definită, pentru a nu primi erori la compilare, dar va rămâne goală</note> | ||
+ | |||
+ | === Crearea unui Task === | ||
Pentru a crea un nou task, utilizăm funcția **BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, const char * const pcName, unsigned short usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask )**, care are următorii parametri: | Pentru a crea un nou task, utilizăm funcția **BaseType_t xTaskCreate( TaskFunction_t pvTaskCode, const char * const pcName, unsigned short usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask )**, care are următorii parametri: | ||
Line 83: | Line 87: | ||
void setup() | void setup() | ||
{ | { | ||
- | TaskHandle_t xHandle; | + | |
+ | ... | ||
+ | |||
+ | // Definim un element de tipul TaskHandle, pentru a referenția viitorul task | ||
+ | TaskHandle_t xHandle; | ||
- | /* Create the task. */ | + | // Creăm task-ul |
if( xTaskCreate( | if( xTaskCreate( | ||
- | vTaskCode, /* Pointer to the function that implements the task. */ | + | vTaskCode, //Pointer către funcția ce va rula la pornirea task-ului |
- | "Demo task", /* Text name given to the task. */ | + | "Demo task", // Un nume pentru task |
- | STACK_SIZE, /* The size of the stack that should be created for the task. | + | 200, // Sunt alocate 200 cuvinte de memorie din stivă pentru acest task |
- | This is defined in words, not bytes. */ | + | (void*) &xParameter, // Cast la (void*) pentru parametrul ce va fi transmis funcției |
- | (void*) &xParameter,/* A reference to xParameters is used as the task parameter. | + | 1, // Task-ul are setată prioritatea |
- | This is cast to a void * to prevent compiler warnings. */ | + | &xHandle // Handle pentru task-ul creat |
- | TASK_PRIORITY, /* The priority to assign to the newly created task. */ | + | |
- | &xHandle /* The handle to the task being created will be placed in | + | |
- | xHandle. */ | + | |
) != pdPASS ) | ) != pdPASS ) | ||
{ | { | ||
- | /* The task could not be created as there was insufficient heap memory remaining. If | + | // Task-ul nu a putut fi creat. Nu există suficient spațiu în stack. |
- | heap_1.c, heap_2.c or heap_4.c are included in the project then this situation can be | + | |
- | trapped using the vApplicationMallocFailedHook() callback (or ‘hook’) function, and the | + | |
- | amount of FreeRTOS heap memory that remains unallocated can be queried using the | + | |
- | xPortGetFreeHeapSize() API function.*/ | + | |
} | } | ||
else | else | ||
{ | { | ||
- | /* The task was created successfully. The handle can now be used in other API functions, | + | // Task-ul a fost creat cu succes |
- | for example to change the priority of the task.*/ | + | |
+ | // Schimbăm prioritatea task-ului, utilizând handle-ul xHandle | ||
vTaskPrioritySet( xHandle, 2 ); | vTaskPrioritySet( xHandle, 2 ); | ||
} | } | ||
+ | |||
+ | // Pornim scheduler-ul FreeRTOS. Fără acest pas, task-ul nu ar rula. | ||
+ | vTaskStartScheduler(); | ||
+ | |||
} | } | ||
+ | // Funcția ce va fi rulată la execuția task-ului | ||
void vTaskCode( void * pvParameters ) | void vTaskCode( void * pvParameters ) | ||
{ | { | ||
xStruct *pxParameters; | xStruct *pxParameters; | ||
- | /* Cast the void * parameter back to the required type. */ | + | // Cast al parametrului înapoi la tipul necesar |
pxParameters = ( xStruct * ) pvParameters; | pxParameters = ( xStruct * ) pvParameters; | ||
- | /* The parameter can now be accessed as expected. */ | + | // Lucru cu parametrul |
if( pxParameters->cStructMember1 != 1 ) | if( pxParameters->cStructMember1 != 1 ) | ||
{ | { | ||
Line 125: | Line 132: | ||
} | } | ||
- | /* Enter an infinite loop to perform the task processing. */ | + | // Buclă infinită |
for( ;; ) | for( ;; ) | ||
{ | { | ||
- | /* Task code goes here. */ | + | ... |
} | } | ||
} | } | ||
+ | |||
+ | // Definim funcția void loop(), dar rămâne goală, pentru că utilizăm task-uri | ||
+ | void loop(){} | ||
+ | |||
</code> | </code> | ||
+ | |||
+ | |||
+ | === vTaskDelay() === | ||
+ | |||
+ | O funcție foarte importantă din Task API este **void vTaskDelay( TickType_t xTicksToDelay );**. Aceasta înlocuiește clasica funcție **delay()** din Arduino. Funcția plasează **task-ul din care a fost apelată** în starea **BLOCAT** pentru o perioadă de X ticks, unde X este oferit ca parametru, timp în care alte task-uri, de exemplu cele cu o prioritate mai mică, pot accesa procesorul. | ||
+ | |||
+ | Pentru a transforma timpul din milisecunde în ticks, utilizăm funcția **pdMS_TO_TICKS()**. | ||
+ | |||
+ | <note warning>Utilizarea funcției **delay()** nu este recomandată împreună cu FreeRTOS, deoarece conduce la **suspendarea activității procesorului**, adică a tutoror task-urilor care rulează. Dacă în cazul programării clasice, acest aspect nu era o problemă (exista un singur task), în cazul FreeRTOS pot apărea efecte neașteptate.</note> | ||
+ | |||
+ | |||
+ | |||
+ | **Exemplu:** | ||
+ | <code c> | ||
+ | void vAnotherTask( void * pvParameters ) | ||
+ | { | ||
+ | for( ;; ) | ||
+ | { | ||
+ | ... | ||
+ | // Intră în starea BLOCAT pentru 20 ticks | ||
+ | vTaskDelay( 20 ); | ||
+ | |||
+ | // Intră în starea blocat pentru 20 ms | ||
+ | vTaskDelay( pdMS_TO_TICKS( 20 ) ); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | === Alte funcții utile === | ||
+ | |||
+ | * **vTaskDelete( TaskHandle_t pxTask ) ** - Șterge task-ul aferent hande-ului oferit ca parametru. Este eliberată memoria alocată automat la momentul creării task-ului | ||
+ | * **xTaskGetCurrentTaskHandle() ** - Este întors handle-ul task-ului care se află **în execuție** la acel moment | ||
+ | * **xTaskGetHandle( const char *pcNameToQuery ) ** - Este întors handle-ul task-ului cu numele oferit ca parametru | ||
+ | * **pcTaskGetName( TaskHandle_t xTaskToQuery ) ** - Întoarce numele task-ului referit prin handle | ||
+ | * **uxTaskPriorityGet( TaskHandle_t pxTask ) ** - Întoarce prioritatea task-ului referit prin hadle, la acel moment | ||
+ | * **vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority ) ** - Setează o nouă prioritate pentru un task | ||
+ | * **vTaskSuspend( TaskHandle_t pxTaskToSuspend ) ** - Pune task-ul în starea **Suspendat** | ||
+ | * **vTaskResume( TaskHandle_t pxTaskToResume) ** - Pune task-ul în starea **Running**. Funcționează doar pentru task-uri **suspendate** | ||
+ | * **taskYIELD() ** - Se apelează dintr-un task **Running**, care iși oferă partiția de timp către alte task-uri **de aceeași prioritate** |