- Kaj je semafor?
- Kako uporabljati Semaphore v FreeRTOS?
- Pojasnilo kode semaforja
- Shema vezja
- Kaj je Mutex?
- Kako uporabljati Mutex v FreeRTOS?
- Pojasnilo kode Mutex
V prejšnjih vajah smo obravnavali osnove FreeRTOS z Arduino in objektom jedra Queue v FreeRTOS Arduino. Zdaj bomo v tej tretji vadnici za FreeRTOS izvedeli več o FreeRTOS in njenih vmesnikih API, s katerimi boste lahko poglobljeno razumeli večopravilno platformo.
Semaphore in Mutex (vzajemna izključitev) sta predmeta jedra, ki se uporabljata za sinhronizacijo, upravljanje virov in zaščito virov pred poškodbami. V prvi polovici te vadnice bomo videli idejo za Semaphore, kako in kje ga uporabiti. V drugem polčasu bomo nadaljevali z Mutexom.
Kaj je semafor?
V prejšnjih vajah smo razpravljali o prednostnih nalogah in spoznali tudi, da naloga z večjo prednostjo prevzame nalogo z nižjo prioriteto, zato lahko med izvajanjem naloge z visoko prioriteto pride do poškodbe podatkov pri nalogi z nižjo prioriteto še ni izveden in podatki neprekinjeno prihajajo v to nalogo s senzorja, ki povzroči izgubo podatkov in nepravilno delovanje celotne aplikacije.
Torej je treba vire zaščititi pred izgubo podatkov in tu ima Semaphore pomembno vlogo.
Semafor je signalni mehanizem, pri katerem opravilo v čakalnem stanju za izvedbo signalizira druga naloga. Z drugimi besedami, ko naloga1 konča svoje delo, bo prikazala zastavico ali jo povečala za 1, nato pa jo bo sprejela druga naloga (naloga2), ki kaže, da lahko svoje delo opravi zdaj. Ko naloga2 opravi svoje delo, se zastavica zmanjša za 1.
V bistvu gre za mehanizem »Daj« in »Vzemi«, semafor pa je celoštevilčna spremenljivka, ki se uporablja za sinhronizacijo dostopa do virov.
Vrste semaforjev v FreeRTOS:
Semafor je dveh vrst.
- Binarni semafor
- Štetje semaforja
1. Binarni semafor: ima dve celoštevilčni vrednosti 0 in 1. Nekoliko je podoben čakalni vrsti dolžine 1. Na primer imamo dve nalogi, task1 in task2. Task1 pošlje podatke v task2, zato task2 neprekinjeno preverja element v čakalni vrsti, če je 1, nato pa lahko prebere podatke, sicer mora počakati, da postane 1. Po prevzemu podatkov task2 zmanjša čakalno vrsto in jo postavi na 0 To pomeni, da task1 znova lahko pošlje podatke na task2.
Iz zgornjega primera lahko rečemo, da se binarni semafor uporablja za sinhronizacijo med nalogami ali med nalogami in prekinitev.
2. Štetje semaforja: Vrednosti so večje od 0 in se lahko štejejo za čakalno vrsto, daljšo od 1. Ta semafor se uporablja za štetje dogodkov. V tem scenariju uporabe bo obdelovalec dogodkov "dal" semafor vsakič, ko se dogodek zgodi (povečanje vrednosti števila semaforjev), naloga skrbnika pa bo "vzel" semafor vsakič, ko obdela dogodek (zmanjšanje vrednosti števila semaforjev).
Vrednost štetja je torej razlika med številom dogodkov in številom obdelanih.
Zdaj pa poglejmo, kako uporabljati Semaphore v naši kodi FreeRTOS.
Kako uporabljati Semaphore v FreeRTOS?
FreeRTOS podpira različne API-je za ustvarjanje semaforja, zajemanje semaforja in dajanje semaforja.
Zdaj lahko obstajata dve vrsti API-jev za isti objekt jedra. Če moramo dati semafor iz ISR, potem običajnega API-ja za semafor ni mogoče uporabiti. Uporabiti morate prekinjene API-je.
V tej vadnici bomo uporabili binarni semafor, ker je enostaven za razumevanje in izvajanje. Ker se tukaj uporablja funkcionalnost prekinitev, morate v funkciji ISR uporabiti zaščitene API-je, ki so zaščiteni pred prekinitvami. Ko govorimo o sinhronizaciji opravila s prekinitvijo, to pomeni, da jo takoj po ISR postavimo v stanje Running.
Ustvarjanje semaforja:
Če želite uporabiti kateri koli objekt jedra, ga moramo najprej ustvariti. Za ustvarjanje binarnega semaforja uporabite vSemaphoreCreateBinary ().
Ta API ne sprejme nobenega parametra in vrne spremenljivko tipa SemaphoreHandle_t. Za shranjevanje semaforja je ustvarjeno globalno ime spremenljivke sema_v.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Dajanje semaforja:
Za podajanje semaforja obstajata dve različici - ena za prekinitev in druga za običajno nalogo.
- xSemaphoreGive (): Ta API pri ustvarjanju semaforja sprejme samo en argument, ki je ime spremenljivke semaforja, kot je sema_v, kot je navedeno zgoraj. Pokličete ga lahko iz katere koli običajne naloge, ki jo želite sinhronizirati.
- xSemaphoreGiveFromISR (): To je zaščitena pred prekinitvami različica API-ja xSemaphoreGive (). Ko moramo sinhronizirati ISR in običajno nalogo, je treba iz funkcije ISR uporabiti xSemaphoreGiveFromISR ().
Jemanje semaforja:
Če želite vzeti semafor, uporabite funkcijo API xSemaphoreTake (). Ta API ima dva parametra.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Ime semaforja, ki ga bomo vzeli v našem primeru sema_v.
xTicksToWait: To je najdaljši čas, ko bo naloga v stanju Blokirana čakala, da bo semafor na voljo. V našem projektu bomo xTicksToWait nastavili na portMAX_DELAY, da bo task_1 neomejeno čakal v blokiranem stanju, dokler ni na voljo sema_v.
Zdaj pa uporabimo te API-je in napišite kodo za izvajanje nekaterih nalog.
Tu sta povezani en gumb in dve LED. Gumb bo deloval kot prekinitveni gumb, ki je pritrjen na zatič 2 Arduino Uno. Ko pritisnete ta gumb, se ustvari prekinitev in LED, ki je priključen na zatič 8, se vklopi in ob ponovnem pritisku ugasne.
Torej, ko pritisnete gumb, bo funkcija xSemaphoreGiveFromISR () poklicana iz funkcije ISR, funkcija xSemaphoreTake () pa iz funkcije TaskLED.
Če želite, da sistem deluje večopravilno, povežite druge diode z zatičem 7, ki bo vedno utripal.
Pojasnilo kode semaforja
Začnimo pisati kodo z odpiranjem Arduino IDE
1. Najprej vključite datoteko glave Arduino_FreeRTOS.h . Zdaj, če se uporablja katerikoli objekt jedra, kot je semafor čakalne vrste, mora biti zanj vključena tudi datoteka glave.
#include #include
2. Prijavite spremenljivko tipa SemaphoreHandle_t za shranjevanje vrednosti semaforja.
SemaphoreHandle_t interruptSemaphore;
3. V void setup () ustvarite dve nalogi (TaskLED in TaskBlink) z API-jem xTaskCreate () in nato z xSemaphoreCreateBinary () ustvarite semafor. Ustvarite nalogo z enakimi prednostmi in kasneje poskusite igrati s to številko. Konfigurirajte tudi zatič 2 kot vhod in omogočite notranji vlečni upor ter pritrdite prekinitveni zatič. Na koncu zaženite načrtovalnik, kot je prikazano spodaj.
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. Zdaj izvedite funkcijo ISR. Naredite funkcijo in jo poimenujte enako kot drugi argument funkcije attachInterrupt () . Če želite, da prekinitev deluje pravilno, morate odstraniti težavo z izklopom gumba s pomočjo funkcije milis ali mikro in s prilagoditvijo časa odklopa. Iz te funkcije pokličite funkcijo interruptHandler (), kot je prikazano spodaj.
dolgo debouncing_time = 150; volatile unsigned long last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = mikro (); } }
V funkciji interruptHandler () pokličite API xSemaphoreGiveFromISR () .
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Ta funkcija bo dala TasmaLedu semafor, da vklopi LED.
5. Ustvarite TaskLed funkcijo in v notranjosti , medtem ko zanke, pokličite xSemaphoreTake () API in preverite, če je semafor, ki je uspešno izvedel ali ne. Če je enak pdPASS (tj. 1), potem preklopite LED, kot je prikazano spodaj.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, IZHOD); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Ustvarite tudi funkcijo utripanja drugih LED, priključenih na zatič 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, IZHOD); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Funkcija void loop bo ostala prazna. Ne pozabite.
void loop () {}
To je to, popolno kodo najdete na koncu te vadnice. Zdaj naložite to kodo in v skladu z vezjem povežite LED in tipko z Arduino UNO.
Shema vezja
Po nalaganju kode boste videli, da po 200 ms utripa LED in ko pritisnete gumb, bo takoj zasvetila druga LED, kot je prikazano v videu na koncu.
Na ta način se lahko semafori uporabljajo v FreeRTOS z Arduino, kjer mora podatke brez napak prenesti iz ene naloge v drugo.
Zdaj pa poglejmo, kaj je Mutex in kako ga uporabljati FreeRTOS.
Kaj je Mutex?
Kot je razloženo zgoraj, je semafor signalni mehanizem, podobno je Mutex mehanizem za zaklepanje, za razliko od semaforja, ki ima ločene funkcije za priraščanje in zmanjševanje, v Mutexu pa funkcija sprejme in odda v sebi. To je tehnika, da se izognemo korupciji skupnih virov.
Če želite zaščititi vir v skupni rabi, viru dodelite kartico z žetoni (mutex). Kdor ima to kartico, lahko dostopa do drugega vira. Drugi naj počakajo, da se kartica vrne. Na ta način lahko samo en vir dostopa do naloge, drugi pa čakajo na svojo priložnost.
Razumimo Mutex v FreeRTOS s pomočjo primera.
Tu imamo tri naloge, eno za tiskanje podatkov na LCD, drugo za pošiljanje podatkov LDR na LCD in zadnjo za pošiljanje temperaturnih podatkov na LCD. Torej tukaj si dve nalogi delita isti vir, tj. LCD. Če naloga LDR in naloga temperature pošljeta podatke istočasno, je eden od podatkov morda poškodovan ali izgubljen.
Torej, da zaščitimo izgubo podatkov, moramo zapreti vir LCD za task1, dokler ne zaključi opravila zaslona. Nato se naloga LCD odklene in nato task2 lahko opravi svoje delo.
Delovanje Mutexa in semaforjev lahko opazujete na spodnjem diagramu.
Kako uporabljati Mutex v FreeRTOS?
Muteksi se uporabljajo tudi na enak način kot semaforji. Najprej ga ustvarite, nato podajte in vzemite z uporabo ustreznih API-jev.
Ustvarjanje mutexa:
Če želite ustvariti Mutex, uporabite API xSemaphoreCreateMutex () . Kot že ime pove, je Mutex vrsta binarnega semaforja. Uporabljajo se v različnih kontekstih in namenih. Binarni semafor je namenjen sinhronizaciji nalog, medtem ko se Mutex uporablja za zaščito virov v skupni rabi.
Ta API ne sprejme nobenega argumenta in vrne spremenljivko tipa SemaphoreHandle_t . Če mutexa ni mogoče ustvariti, xSemaphoreCreateMutex () vrne NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Jemanje mutexa:
Ko naloga želi dostopati do vira, bo z uporabo API-ja xSemaphoreTake () vzel Mutex . Enako je kot binarni semafor. Potrebna sta tudi dva parametra.
xSemaphore: Ime Mutexa, ki ga bomo v našem primeru uporabili mutex_v .
xTicksToWait: To je najdaljši čas, ko bo naloga čakala v blokiranem stanju, da bo Mutex na voljo. V našem projektu bomo xTicksToWait nastavili na portMAX_DELAY, da bo task_1 neomejeno čakal v blokiranem stanju, dokler mutex_v ne bo na voljo.
Dajanje mutexa:
Po dostopu do vira v skupni rabi mora naloga vrniti Mutex, tako da lahko druge naloge dostopajo do njega. API xSemaphoreGive () se uporablja za vrnitev Mutexa nazaj.
Funkcija xSemaphoreGive () sprejme samo en argument, ki je Mutex, ki ga je treba podati v našem primeru mutex_v.
Z uporabo zgornjih API-jev implementiramo Mutex v kodo FreeRTOS z uporabo Arduino IDE.
Pojasnilo kode Mutex
Tu je cilj tega dela uporaba serijskega monitorja kot skupnega vira in dve različni nalogi za dostop do serijskega monitorja za tiskanje sporočila.
1. Datoteke z glavo ostanejo enake kot semafor.
#include #include
2. Razglasite spremenljivko tipa SemaphoreHandle_t za shranjevanje vrednosti Mutexa.
SemaphoreHandle_t mutex_v;
3. V void setup () inicializirajte serijski monitor s hitrostjo prenosa 9600 in ustvarite dve nalogi (Task1 in Task2) z API-jem xTaskCreate () . Nato ustvarite Mutex z uporabo xSemaphoreCreateMutex (). Ustvarite nalogo z enakimi prioritetami in se kasneje poskusite igrati s to številko.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Mutexa ni mogoče ustvariti"); } xTaskCreate (Task1, "Task 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "Task 2", 128, NULL, 1, NULL); }
4. Zdaj naredite funkcije opravil za Task1 in Task2. V while zanko funkcije opravil pred tiskanjem sporočilo o serijski monitor moramo vzeti mutex uporabo xSemaphoreTake () nato natisnete sporočilo in nato vrne mutex uporabo xSemaphoreGive (). Nato dajte nekaj zamude.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Hi from Task1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Podobno izvedite funkcijo Task2 z zamikom 500 ms.
5. Void zanka () ostane prazna.
Zdaj naložite to kodo na Arduino UNO in odprite serijski monitor.
Videli boste, da se sporočila tiskajo iz task1 in task2.
Če želite preizkusiti delovanje Mutexa, samo komentirajte xSemaphoreGive (mutex_v); iz katere koli naloge. Vidite, da program visi na zadnjem tiskanem sporočilu .
Tako lahko Semaphore in Mutex implementiramo v FreeRTOS z Arduino. Za več informacij o Semaphore in Mutex lahko obiščete uradno dokumentacijo FreeRTOS.
Popolne kode in video za Semaphore in Mutes so navedeni spodaj.