- Ștergerea unei activități în FreeRTOS Arduino
- Ce este coada în FreeRTOS?
- Crearea unei cozi în FreeRTOS
- Diagrama circuitului
- Implementarea cozii FreeRTOS în Arduino IDE
În tutorialul anterior, am introdus FreeRTOS în Arduino Uno și am creat o sarcină pentru LED-ul intermitent. Acum, în acest tutorial, ne vom scufunda mai mult în conceptele avansate ale API-urilor RTOS și vom afla despre comunicarea dintre diferite sarcini. Aici aflăm și despre Queue pentru a transfera date dintr-o sarcină în alta și pentru a demonstra funcționarea API-urilor de coadă prin interfața LCD 16x2 și LDR cu Arduino Uno.
Înainte de a discuta despre cozi, să vedem încă o API FreeRTOS care este utilă în ștergerea sarcinilor atunci când este terminată cu lucrarea atribuită. Uneori, sarcina trebuie ștearsă pentru a elibera memoria alocată. În continuarea tutorialului anterior, vom folosi vTaskDelete () funcția API în același cod pentru a șterge una dintre sarcini. O activitate poate utiliza funcția API vTaskDelete () pentru a se șterge singură sau orice altă activitate.
Pentru a utiliza acest API, trebuie să configurați fișierul FreeRTOSConfig.h . Acest fișier este utilizat pentru a adapta FreeRTOS în funcție de aplicație. Este folosit pentru a schimba algoritmii de planificare și mulți alți parametri. Fișierul poate fi găsit în Arduino Directory, care este în general disponibil în folderul Documente al computerului. În cazul meu, este disponibil în \ Documents \ Arduino \ libraries \ FreeRTOS \ src așa cum se arată mai jos.
Acum, deschide acest fișier folosind orice editor de text și de căutare pentru # define INCLUDE_vTaskDelete și asigurați - vă că valoarea sa este „1“ (1 înseamnă activa și 0 înseamnă dezactivare). Este 1 în mod implicit, dar îl verifică.
Vom folosi frecvent acest fișier de configurare în următoarele tutoriale pentru setarea parametrilor.
Acum, să vedem cum să ștergeți o activitate.
Ștergerea unei activități în FreeRTOS Arduino
Pentru a șterge o sarcină, trebuie să folosim funcția API vTaskDelete (). Este nevoie de un singur argument.
vTaskDelete (TaskHandle_t pxTaskToDelete);
pxTaskToDelete: este mânerul sarcinii care urmează să fie șters. Este la fel ca 6 - lea argument al xTaskCreate () API. În tutorialul anterior, acest argument este setat ca NULL, dar puteți transmite adresa conținutului sarcinii folosind orice nume. Spuneți dacă doriți să setați mânerul sarcinii pentru Task2 care este declarat ca
TaskHandle_t any_name; Exemplu: TaskHandle_t xTask2Handle;
Acum, în vTaskCreate () API setați al 6- lea argument ca
xTaskCreate (TaskBlink2, "task2", 128, NULL, 1 și & xTask2Handle);
Conținutul acestei sarcini poate fi acum accesat folosind mânerul dat de dvs.
De asemenea, o sarcină se poate șterge singură trecând NULL în locul unui handle valid al sarcinii.
Dacă vrem să ștergem sarcina 3 din sarcina 3 în sine, trebuie să scrieți vTaskDelete (NULL); în interiorul funcției Task3, dar dacă doriți să ștergeți sarcina 3 din sarcina 2, scrieți vTaskDelete (xTask3Handle); în interiorul funcției task2.
În codul anterior de tutorial, pentru a șterge Task2 din task2 în sine, trebuie doar să adăugați vTaskDelete (NULL); în funcția void TaskBlink2 (void * pvParameters) . Atunci funcția de mai sus va arăta astfel
void TaskBlink2 (void * pvParameters) { Serial.println („Task2 se execută și urmează să fie șters”); vTaskDelete (NULL); pinMode (7, OUTPUT); while (1) { digitalWrite (7, HIGH); vTaskDelay (300 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (300 / portTICK_PERIOD_MS); } }
Acum, încărcați codul și respectați LED-urile și monitorul serial. Veți vedea că al doilea LED nu clipește acum și task2 este șters după ce ați întâlnit API-ul de ștergere.
Deci, acest API poate fi folosit pentru a opri executarea sarcinii particulare.
Acum, să începem cu Coada.
Ce este coada în FreeRTOS?
Coada este structura de date care poate conține numărul finit de elemente de dimensiuni fixe și este operată în schema FIFO (First-in First-out). Cozile oferă un mecanism de comunicare sarcină-sarcină, sarcină-întrerupere și întrerupere-sarcină.
Numărul maxim de elemente pe care poate fi păstrată coada se numește „lungimea” acestuia. Atât lungimea, cât și dimensiunea fiecărui element sunt setate la crearea cozii.
Un exemplu despre modul în care coada este utilizată pentru transferul de date este ilustrat bine în documentația FreeRTOS care poate fi găsită aici. Puteți înțelege cu ușurință exemplul dat.
(…)După ce înțelegem cozile, să încercăm să înțelegem procesul de creare a unei cozi și să încercăm să o implementăm în codul nostru FreeRTOS.
Crearea unei cozi în FreeRTOS
Mai întâi, descrieți declarația de problemă care urmează să fie implementată cu ajutorul cozii FreeRTOS și Arduino Uno.
Vrem să imprimăm valoarea senzorului LDR pe ecranul LCD 16 * 2. Deci, există două sarcini acum
- Task1 obține valori analogice ale LDR.
- Task2 tipărește valoarea analogică pe ecranul LCD.
Deci, aici coada joacă rolul său, deoarece pentru a trimite datele generate de task1 la task2. În task1, vom trimite valoare analogică la coadă și în task2, o vom primi din coadă.
Există trei funcții pentru a lucra cu cozile
- Crearea unei cozi
- Trimiterea datelor către Coadă
- Primirea datelor din Coadă
Pentru crearea cozii, utilizați funcția API xQueueCreate (). Este nevoie de două argumente.
xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
uxQueueLength: Numărul maxim de articole pe care coada creată le poate păstra simultan.
uxItemSize: Dimensiunea în octeți a fiecărui element de date care poate fi stocată în coadă.
Dacă această funcție returnează NULL, atunci coada nu este creată din cauza memoriei insuficiente și dacă returnează o valoare non-NULL, coada este creată cu succes. Stocați această valoare de returnare într-o variabilă pentru a o folosi ca un mâner pentru a accesa coada așa cum se arată mai jos.
QueueHandle_t queue1; queue1 = xQueueCreate (4, sizeof (int));
Aceasta va crea o coadă de 4 elemente în memoria heap de dimensiunea int (2 octeți din fiecare bloc) și va stoca valoarea returnată în variabila handle-ului queue1 .
2. Trimiterea datelor la coadă în FreeRTOS
Pentru a trimite valorile la coadă, FreeRTOS are 2 variante de API în acest scop.
- xQueueSendToBack (): Folosit pentru a trimite date în spatele (coada) unei cozi.
- xQueueSendToFront (): Folosit pentru a trimite date către partea din față (capul) unei cozi.
Acum , xQueueSend () este echivalent cu și exact la fel ca xQueueSendToBack ().
Toate aceste API-uri acceptă 3 argumente.
xQueueSendToBack (QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);
xQueue: mânerul cozii la care datele sunt trimise (scrise). Această variabilă este aceeași cu cea utilizată pentru a stoca valoarea returnată a xQueueCreate API.
pvItemToQueue: un indicator către datele care trebuie copiate în coadă.
xTicksToWait: perioada maximă de timp în care sarcina ar trebui să rămână în starea Blocat pentru a aștepta ca spațiul să devină disponibil în coadă.
Setarea xTicksToWait la portMAX_DELAY va determina sarcina să aștepte la nesfârșit (fără expirare), cu condiția ca INCLUDE_vTaskSuspend să fie setată la 1 în FreeRTOSConfig.h altfel puteți utiliza macro pdMS_TO_TICKS () pentru a converti un timp specificat în milisecunde într-un timp specificat în ticks.
3. Primirea datelor din coadă în FreeRTOS
Pentru a primi (citi) un articol dintr-o coadă, se utilizează xQueueReceive (). Elementul primit este eliminat din coadă.
Acest API ia, de asemenea, trei argumente.
xQueueReceive (QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait);
Primul și al treilea argument sunt aceleași cu trimiterea API. Numai al doilea argument este diferit.
const pvBuffer: un indicator către memoria în care vor fi copiate datele primite.
Sper că ați înțeles cele trei API-uri. Acum, vom implementa aceste API-uri în IDE-ul Arduino și vom încerca să rezolvăm afirmația problemă pe care am descris-o mai sus.
Diagrama circuitului
Așa arată pe panou:
Implementarea cozii FreeRTOS în Arduino IDE
Să începem să scriem cod pentru aplicația noastră.
1. Mai întâi, deschideți Arduino IDE și includeți fișierul de antet Arduino_FreeRTOS.h . Acum, dacă se folosește orice obiect kernel, cum ar fi coada, includeți fișierul antet al acestuia. Deoarece folosim 16 * 2 LCD, așa că includeți și biblioteca pentru acesta.
#include #include
2. Inițializați un mâner de coadă pentru a stoca conținutul cozii. De asemenea, inițializați numerele de pin LCD.
QueueHandle_t coadă_1; LiquidCrystal lcd (7, 8, 9, 10, 11, 12);
3. În setarea nulă (), inițializați monitorul LCD și serial cu o rată de 9600 baud. Creați o coadă și două activități folosind API-urile respective. Aici vom crea o coadă de dimensiunea 4 cu tip întreg. Creați o sarcină cu priorități egale și mai târziu încercați să jucați cu acest număr. În cele din urmă, porniți planificatorul așa cum se arată mai jos.
void setup () { Serial.begin (9600); lcd.inceput (16, 2); coadă_1 = xQueueCreate (4, sizeof (int)); if (queue_1 == NULL) { Serial.println ("Coada nu poate fi creată"); } xTaskCreate (TaskDisplay, "Display_task", 128, NULL, 1, NULL); xTaskCreate (TaskLDR, "LDR_task", 128, NULL, 1, NULL); vTaskStartScheduler (); }
4. Acum, creați două funcții TaskDisplay și TaskLDR . În funcția TaskLDR , citiți pinul analogic A0 într-o variabilă deoarece avem LDR conectat la pinul A0 al Arduino UNO. Acum trimiteți valoarea stocată în variabilă trecând-o în API xQueueSend și trimiteți sarcina la starea de blocare după 1 secundă folosind vTaskDelay () API așa cum se arată mai jos.
void TaskLDR (void * pvParameters) { int current_intensity; while (1) { Serial.println ("Task1"); current_intensity = analogRead (A0); Serial.println (current_intensity); xQueueSend (coadă_1, & current_intensity, portMAX_DELAY); vTaskDelay (1000 / portTICK_PERIOD_MS); } }
5. În mod similar, creați o funcție pentru TaskDisplay și primiți valorile într-o variabilă care este trecută la funcția xQueueReceive . De asemenea, xQueueReceive () returnează pdPASS dacă datele pot fi primite cu succes de la coadă și returnează errQUEUE_EMPTY dacă o coadă este goală.
Acum, afișați valorile pe ecranul LCD utilizând funcția lcd.print () .
void TaskDisplay (void * pvParameters) { int intensitate = 0; while (1) { Serial.println ("Task2"); if (xQueueReceive (coadă_1, & intensitate, portMAX_DELAY) == pdPASS) { lcd.clear (); lcd.setCursor (0, 0); lcd.print ("Intensitate:"); lcd.setCursor (11, 0); lcd.print (intensitate); } } }
Asta e. Am terminat partea de codificare a implementării cozii. Codul complet cu un videoclip funcțional poate fi găsit la sfârșit.
Acum, conectați LCD-ul și LDR-ul cu Arduino UNO conform schemei de circuite încărcați codul. Deschideți monitorul serial și respectați sarcinile. Veți vedea că sarcinile se schimbă și valorile LDR se schimbă în funcție de intensitatea luminii.
NOTĂ: Majoritatea bibliotecilor realizate pentru senzori diferiți nu sunt acceptate de kernel-ul FreeRTOS din cauza implementării funcției de întârziere în biblioteci. Întârzierea face ca CPU să se oprească complet, prin urmare, kernel-ul FreeRTOS nu mai funcționează și codul nu se va mai executa și începe să se comporte greșit. Deci, trebuie să facem bibliotecile fără întârziere pentru a lucra cu FreeRTOS.