- Cum funcționează RTOS?
- Termeni frecvent folosiți în RTOS
- Instalarea Arduino FreeRTOS Library
- Diagrama circuitului
- Exemplu Arduino FreeRTOS - Crearea sarcinilor FreeRTOS în Arduino IDE
- Implementarea sarcinii FreeRTOS în Arduino IDE
Sistemul de operare prezent în interiorul dispozitivelor încorporate se numește RTOS (Real-Time Operating System). În dispozitivele încorporate, sarcinile în timp real sunt esențiale, în care sincronizarea joacă un rol foarte important. Sarcinile în timp real sunt determinante în timp înseamnă că timpul de răspuns la orice eveniment este întotdeauna constant, astfel încât să poată fi garantat că orice eveniment anume va avea loc la un moment fix. RTOS este conceput pentru a rula aplicații cu sincronizare foarte precisă și un grad ridicat de fiabilitate. RTOS ajută, de asemenea, la multi-tasking cu un singur nucleu.
Am acoperit deja un tutorial despre cum să utilizați RTOS în sistemele încorporate, unde puteți afla mai multe despre RTOS, diferența dintre sistemul de operare general și RTOS, diferitele tipuri de RTOS etc.
În acest tutorial, vom începe cu FreeRTOS. FreeRTOS este o clasă de RTOS pentru dispozitive încorporate care este suficient de mică pentru a putea rula pe microcontrolere de 8/16 biți, deși utilizarea sa nu se limitează la aceste microcontrolere. Este o sursă complet deschisă și codul său este disponibil pe github. Dacă cunoaștem câteva concepte de bază despre RTOS, atunci este foarte ușor de utilizat FreeRTOS, deoarece are API-uri bine documentate, care pot fi utilizate direct în cod, fără a cunoaște partea de backend a codării. Documentația completă FreeRTOS poate fi găsită aici.
Deoarece FreeRTOS poate rula pe MCU pe 8 biți, poate fi rulat și pe placa Arduino Uno. Trebuie doar să descărcăm biblioteca FreeRTOS și apoi să începem să implementăm codul folosind API-uri. Acest tutorial este destinat unui începător complet, mai jos sunt subiectele pe care le vom aborda în acest tutorial Arduino FreeRTOS:
- Cum funcționează RTOS
- Unii termeni folosiți frecvent în RTOS
- Instalarea FreeRTOS în Arduino IDE
- Cum se creează sarcini FreeRTOS cu un exemplu
Cum funcționează RTOS?
Înainte de a începe cu funcționarea RTOS, să vedem ce este o sarcină. Activitatea este o bucată de cod care poate fi executată pe CPU. Deci, dacă doriți să efectuați o anumită sarcină, atunci ar trebui să fie programată utilizând întârzierea kernelului sau folosind întreruperi. Această lucrare este realizată de Scheduler prezent în nucleu. Într-un procesor cu un singur nucleu, programatorul ajută sarcinile să se execute într-o anumită porțiune de timp, dar se pare că diferite sarcini se execută simultan. Fiecare sarcină se execută în funcție de prioritatea acordată acesteia.
Acum, să vedem ce se întâmplă în kernel-ul RTOS dacă vrem să creăm o sarcină pentru LED-ul intermitent cu un interval de o secundă și să punem această sarcină pe cea mai mare prioritate.
În afară de sarcina LED, va mai exista o sarcină care este creată de nucleu, este cunoscută sub numele de sarcină inactivă. Activitatea inactivă este creată atunci când nu este disponibilă nicio activitate pentru executare. Această sarcină rulează întotdeauna cu cea mai mică prioritate, adică 0 prioritate. Dacă analizăm graficul de sincronizare dat mai sus, se poate vedea că execuția începe cu o sarcină LED și rulează pentru un timp specificat, apoi pentru timpul rămas, sarcina inactivă rulează până când apare o întrerupere a bifării. Apoi, nucleul decide ce sarcină trebuie executată în funcție de prioritatea sarcinii și de timpul total scurs pentru sarcina LED. Când o secundă este finalizată, nucleul alege din nou sarcina condusă pentru a se executa deoarece are o prioritate mai mare decât sarcina inactivă, putem spune, de asemenea, că sarcina LED preîntâmpină sarcina inactivă. Dacă există mai mult de două sarcini cu aceeași prioritate, atunci acestea vor rula în mod rotund pentru un timp specificat.
Sub diagrama de stare, deoarece arată comutarea sarcinii care nu rulează în stare de rulare.
Fiecare activitate nou creată intră în starea Ready (parte a stării de nefuncționare). Dacă sarcina creată (Task1) are cea mai mare prioritate decât alte sarcini, atunci va trece la starea de rulare. Dacă această sarcină în curs de executare se oprește de cealaltă sarcină, atunci va reveni la starea de pregătire. Altfel, dacă sarcina 1 este blocată utilizând API-ul de blocare, atunci CPU nu va interacționa cu această sarcină până la expirarea timpului definit de utilizator.
Dacă Task1 este suspendat în starea de rulare utilizând API-uri de suspendare, atunci Task1 va trece la starea de suspendare și nu va mai fi disponibil din nou pentru planificator. Dacă reluați Task1 în starea suspendată, acesta va reveni la starea de pregătire așa cum puteți vedea în diagrama bloc.
Aceasta este ideea de bază a modului în care Sarcinile rulează și își schimbă stările. În acest tutorial, vom implementa două sarcini în Arduino Uno folosind FreeRTOS API.
Termeni frecvent folosiți în RTOS
1. Sarcină: este o bucată de cod care poate fi executată pe CPU.
2. Programator: este responsabil pentru selectarea unei sarcini din lista de stări pregătite până la starea de rulare. Programatorii sunt adesea implementați, astfel încât să țină toate resursele computerului ocupate (ca în echilibrarea încărcării).
3. Preemption: Este acțiunea de a întrerupe temporar o sarcină deja executată cu intenția de a o elimina din starea de rulare fără cooperarea acesteia.
4. Comutarea contextului: în prevenirea bazată pe priorități, planificatorul compară prioritatea sarcinilor care rulează cu prioritatea listei de sarcini gata pe fiecare întrerupere sistemică . Dacă există o sarcină în listă a cărei prioritate este mai mare decât sarcina de executare, atunci are loc comutarea contextului. Practic, în acest proces conținutul diferitelor sarcini este salvat în memoria lor respectivă.
5. Tipuri de politici de programare:
- Planificare preventivă: în acest tip de planificare, sarcinile se execută cu felie de timp egală, fără a lua în considerare prioritățile.
- Prioritate bazată pe prioritate : sarcina cu prioritate înaltă va rula mai întâi.
- Programare în cooperare: Comutarea contextului se va întâmpla numai cu cooperarea sarcinilor care rulează. Sarcina va rula continuu până când se apelează randamentul sarcinii.
6. Obiecte Kernel: Pentru a semnaliza sarcina pentru a efectua unele lucrări, se utilizează procesul de sincronizare. Pentru a efectua acest proces sunt folosite obiecte Kernel. Unele obiecte Kernel sunt Evenimente, Semafore, Cozi, Mutex, Cutii poștale etc. Vom vedea cum să folosim aceste obiecte în tutoriale viitoare.
Din discuția de mai sus, avem câteva idei de bază despre conceptul RTOS și acum putem implementa proiectul FreeRTOS în Arduino. Deci, să începem instalând biblioteci FreeRTOS în Arduino IDE.
Instalarea Arduino FreeRTOS Library
1. Deschideți Arduino IDE și accesați Sketch -> Include Library -> Manage Libraries . Căutați FreeRTOS și instalați biblioteca așa cum se arată mai jos.
Puteți descărca biblioteca de pe github și adăugați fișierul.zip în Sketch-> Include Library -> Add.zip file.
Acum, reporniți ID-ul Arduino. Această bibliotecă oferă câteva exemple de cod, de asemenea, care pot fi găsite în Fișier -> Exemple -> FreeRTOS așa cum se arată mai jos.
Aici vom scrie codul de la zero pentru a înțelege funcționarea, ulterior puteți verifica exemplele de coduri și le puteți utiliza.
Diagrama circuitului
Mai jos este schema de circuit pentru crearea activității LED intermitent utilizând FreeRTOS pe Arduino:
Exemplu Arduino FreeRTOS - Crearea sarcinilor FreeRTOS în Arduino IDE
Să vedem o structură de bază pentru a scrie un proiect FreeRTOS.
1. În primul rând, includeți fișierul antet Arduino FreeRTOS ca
#include
2. Oferiți prototipul funcției tuturor funcțiilor pe care le scrieți pentru executare, care este scris ca
void Task1 (void * pvParameters); void Task2 (void * pvParameters); .. …
3. Acum, în funcția de configurare nulă () , creați activități și porniți programatorul de activități.
Pentru crearea sarcinii, API-ul xTaskCreate () este apelat în funcția de configurare cu anumiți parametri / argumente.
xTaskCreate (TaskFunction_t pvTaskCode, const char * const pcName, uint16_t usStackDepth, void * pvParameters, UBaseType_t uxPriority, TaskHandle_t * pxCreatedTask);
Există 6 argumente care ar trebui transmise în timpul creării oricărei sarcini. Să vedem care sunt aceste argumente
- pvTaskCode: Este pur și simplu un indicator către funcția care implementează sarcina (de fapt, doar numele funcției).
- pcName: un nume descriptiv pentru sarcină. Acest lucru nu este utilizat de FreeRTOS. Este inclus numai în scopuri de depanare.
- usStackDepth: Fiecare activitate are propria sa stivă unică care este alocată de nucleu sarcinii atunci când sarcina este creată. Valoarea specifică numărul de cuvinte pe care stiva le poate conține, nu numărul de octeți. De exemplu, dacă stiva are 32 de biți lățime și usStackDepth este transmis ca 100, atunci 400 de octeți de spațiu de stivă vor fi alocați (100 * 4 octeți) în RAM. Utilizați acest lucru cu înțelepciune, deoarece Arduino Uno are doar 2Kbyte RAM.
- pvParameters: Parametrul de intrare a activității (poate fi NULL).
- uxPriority: Prioritatea sarcinii (0 este cea mai mică prioritate).
- pxCreatedTask: Poate fi folosit pentru a transmite un mâner sarcinii create. Acest handle poate fi apoi utilizat pentru a face referire la sarcina din apelurile API care, de exemplu, modifică prioritatea sarcinii sau șterg sarcina (poate fi NUL).
Exemplu de creare a sarcinilor
xTaskCreate (task1, "task1", 128, NULL, 1, NULL); xTaskCreate (task2, "task2", 128, NULL, 2, NULL);
Aici, Task2 are o prioritate mai mare și, prin urmare, se execută mai întâi.
4. După crearea sarcinii, porniți programatorul într-o configurare nulă utilizând vTaskStartScheduler (); API.
5. Funcția Buclă vid () va rămâne goală, deoarece nu vrem să rulăm nicio sarcină manual și infinit. Deoarece execuția sarcinii este acum gestionată de Scheduler.
6. Acum, trebuie să implementăm funcții de sarcină și să scriem logica pe care doriți să o executați în aceste funcții. Numele funcției ar trebui să fie același cu primul argument al xTaskCreate () API.
void task1 (void * pvParameters) { while (1) { .. ..// logica ta } }
7. Majoritatea codului are nevoie de funcție de întârziere pentru a opri sarcina de rulare, dar în RTOS nu este sugerat să folosiți funcția Întârziere () deoarece oprește procesorul și, prin urmare, RTOS nu mai funcționează. Deci, FreeRTOS are un API de kernel pentru a bloca sarcina pentru o anumită perioadă de timp.
vTaskDelay (const TickType_t xTicksToDelay);
Acest API poate fi utilizat în scopuri de întârziere. Acest API întârzie o sarcină pentru un anumit număr de căpușe. Timpul real pentru care sarcina rămâne blocată depinde de rata de bifare. Constante portTICK_PERIOD_MS pot fi folosite pentru a calcula în timp real de la rata de căpușă.
Aceasta înseamnă că, dacă doriți o întârziere de 200 ms, scrieți doar această linie
vTaskDelay (200 / portTICK_PERIOD_MS);
Deci, pentru acest tutorial, vom folosi aceste API-uri FreeRTOS pentru a implementa trei sarcini.
API-uri care trebuie utilizate:
- xTaskCreate ();
- vTaskStartScheduler ();
- vTaskDelay ();
Sarcină de creat pentru acest tutorial:
- LED-ul clipește la pinul digital 8 cu o frecvență de 200 ms
- LED-ul clipește la pinul digital 7 cu o frecvență de 300 ms
- Imprimați numerele în monitor serial cu o frecvență de 500 ms.
Implementarea sarcinii FreeRTOS în Arduino IDE
1. Din explicația de mai sus a structurii de bază, includeți fișierul antet Arduino FreeRTOS. Apoi faceți prototipuri funcționale. Deoarece avem trei sarcini, faceți trei funcții și sunt prototipuri.
#include void TaskBlink1 (void * pvParameters); void TaskBlink2 (void * pvParameters); void Taskprint (void * pvParameters);
2. În funcția de configurare nulă () , inițializați comunicația serială la 9600 biți pe secundă și creați toate cele trei activități folosind xTaskCreate () API. Inițial, stabiliți prioritățile tuturor sarcinilor ca „1” și porniți planificatorul.
void setup () { Serial.begin (9600); xTaskCreate (TaskBlink1, "Task1", 128, NULL, 1, NULL); xTaskCreate (TaskBlink2, "Task2", 128, NULL, 1, NULL); xTaskCreate (Taskprint, "Task3", 128, NULL, 1, NULL); vTaskStartScheduler (); }
3. Acum, implementați toate cele trei funcții așa cum se arată mai jos pentru LED-ul task1 clipește.
void TaskBlink1 (void * pvParameters) { pinMode (8, OUTPUT); while (1) { digitalWrite (8, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (8, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
În mod similar, implementați funcția TaskBlink2. Funcția Task3 va fi scrisă ca
void Taskprint (void * pvParameters) { int counter = 0; while (1) { counter ++; Serial.println (contor); vTaskDelay (500 / portTICK_PERIOD_MS); } }
Asta e. Am finalizat cu succes un proiect FreeRTOS Arduino pentru Arduino Uno. Puteți găsi codul complet împreună cu un videoclip la sfârșitul acestui tutorial.
În cele din urmă, conectați două LED-uri la pinul digital 7 și 8 și încărcați codul pe placa Arduino și deschideți monitorul Serial. Veți vedea că un contor rulează o dată la 500 ms cu numele activității, așa cum se arată mai jos.
De asemenea, observați LED-urile, acestea clipesc la diferite intervale de timp. Încercați să jucați cu argumentul de prioritate din funcția xTaskCreate . Schimbați numărul și respectați comportamentul pe monitorul serial și pe LED-uri.
Acum, puteți înțelege primele două exemple de coduri în care sunt create sarcini de citire analogică și citire digitală. În acest fel, puteți face mai multe proiecte avansate folosind doar API-urile Arduino Uno și FreeRTOS.