- Čo je Semafor?
- Ako používať Semafor vo FreeRTOS?
- Vysvetlenie semaforového kódu
- Schéma zapojenia
- Čo je Mutex?
- Ako používať Mutex vo FreeRTOS?
- Vysvetlenie kódu Mutex
V predchádzajúcich tutoriáloch sme sa venovali základom FreeRTOS s Arduinom a objektu jadra Queue vo FreeRTOS Arduino. Teraz sa v tomto treťom tutoriáli k FreeRTOS dozvieme viac o FreeRTOS a jeho pokrokových API, vďaka ktorým môžete hlbšie porozumieť platforme pre viac úloh.
Semafor a Mutex (vzájomné vylúčenie) sú objekty jadra, ktoré sa používajú na synchronizáciu, správu zdrojov a ochranu zdrojov pred poškodením. V prvej polovici tohto tutoriálu si pozrieme myšlienku, ktorá stojí za Semaphore, ako a kde ju použiť. V druhej polovici budeme pokračovať Mutexom.
Čo je Semafor?
V predchádzajúcich tutoriáloch sme diskutovali o prioritách úloh a tiež sme sa dozvedeli, že úloha s vyššou prioritou predchádza úlohe s nižšou prioritou, takže pri vykonávaní úlohy s vysokou prioritou môže existovať možnosť, že v úlohe s nízkou prioritou môže dôjsť k poškodeniu údajov, pretože ešte nie je vykonaná a dáta k tejto úlohe prichádzajú priebežne zo senzora, ktorý spôsobí stratu dát a nefunkčnosť celej aplikácie.
Je teda potrebné chrániť zdroje pred stratou údajov, a preto tu hrá dôležitú úlohu Semafor.
Semafor je signalizačný mechanizmus, v ktorom je úloha v stave čakania signalizovaná inou úlohou na vykonanie. Inými slovami, keď úloha1 dokončí svoju prácu, potom zobrazí príznak alebo zvýši príznak o 1 a potom tento príznak dostane iná úloha (úloha2), ktorá ukazuje, že teraz môže vykonávať svoju prácu. Keď úloha 2 dokončí svoju prácu, príznak sa zníži o 1.
V zásade teda ide o mechanizmus „Dajte“ a „Vezmite“ a semafor je celočíselná premenná, ktorá sa používa na synchronizáciu prístupu k zdrojom.
Typy semaforov vo FreeRTOS:
Semafor je dvoch typov.
- Binárny semafor
- Počítam semafor
1. Binárny semafor: Má dve celočíselné hodnoty 0 a 1. Je trochu podobný frontu dĺžky 1. Napríklad máme dve úlohy, task1 a task2. Úloha 1 odosiela údaje do úlohy 2, takže úloha 2 nepretržite kontroluje položku frontu, ak je 1, potom môže čítať údaje, ktoré musí čakať, kým sa z nich nestane 1. Po prevzatí údajov úloha 2 zníži poradie frontu a urobí z neho 0 To znamená znova úlohu1 môže odoslať údaje úlohe2.
Z vyššie uvedeného príkladu je možné povedať, že binárny semafor sa používa na synchronizáciu medzi úlohami alebo medzi úlohami a prerušením.
2. Počítanie semaforu: Má hodnoty väčšie ako 0 a dá sa uvažovať o rade dlhšej ako 1. Tento semafor sa používa na počítanie udalostí. V tomto scenári použitia obslužná rutina udalosti „dá“ semafor vždy, keď dôjde k udalosti (zvýši hodnotu počtu semaforov), a úloha obslužnej rutiny „zoberie“ semafor vždy, keď spracuje udalosť (zníži hodnotu počtu semaforov).
Hodnota count je teda rozdiel medzi počtom udalostí, ku ktorým došlo, a počtom, ktoré boli spracované.
Teraz sa pozrime, ako používať Semaphore v našom kóde FreeRTOS.
Ako používať Semafor vo FreeRTOS?
FreeRTOS podporuje rôzne API na vytváranie semaforov, vytváranie semaforov a vytváranie semaforov.
Teraz môžu existovať dva typy API pre rovnaký objekt jadra. Ak musíme dať semafor z ISR, potom nemožno použiť normálne API pre semafor. Mali by ste používať API chránené proti prerušeniu.
V tomto tutoriále budeme používať binárny semafor, pretože je ľahko pochopiteľný a implementovateľný. Pretože sa tu používa funkčnosť prerušenia, musíte vo funkcii ISR použiť API chránené proti prerušeniu. Keď hovoríme o synchronizácii úlohy s prerušením, znamená to uvedenie úlohy do spusteného stavu hneď za ISR.
Vytvorenie semaforu:
Aby sme mohli použiť akýkoľvek objekt jadra, musíme ho najskôr vytvoriť. Na vytvorenie binárneho semaforu použite vSemaphoreCreateBinary ().
Toto API neprijíma žiadny parameter a vracia premennú typu SemaphoreHandle_t. Na uloženie semaforu je vytvorený globálny názov premennej sema_v.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Dať semafor:
Pre uvedenie semaforu existujú dve verzie - jedna pre prerušenie a druhá pre normálnu úlohu.
- xSemaphoreGive (): Toto API má pri vytváraní semaforu iba jeden argument, ktorým je názov premennej semafor ako sema_v, ako je uvedené vyššie. Dá sa to zavolať z akejkoľvek normálnej úlohy, ktorú chcete synchronizovať.
- xSemaphoreGiveFromISR (): Toto je verzia API chránená proti prerušeniu xSemaphoreGive (). Ak potrebujeme synchronizovať ISR a normálnu úlohu, potom by sa z funkcie ISR malo použiť xSemaphoreGiveFromISR ().
Vezmem semafor:
Ak chcete zaujať semafor, použite funkciu API xSemaphoreTake (). Toto API má dva parametre.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Názov semaforu, ktorý sa má prijať v našom prípade sema_v.
xTicksToWait: Toto je maximálna doba, po ktorú bude úloha čakať v zablokovanom stave, kým nebude k dispozícii semafor. V našom projekte nastavíme xTicksToWait na portMAX_DELAY , aby task_1 čakal nekonečne dlho v zablokovanom stave, kým nebude k dispozícii sema_v.
Teraz však poďme použiť tieto API a napíšeme kód na vykonávanie niektorých úloh.
Tu je prepojené jedno tlačidlo a dve LED. Tlačidlo bude slúžiť ako prerušovacie tlačidlo, ktoré je pripojené k pólu 2 Arduino Uno. Po stlačení tohto tlačidla sa vygeneruje prerušenie a rozsvieti sa LED pripojená ku kolíku 8 a po opätovnom stlačení sa vypne.
Takže keď je stlačené tlačidlo, xSemaphoreGiveFromISR () sa bude volať z funkcie ISR a xSemaphoreTake () sa bude volať z funkcie TaskLED.
Aby systém vyzeral ako multitasking, pripojte ďalšie LED diódy k pólu 7, ktorý bude vždy blikať.
Vysvetlenie semaforového kódu
Začnime písať kód otvorením IDE Arduino
1. Najskôr zahrňte súbor hlavičky Arduino_FreeRTOS.h . Teraz, ak sa používa niektorý objekt jadra ako semafor frontu, musí byť preň zahrnutý aj hlavičkový súbor.
#include #include
2. Deklarujte premennú typu SemaphoreHandle_t na uloženie hodnôt semaforu.
SemaphoreHandle_t interruptSemaphore;
3. V programe void setup () vytvorte dve úlohy (TaskLED a TaskBlink) pomocou rozhrania xTaskCreate () API a potom vytvorte semafor pomocou xSemaphoreCreateBinary (). Vytvorte úlohu s rovnakými prioritami a neskôr sa pokúste hrať s týmto číslom. Nakonfigurujte tiež pin 2 ako vstup, povoľte interný pull-up rezistor a pripevnite prerušovací pin. Nakoniec spustite plánovač, ako je uvedené nižšie.
void setup () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); if (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Teraz implementujte funkciu ISR. Vytvorte funkciu a pomenujte ju rovnako ako druhý argument funkcie attachInterrupt () . Aby prerušenie správne fungovalo, musíte odstrániť problém s odrazom tlačidla pomocou funkcie millis alebo mikroskop a nastavením času odblokovania. Z tejto funkcie zavolajte funkciu interruptHandler (), ako je uvedené nižšie.
dlhý debouncing_time = 150; volatile unsigned long last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
Vo funkcii interruptHandler () zavolajte xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Táto funkcia poskytne funkcii TaskLed semafor, aby rozsvietila LED diódu.
5. Vytvorte funkciu TaskLed a vo vnútri while cyklu zavolajte xSemaphoreTake () API a skontrolujte, či je semafor úspešne prijatý alebo nie. Ak sa rovná pdPASS (tj. 1), potom prepnite LED tak, ako je to znázornené nižšie.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, VÝSTUP); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Vytvorte tiež funkciu blikania inej LED pripojenej k pólu 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, VÝSTUP); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Funkcia neplatnej slučky zostane prázdna. Nezabudni na to.
void loop () {}
To je všetko, kompletný kód nájdete na konci tohto tutoriálu. Teraz nahrajte tento kód a pripojte LED a tlačidlo k Arduino UNO podľa schémy zapojenia.
Schéma zapojenia
Po nahraní kódu uvidíte, že LED dióda bliká po 200 ms a po stlačení tlačidla sa okamžite rozsvieti druhá LED dióda, ako je to znázornené na konci videa.
Týmto spôsobom je možné použiť semafory vo FreeRTOS s Arduinom, kde je potrebné bez straty preniesť údaje z jednej úlohy na druhú.
Teraz sa pozrime, čo je Mutex a ako ho používať FreeRTOS.
Čo je Mutex?
Ako je vysvetlené vyššie, semafor je signalizačný mechanizmus, podobne ako Mutex je blokovací mechanizmus na rozdiel od semaforu, ktorý má samostatné funkcie na zvýšenie a zníženie, ale v Mutexe táto funkcia berie a dáva sama o sebe. Je to technika, ako zabrániť poškodeniu zdieľaných zdrojov.
Na ochranu zdieľaného zdroja sa k prostriedku priradí tokenová karta (mutex). Ktokoľvek má túto kartu, môže získať prístup k ďalšiemu zdroju. Ostatní by mali počkať, kým sa karta vráti. Týmto spôsobom má prístup k úlohe iba jeden zdroj a ďalšie čakajú na svoju príležitosť.
Poďme pochopiť Mutex vo FreeRTOS pomocou príkladu.
Tu máme tri úlohy, jednu pre tlač údajov na LCD, druhú pre odoslanie údajov LDR na úlohu LCD a poslednú úlohu pre odoslanie údajov o teplote na LCD. Takže tu zdieľajú dve úlohy rovnaký zdroj, tj LCD. Ak úloha LDR a úloha teploty odosielajú údaje súčasne, jedno z údajov môže byť poškodené alebo stratené.
Aby sme chránili stratu dát, musíme uzamknúť zdroj LCD pre úlohu1, kým nedokončí úlohu zobrazenia. Potom sa úloha LCD odomkne a potom môže úloha2 vykonávať svoju prácu.
Fungovanie Mutexu a semaforov môžete sledovať na nasledujúcom diagrame.
Ako používať Mutex vo FreeRTOS?
Mutexy sa tiež používajú rovnakým spôsobom ako semafory. Najskôr ho vytvorte a potom používajte príslušné API.
Vytvorenie Mutexu:
Na vytvorenie Mutexu použite xSemaphoreCreateMutex () API . Ako naznačuje jeho názov, Mutex je typom binárneho semaforu. Používajú sa v rôznych kontextoch a na rôzne účely. Binárny semafor slúži na synchronizáciu úloh, zatiaľ čo Mutex sa používa na ochranu zdieľaného zdroja.
Toto API neberie žiadny argument a vracia premennú typu SemaphoreHandle_t . Ak nie je možné vytvoriť mutex, funkcia xSemaphoreCreateMutex () vráti hodnotu NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Užívanie Mutexu:
Keď chce úloha získať prístup k prostriedku, použije Mutex pomocou rozhrania xSemaphoreTake () API. Je to rovnaké ako binárny semafor. Trvá to tiež dva parametre.
xSemaphore: Názov Mutexu, ktorý sa má brať v našom prípade mutex_v .
xTicksToWait: Toto je maximálna doba, po ktorú bude úloha čakať v zablokovanom stave, kým bude k dispozícii Mutex. V našom projekte nastavíme xTicksToWait na portMAX_DELAY , aby task_1 čakal nekonečne dlho v blokovanom stave, kým nebude k dispozícii mutex_v .
Dať Mutex:
Po prístupe k zdieľanému prostriedku by mala úloha vrátiť Mutex, aby k nemu mali prístup ďalšie úlohy. xSemaphoreGive () API sa používa na vrátenie Mutexu .
Funkcia xSemaphoreGive () trvá iba jeden argument, ktorým je v našom prípade Mutex, ktorý sa má uviesť mutex_v.
Pomocou vyššie uvedených rozhraní API implementujme Mutex v kóde FreeRTOS pomocou Arduino IDE.
Vysvetlenie kódu Mutex
Tu je cieľom tejto časti použitie sériového monitora ako zdieľaného prostriedku a dve rôzne úlohy na prístup k sériovému monitoru na vytlačenie nejakej správy.
1. Súbory hlavičiek zostanú rovnaké ako semafor.
#include #include
2. Deklarujte premennú typu SemaphoreHandle_t na ukladanie hodnôt Mutexu.
SemaphoreHandle_t mutex_v;
3. V rámci void setup () inicializujte sériový monitor s prenosovou rýchlosťou 9600 a pomocou API xTaskCreate () vytvorte dve úlohy (Task1 a Task2). Potom vytvorte Mutex pomocou xSemaphoreCreateMutex (). Vytvorte úlohu s rovnakými prioritami a neskôr sa pokúste hrať s týmto číslom.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Mutex nie je možné vytvoriť"); } xTaskCreate (Task1, "Task 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "Task 2", 128, NULL, 1, NULL); }
4. Teraz vytvorte funkcie úloh pre úlohy 1 a 2. Vo chvíli slučky funkcie úloh, pred tlačou správa o sériovom monitore musíme vziať mutex pomocou xSemaphoreTake (), potom tlačiť správu a potom sa vrátiť do mutex pomocou xSemaphoreGive (). Potom dajte nejaké oneskorenie.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Ahoj z úlohy1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Podobne implementujte funkciu Task2 so oneskorením 500 ms.
5. Prázdna slučka () zostane prázdna.
Teraz nahrajte tento kód na Arduino UNO a otvorte sériový monitor.
Uvidíte správy, ktoré sa tlačia z úloh1 a úloh2.
Ak chcete vyskúšať fungovanie Mutexu, stačí komentovať xSemaphoreGive (mutex_v); z akejkoľvek úlohy. Vidíte, že program visí na poslednej tlačovej správe .
Takto je možné implementovať Semaphore a Mutex vo FreeRTOS s Arduinom. Viac informácií o Semaphore a Mutexe nájdete v oficiálnej dokumentácii FreeRTOS.
Kompletné kódy a videá pre Semaphore a Mutes sú uvedené nižšie.