- Hvad er Semaphore?
- Hvordan bruges Semaphore i FreeRTOS?
- Semaphore Code Forklaring
- Kredsløbsdiagram
- Hvad er Mutex?
- Hvordan bruges Mutex i FreeRTOS?
- Mutex-kode Forklaring
I tidligere tutorials har vi dækket det grundlæggende i FreeRTOS med Arduino og kø-kerneobjektet i FreeRTOS Arduino. Nu, i denne tredje FreeRTOS-tutorial, lærer vi mere om FreeRTOS og dens forhånds-API'er, som kan få dig til at forstå multi-tasking-platformen dybere.
Semaphore og Mutex (gensidig udelukkelse) er de kerneobjekter, der bruges til synkronisering, ressourcehåndtering og beskyttelse af ressourcer mod korruption. I første halvdel af denne tutorial vil vi se ideen bag Semaphore, hvordan og hvor den skal bruges. I anden halvdel fortsætter vi med Mutex.
Hvad er Semaphore?
I tidligere tutorials har vi diskuteret om opgaveprioriteter og lærer også at vide, at en opgave med højere prioritet forkaster en opgave med lavere prioritet, så mens udførelse af opgave med høj prioritet kan der være en mulighed for, at datakorruption kan ske i en opgave med lavere prioritet, fordi den udføres endnu ikke, og data kommer løbende til denne opgave fra en sensor, der forårsager datatab og funktionsfejl i hele applikationen.
Så der er behov for at beskytte ressourcer mod datatab, og her spiller Semaphore en vigtig rolle.
Semaphore er en signalmekanisme, hvor en opgave i ventetilstand signaliseres af en anden opgave til udførelse. Med andre ord, når en opgave1 er færdig med sit arbejde, så viser den et flag eller forøger et flag med 1, og så modtages dette flag af en anden opgave (opgave2), der viser, at det kan udføre sit arbejde nu. Når task2 er færdig med sit arbejde, reduceres flagget med 1.
Så grundlæggende er det en "Give" og "Take" -mekanisme, og semafor er en heltalsvariabel, der bruges til at synkronisere adgangen til ressourcer.
Typer af semafor i FreeRTOS:
Semafor er af to typer.
- Binær semafor
- Tæller Semaphore
1. Binær semafor: Det har to heltal værdier 0 og 1. Det svarer noget til køen af længde 1. For eksempel har vi to opgaver, opgave1 og opgave2. Opgave1 sender data til opgave2, så opgave2 kontrollerer løbende køelementet, hvis der er 1, så kan den læse dataene, den skal vente, indtil den bliver 1. Efter at have taget dataene, reduceres opgave2 køen og gør den til 0 Det betyder opgave1 igen kan sende dataene til task2.
Fra ovenstående eksempel kan det siges, at binær semafor bruges til synkronisering mellem opgaver eller mellem opgaver og afbrydelse.
2. Optælling af semafor: Den har værdier større end 0 og kan tænkes at køen skal være mere end 1. Denne semafor bruges til at tælle begivenheder. I dette brugsscenarie vil en begivenhedshåndterer 'give' en semafor hver gang en begivenhed opstår (stigning af semafortællingsværdien), og en håndteringsopgave 'tager' en semafor hver gang den behandler en begivenhed (formindskelse af semafortællingsværdien).
Tællingsværdien er derfor forskellen mellem antallet af begivenheder, der har fundet sted, og det antal, der er blevet behandlet.
Lad os nu se, hvordan du bruger Semaphore i vores FreeRTOS-kode.
Hvordan bruges Semaphore i FreeRTOS?
FreeRTOS understøtter forskellige API'er til at oprette en semafor, tage en semafor og give en semafor.
Nu kan der være to typer API'er til det samme kerneobjekt. Hvis vi skal give semafor fra en ISR, kan normal semafor API ikke bruges. Du skal bruge afbrudt beskyttede API'er.
I denne vejledning bruger vi binær semafor, fordi det er let at forstå og implementere. Da afbrydelsesfunktionalitet bruges her, skal du bruge afbrydelsesbeskyttede API'er i ISR-funktionen. Når vi siger at synkronisere en opgave med en afbrydelse, betyder det at sætte opgaven i kørende tilstand lige efter ISR.
Oprettelse af en semafor:
For at bruge ethvert kerneobjekt skal vi først oprette det. Brug vSemaphoreCreateBinary () til at oprette en binær semafor .
Denne API tager ikke nogen parameter og returnerer en variabel af typen SemaphoreHandle_t. Et globalt variabelnavn sema_v oprettes for at gemme semaforen.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
At give en semafor:
For at give en semafor er der to versioner - en til afbrydelse og en anden til den normale opgave.
- xSemaphoreGive (): Denne API tager kun et argument, som er variablenavnet for semafor som sema_v som angivet ovenfor, mens du opretter en semafor. Det kan kaldes fra enhver normal opgave, som du vil synkronisere.
- xSemaphoreGiveFromISR (): Dette er den afbrydelsesbeskyttede API-version af xSemaphoreGive (). Når vi har brug for at synkronisere en ISR og en normal opgave, skal xSemaphoreGiveFromISR () bruges fra ISR-funktionen.
At tage en semafor:
For at tage en semafor skal du bruge API-funktionen xSemaphoreTake (). Denne API tager to parametre.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Navnet på semaforen, der skal tages i vores tilfælde sema_v.
xTicksToWait: Dette er den maksimale tid, som opgaven venter i blokeret tilstand for, at semaforen bliver tilgængelig. I vores projekt indstiller vi xTicksToWait til portMAX_DELAY for at få task_1 til at vente på ubestemt tid i blokeret tilstand, indtil sema_v er tilgængelig.
Lad os nu bruge disse API'er og skrive en kode til at udføre nogle opgaver.
Her er en trykknap og to lysdioder grænseflader. Trykknappen fungerer som en afbryderknap, der er fastgjort til pin 2 i Arduino Uno. Når der trykkes på denne knap, genereres en afbrydelse, og en LED, der er tilsluttet pin 8, tændes, og når du trykker på den igen, vil den være OFF.
Så når der trykkes på knappen, kaldes xSemaphoreGiveFromISR () fra ISR-funktionen, og xSemaphoreTake () -funktionen kaldes fra TaskLED-funktionen.
For at få systemet til at se multitasking ud, skal du forbinde andre lysdioder med pin 7, som altid vil blinke.
Semaphore Code Forklaring
Lad os begynde at skrive kode til ved at åbne Arduino IDE
1. Inkluder først Arduino_FreeRTOS.h headerfilen. Nu, hvis der anvendes et kerneobjekt som kø-semafor, skal der også inkluderes en headerfil til det.
# inkluderer # inkluderer
2. Erklær en variabel af typen SemaphoreHandle_t for at gemme semaforens værdier.
SemaphoreHandle_t interruptSemaphore;
3. I ugyldig opsætning () skal du oprette to opgaver (TaskLED og TaskBlink) ved hjælp af xTaskCreate () API og derefter oprette en semafor ved hjælp af xSemaphoreCreateBinary (). Opret en opgave med lige prioriteter og prøv senere at lege med dette nummer. Konfigurer også pin 2 som en indgang, og aktiver den interne pull-up-modstand, og fastgør interrupt pin. Endelig skal du starte planlæggeren som vist nedenfor.
ugyldig opsætning () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); hvis (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Implementer nu ISR-funktionen. Lav en funktion, og navngiv den som den anden argumentation for attachInterrupt () -funktionen. For at få afbrydelsen til at fungere korrekt, skal du fjerne afvisningsproblemet for trykknappen ved hjælp af millis- eller mikrofunktionen og ved at justere afvisningstiden. Fra denne funktion skal du ringe til interruptHandler () -funktionen som vist nedenfor.
lang debouncing_time = 150; flygtige usignerede længe sidste_mikroer; ugyldigt debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
I funktionen interruptHandler () skal du ringe til xSemaphoreGiveFromISR () API.
ugyldig interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Denne funktion giver TaskLed en semafor for at tænde lysdioden.
5. Opret en TaskLed- funktion, og inden i while- sløjfen skal du ringe til xSemaphoreTake () API og kontrollere, om semaforen er taget med succes eller ej. Hvis det er lig med pdPASS (dvs. 1), skal du skifte LED som vist nedenfor.
ugyldig TaskLed (ugyldig * pvParameters) { (ugyldig) pvParameters; pinMode (8, OUTPUT); mens (1) { hvis (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Opret også en funktion til at blinke anden LED tilsluttet pin 7.
ugyldig TaskLed1 (ugyldig * pvParameters) { (ugyldig) pvParameters; pinMode (7, OUTPUT); mens (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LAV); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Funktionen med ugyldig sløjfe forbliver tom. Glem det ikke.
ugyldig sløjfe () {}
Det er det, komplet kode kan findes i slutningen af denne vejledning. Upload nu denne kode og tilslut lysdioderne og trykknappen med Arduino UNO i henhold til kredsløbsdiagrammet.
Kredsløbsdiagram
Efter uploaden af koden vil du se, at en LED blinker efter 200 ms, og når der trykkes på knappen, vil den anden LED straks lyse som vist i videoen til sidst.
På denne måde kan semaforer bruges i FreeRTOS med Arduino, hvor det er nødvendigt at videregive dataene fra en opgave til en anden uden tab.
Lad os nu se, hvad der er Mutex, og hvordan man bruger det FreeRTOS.
Hvad er Mutex?
Som forklaret ovenfor er semafor en signalmekanisme, ligesom Mutex er en låsemekanisme i modsætning til semaforen, der har separate funktioner til stigning og reduktion, men i Mutex tager funktionen og giver i sig selv. Det er en teknik til at undgå korruption af delte ressourcer.
For at beskytte den delte ressource tildeles man et token-kort (mutex) til ressourcen. Den, der har dette kort, kan få adgang til den anden ressource. Andre bør vente, indtil kortet returneres. På denne måde kan kun én ressource få adgang til opgaven, og andre venter på deres chance.
Lad os forstå Mutex i FreeRTOS ved hjælp af et eksempel.
Her har vi tre opgaver, en til udskrivning af data på LCD, en anden til at sende LDR-data til LCD-opgave og sidste opgave til at sende temperaturdata på LCD. Så her deler to opgaver den samme ressource, dvs. LCD. Hvis LDR-opgaven og temperaturopgaven sender data samtidigt, kan en af dataene blive beskadiget eller gå tabt.
Så for at beskytte datatabet skal vi låse LCD-ressourcen til opgave1, indtil den er færdig med skærmopgaven. Derefter låses LCD-opgaven op, og derefter kan task2 udføre sit arbejde.
Du kan se funktionen af Mutex og semaforer i nedenstående diagram.
Hvordan bruges Mutex i FreeRTOS?
Mutexs bruges også på samme måde som semaforer. Først skal du oprette det, derefter give og tage ved hjælp af respektive API'er.
Oprettelse af en Mutex:
For at oprette en Mutex skal du bruge xSemaphoreCreateMutex () API . Som navnet antyder, er Mutex en type binær semafor. De bruges i forskellige sammenhænge og formål. En binær semafor er til synkronisering af opgaver, mens Mutex bruges til at beskytte en delt ressource.
Denne API tager ikke noget argument og returnerer en variabel af typen SemaphoreHandle_t . Hvis mutex ikke kan oprettes, returnerer xSemaphoreCreateMutex () NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
At tage en Mutex:
Når en opgave ønsker at få adgang til en ressource, tager den en Mutex ved hjælp af xSemaphoreTake () API. Det er det samme som en binær semafor. Det tager også to parametre.
xSemaphore: Navnet på Mutex, der skal tages i vores tilfælde mutex_v .
xTicksToWait: Dette er den maksimale tid, som opgaven venter i blokeret tilstand, indtil Mutex bliver tilgængelig. I vores projekt indstiller vi xTicksToWait til portMAX_DELAY for at få task_1 til at vente på ubestemt tid i Blokeret tilstand, indtil mutex_v er tilgængelig.
At give en Mutex:
Efter adgang til den delte ressource skal opgaven returnere Mutex, så andre opgaver kan få adgang til den. xSemaphoreGive () API bruges til at give Mutex tilbage.
Funktionen xSemaphoreGive () tager kun et argument, som er Mutex, der skal gives i vores tilfælde mutex_v.
Brug ovenstående API'er, lad os implementere Mutex i FreeRTOS-koden ved hjælp af Arduino IDE.
Mutex-kode Forklaring
Her er målet for denne del at bruge en seriel skærm som en delt ressource og to forskellige opgaver for at få adgang til den serielle skærm for at udskrive en besked.
1. Overskriftsfilerne forbliver de samme som en semafor.
# inkluderer # inkluderer
2. Erklær en variabel af typen SemaphoreHandle_t for at gemme værdierne for Mutex.
SemaphoreHandle_t mutex_v;
3. I ugyldig opsætning () skal du initialisere seriel skærm med 9600 baudrate og oprette to opgaver (Task1 og Task2) ved hjælp af xTaskCreate () API. Opret derefter en Mutex ved hjælp af xSemaphoreCreateMutex (). Opret en opgave med lige prioriteter, og prøv senere at lege med dette nummer.
ugyldig opsætning () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); hvis (mutex_v == NULL) { Serial.println ("Mutex kan ikke oprettes"); } xTaskCreate (Task1, "Task 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "Task 2", 128, NULL, 1, NULL); }
4. Lav nu opgavefunktioner til Task1 og Task2. Inden for et stykke loop af opgavefunktionen, inden vi udskriver en besked på den serielle skærm, skal vi tage en Mutex ved hjælp af xSemaphoreTake (), derefter udskrive meddelelsen og derefter returnere Mutex ved hjælp af xSemaphoreGive (). Giv derefter lidt forsinkelse.
ugyldig opgave1 (ugyldig * pvParameters) { mens (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Hej fra opgave1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
På samme måde implementerer du Task2-funktionen med en forsinkelse på 500 ms.
5. Void loop () forbliver tom.
Upload nu denne kode på Arduino UNO og åbn den serielle skærm.
Du vil se beskeder udskrives fra opgave1 og opgave2.
For at teste funktionen af Mutex skal du bare kommentere xSemaphoreGive (mutex_v); fra enhver opgave. Du kan se, at programmet hænger på den sidste udskrivningsmeddelelse .
Sådan kan Semaphore og Mutex implementeres i FreeRTOS med Arduino. For mere information om Semaphore og Mutex kan du besøge den officielle dokumentation for FreeRTOS.
Komplette koder og video til Semaphore og Mutes er angivet nedenfor.