- Što je Semafor?
- Kako koristiti Semaphore u FreeRTOS-u?
- Objašnjenje semaforskog koda
- Kružni dijagram
- Što je Mutex?
- Kako koristiti Mutex u FreeRTOS-u?
- Objašnjenje mutex koda
U prethodnim tutorijalima pokrivali smo osnove FreeRTOS-a s Arduinom i objektom jezgre Queue u FreeRTOS Arduino. Sada ćemo u ovom trećem vodiču za FreeRTOS naučiti više o FreeRTOS-u i njegovim naprednim API-jevima, što vam može pomoći da dublje razumijete platformu s više zadataka.
Semaphore i Mutex (međusobno isključivanje) su objekti jezgre koji se koriste za sinkronizaciju, upravljanje resursima i zaštitu resursa od korupcije. U prvoj polovici ovog vodiča vidjet ćemo ideju koja stoji iza Semaphorea, kako i gdje ga koristiti. U drugom poluvremenu nastavit ćemo s Mutexom.
Što je Semafor?
U prethodnim tutorijalima raspravljali smo o prioritetima zadataka, a također smo upoznali da zadatak višeg prioriteta preuzima zadatak nižeg prioriteta, pa tijekom izvršavanja zadatka visokog prioriteta može postojati mogućnost da se u zadatku nižeg prioriteta dogodi oštećenje podataka još nije izvršen i podaci neprestano dolaze na ovaj zadatak sa senzora koji uzrokuje gubitak podataka i neispravnost cijele aplikacije.
Dakle, postoji potreba za zaštitom resursa od gubitka podataka i ovdje Semaphore igra važnu ulogu.
Semafor je signalni mehanizam u kojem zadatak u stanju čekanja signalizira drugi zadatak za izvršenje. Drugim riječima, kada zadatak1 završi svoj posao, tada će prikazati zastavicu ili povećati zastavicu za 1, a zatim će ovu zastavu primiti drugi zadatak (zadatak2) pokazujući da sada može obavljati svoj posao. Kada zadatak2 završi s radom, zastava će se smanjiti za 1.
U osnovi, to je mehanizam "Daj" i "Uzmi", a semafor je cijela varijabla koja se koristi za sinkronizaciju pristupa resursima.
Vrste semafora u FreeRTOS-u:
Semafor je dvije vrste.
- Binarni semafor
- Brojeći Semafor
1. Binarni semafor: Ima dvije cjelobrojne vrijednosti 0 i 1. Nešto je sličan redu duljine 1. Na primjer, imamo dva zadatka, zadatak1 i zadatak2. Zadatak1 šalje podatke na zadatak2, tako da zadatak2 kontinuirano provjerava stavku u redu ako ih ima 1, a zatim može pročitati podatke, inače mora pričekati dok ne postane 1. Nakon preuzimanja podataka, zadatak2 smanjuje red i čini ga 0 To znači da zadatak1 opet može poslati podatke na task2.
Iz gornjeg primjera može se reći da se binarni semafor koristi za sinkronizaciju između zadataka ili između zadataka i prekida.
2. Brojanje semafora: Vrijednosti su veće od 0 i može se smatrati redom duljim od 1. Ovaj semafor koristi se za brojanje događaja. U ovom scenariju upotrebe, rukovatelj događajima će 'dati' semafor svaki put kad se dogodi događaj (povećanje vrijednosti broja semafora), a zadatak rukovatelja će 'uzeti' semafor svaki put kad obradi događaj (smanjujući vrijednost broja semafora).
Vrijednost brojanja je, dakle, razlika između broja događaja koji su se dogodili i broja obrađenih.
Sada, da vidimo kako koristiti Semaphore u našem FreeRTOS kodu.
Kako koristiti Semaphore u FreeRTOS-u?
FreeRTOS podržava različite API-je za stvaranje semafora, uzimanje semafora i davanje semafora.
Sada mogu postojati dvije vrste API-ja za isti objekt jezgre. Ako moramo dati semafor s ISR-a, tada se ne može koristiti uobičajeni API za semafor. Trebali biste koristiti API-je zaštićene prekidima.
U ovom uputstvu koristit ćemo binarni semafor jer ga je lako razumjeti i implementirati. Kako se ovdje koristi funkcionalnost prekida, u ISR funkciji morate koristiti API-je zaštićene prekidima. Kad kažemo sinkronizacija zadatka s prekidom, to znači stavljanje zadatka u stanje Izvođenje odmah nakon ISR-a.
Stvaranje semafora:
Da bismo koristili bilo koji objekt jezgre, prvo ga moramo stvoriti. Za stvaranje binarnog semafora koristite vSemaphoreCreateBinary ().
Ovaj API ne uzima nijedan parametar i vraća varijablu tipa SemaphoreHandle_t. Globalno ime varijable sema_v stvoreno je za pohranu semafora.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Davanje semafora:
Za davanje semafora postoje dvije verzije - jedna za prekid i druga za uobičajeni zadatak.
- xSemaphoreGive (): Ovaj API uzima samo jedan argument koji je naziv varijable semafora poput sema_v kako je gore navedeno tijekom stvaranja semafora. Može se pozvati iz bilo kojeg uobičajenog zadatka koji želite sinkronizirati.
- xSemaphoreGiveFromISR (): Ovo je zaštićena API verzija xSemaphoreGive () od prekida. Kada trebamo sinkronizirati ISR i uobičajeni zadatak, tada se iz funkcije ISR treba koristiti xSemaphoreGiveFromISR ().
Uzimanje semafora:
Da biste uzeli semafor, upotrijebite API funkciju xSemaphoreTake (). Ovaj API uzima dva parametra.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Naziv semafora koji se uzima u našem slučaju sema_v.
xTicksToWait: Ovo je maksimalno vrijeme u kojem će zadatak čekati u Blokiranom stanju da semafor postane dostupan. U našem projektu postavit ćemo xTicksToWait na portMAX_DELAY kako bi zadatak_1 neograničeno čekao u Blokiranom stanju dok sema_v ne postane dostupan.
Sada, upotrijebimo ove API-je i napišite kod za obavljanje nekih zadataka.
Ovdje su povezani jedan gumb i dvije LED diode. Gumb će djelovati kao prekidni gumb koji je pričvršćen na pin 2 Arduino Uno. Kada se ovaj gumb pritisne, generirat će se prekid i LED koja je spojena na pin 8 UKLJUČIT će se, a kad ga pritisnete ponovno, ISKLJUČIT će se.
Dakle, kada se pritisne tipka, xSemaphoreGiveFromISR () bit će pozvano iz funkcije ISR, a funkcija xSemaphoreTake () iz funkcije TaskLED.
Da bi sustav izgledao višezadaćno, spojite druge LED diode pomoću pina 7 koji će uvijek treptati.
Objašnjenje semaforskog koda
Počnimo s pisanjem koda za otvaranje Arduino IDE-a
1. Prvo uključite datoteku zaglavlja Arduino_FreeRTOS.h . Sada, ako se koristi bilo koji objekt jezgre poput semafora u redu, tada za njega mora biti uključena i datoteka zaglavlja.
#include #include
2. Proglasite varijablu tipa SemaphoreHandle_t za pohranu vrijednosti semafora.
SemaphoreHandle_t interruptSemaphore;
3. U void setup (), stvorite dva zadatka (TaskLED i TaskBlink) pomoću xTaskCreate () API, a zatim stvorite semafor pomoću xSemaphoreCreateBinary (). Stvorite zadatak s jednakim prioritetima i kasnije pokušajte igrati s ovim brojem. Također, konfigurirajte pin 2 kao ulaz i omogućite unutarnji otpor za izvlačenje i pričvrstite prekidni pin. Napokon, pokrenite program za planiranje kao što je prikazano dolje.
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. Sada implementirajte ISR funkciju. Napravite funkciju i nazovite je isto kao i drugi argument funkcije attachInterrupt () . Da bi prekid ispravno radio, morate ukloniti problem otkazivanja tipke pomoću funkcije milis ili mikro i podešavanjem vremena opoziva. Iz ove funkcije pozovite funkciju interruptHandler () kako je prikazano dolje.
dugo debouncing_time = 150; hlapljivi nepotpisani long last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = mikro (); } }
U funkciji interruptHandler () pozovite xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Ova će funkcija TasmaLedu dati semafor da UKLJUČI LED.
5. Stvorite TaskLed funkciju i unutar while petlje pozovite xSemaphoreTake () API i provjerite je li semafor uspješno uzet ili nije. Ako je jednako pdPASS (tj. 1), učinite da se LED prebaci kako je prikazano dolje.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, IZLAZ); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Također, stvorite funkciju treptanja druge LED diode spojene na pin 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, IZLAZ); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Funkcija void loop će ostati prazna. Ne zaboravi.
petlja void () {}
To je to, kompletni kod možete pronaći na kraju ovog vodiča. Sada prenesite ovaj kod i spojite LED i tipku s Arduino UNO prema shemi spojeva.
Kružni dijagram

Nakon prijenosa koda, vidjet ćete kako LED lampica trepće nakon 200 ms, a kada se pritisne tipka, odmah će zasvijetliti druga LED dioda kao što je prikazano u videu na kraju.

Na taj se način semafori mogu koristiti u FreeRTOS-u s Arduinom gdje trebaju prenositi podatke s jednog zadatka na drugi bez ikakvih gubitaka.
Sada, da vidimo što je Mutex i kako ga koristiti FreeRTOS.
Što je Mutex?
Kao što je gore objašnjeno, semafor je signalni mehanizam, slično tome, Mutex je mehanizam zaključavanja za razliku od semafora koji ima odvojene funkcije za povećanje i smanjenje, ali u Mutexu funkcija uzima i daje u sebi. To je tehnika za izbjegavanje korupcije zajedničkih resursa.
Da bi se zaštitio zajednički resurs, resursu se dodjeljuje token kartica (mutex). Tko ima ovu karticu, može pristupiti drugom resursu. Ostali bi trebali pričekati dok se kartica ne vrati. Na taj način samo jedan resurs može pristupiti zadatku, a drugi čekaju svoju priliku.
Razumijemo Mutex u FreeRTOS- u uz pomoć primjera.
Ovdje imamo tri zadatka, jedan za ispis podataka na LCD, drugi za slanje LDR podataka na LCD zadatak i zadnji zadatak za slanje podataka o temperaturi na LCD. Dakle, ovdje dva zadatka dijele isti resurs, tj. LCD. Ako zadatak LDR-a i zadatak temperature pošalju podatke istovremeno, jedan od podataka može biti oštećen ili izgubljen.
Dakle, da bismo zaštitili gubitak podataka, moramo zaključati LCD resurs za task1 dok ne završi zadatak prikaza. Tada će se LCD zadatak otključati i tada task2 može obaviti svoj posao.
Na slijedećem dijagramu možete promatrati rad Mutexa i semafora.

Kako koristiti Mutex u FreeRTOS-u?
Muteksi se također koriste na isti način kao i semafori. Prvo ga stvorite, a zatim dajte i uzmite koristeći odgovarajuće API-je.
Stvaranje mutexa:
Da biste stvorili Mutex, upotrijebite API xSemaphoreCreateMutex () . Kao što mu samo ime govori da je Mutex vrsta Binarnog semafora. Koriste se u različitim kontekstima i svrhama. Binarni semafor služi za sinkronizaciju zadataka, dok se Mutex koristi za zaštitu zajedničkog resursa.
Ovaj API ne uzima nijedan argument i vraća varijablu tipa SemaphoreHandle_t . Ako se mutex ne može stvoriti, xSemaphoreCreateMutex () vraća NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Uzimanje mutexa:
Kada zadatak želi pristupiti resursu, trebat će mutex koristeći xSemaphoreTake () API. To je isto kao binarni semafor. Potrebna su i dva parametra.
xSemaphore: Naziv Mutexa koji se uzima u našem slučaju mutex_v .
xTicksToWait: Ovo je maksimalno vrijeme u kojem će zadatak čekati u Blokiranom stanju da Mutex postane dostupan. U našem projektu postavit ćemo xTicksToWait na portMAX_DELAY kako bi zadatak_1 neograničeno čekao u Blokiranom stanju dok mutex_v ne postane dostupan.
Davanje mutexa:
Nakon pristupa zajedničkom resursu, zadatak bi trebao vratiti Mutex kako bi mu mogli pristupiti i drugi zadaci. API xSemaphoreGive () koristi se za vraćanje Mutexa .
Funkcija xSemaphoreGive () uzima samo jedan argument koji je Mutex koji se u našem slučaju daje mutex_v.
Koristeći gornje API-je, implementiramo Mutex u FreeRTOS kod pomoću Arduino IDE-a.
Objašnjenje mutex koda
Ovdje je cilj ovog dijela korištenje serijskog monitora kao zajedničkog resursa i dva različita zadatka za pristup serijskom monitoru za ispis neke poruke.
1. Datoteke zaglavlja ostat će iste kao semafor.
#include #include
2. Proglasite varijablu tipa SemaphoreHandle_t za pohranu vrijednosti Mutexa.
SemaphoreHandle_t mutex_v;
3. U void setup (), inicijalizirajte serijski monitor brzinom od 9600 baud i stvorite dva zadatka (Task1 i Task2) pomoću xTaskCreate () API. Zatim stvorite Mutex pomoću xSemaphoreCreateMutex (). Stvorite zadatak s jednakim prioritetima, a kasnije se pokušajte igrati s tim brojem.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Mutex nije moguće stvoriti"); } xTaskCreate (Zadatak1, "Zadatak 1", 128, NULL, 1, NULL); xTaskCreate (Zadatak2, "Zadatak 2", 128, NULL, 1, NULL); }
4. Sada napravite funkcije zadataka za Task1 i Task2. U while petlji funkcije zadatak, prije ispisa poruke o serijskom monitoru moramo uzeti Mutex pomoću xSemaphoreTake () zatim ispisati poruku i zatim vratiti Mutex pomoću xSemaphoreGive (). Onda dajte malo odgode.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Bok iz zadatka1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Slično tome, implementirajte funkciju Task2 s kašnjenjem od 500 ms.
5. Void petlja () ostat će prazna.
Sada prenesite ovaj kod na Arduino UNO i otvorite serijski monitor.
Vidjet ćete da se poruke ispisuju iz task1 i task2.

Da biste testirali rad Mutexa , samo komentirajte xSemaphoreGive (mutex_v); iz bilo kojeg zadatka. Možete vidjeti da program visi na zadnjoj ispisnoj poruci .

Tako se Semaphore i Mutex mogu implementirati u FreeRTOS s Arduinom. Za više informacija o Semaforu i Muteksu možete posjetiti službenu dokumentaciju FreeRTOS-a.
Kompletni kodovi i video za Semaphore i Mutes dati su u nastavku.
