- Hva er Semaphore?
- Hvordan bruke Semaphore i FreeRTOS?
- Semaforekode Forklaring
- Kretsdiagram
- Hva er Mutex?
- Hvordan bruke Mutex i FreeRTOS?
- Mutex-kode Forklaring
I tidligere veiledninger har vi dekket det grunnleggende om FreeRTOS med Arduino og Queue-kerneobjektet i FreeRTOS Arduino. Nå, i denne tredje FreeRTOS-opplæringen, vil vi lære mer om FreeRTOS og dets forhånds-API-er, noe som kan få deg til å forstå multi-tasking-plattformen dypere.
Semaphore og Mutex (gjensidig utelukkelse) er kjerneobjektene som brukes til synkronisering, ressursadministrasjon og beskyttelse av ressurser mot korrupsjon. I første halvdel av denne opplæringen vil vi se ideen bak Semaphore, hvordan og hvor den skal brukes. I andre omgang fortsetter vi med Mutex.
Hva er Semaphore?
I tidligere veiledninger har vi diskutert om oppgaveprioriteringer og også blir kjent med at en oppgave med høyere prioritet forhåndsutvikler en oppgave med lavere prioritet, så mens utføring av oppgave med høy prioritet kan det være en mulighet for at datakorrupsjon kan skje i oppgave med lavere prioritet er ikke utført ennå, og data kommer kontinuerlig til denne oppgaven fra en sensor som forårsaker datatap og funksjonsfeil i hele applikasjonen.
Så det er behov for å beskytte ressurser mot tap av data, og her spiller Semaphore en viktig rolle.
Semaphore er en signalmekanisme der en oppgave i ventetilstand signaliseres av en annen oppgave for utføring. Med andre ord, når en oppgave1 avsluttet sitt arbeid, vil den vise et flagg eller øke et flagg med 1, og deretter mottas dette flagget av en annen oppgave (oppgave2) som viser at den kan utføre sitt arbeid nå. Når oppgave2 er ferdig, vil flagget bli redusert med 1.
Så i utgangspunktet er det en "Give" og "Take" -mekanisme, og semafor er en heltallvariabel som brukes til å synkronisere tilgang til ressurser.
Typer av semafor i FreeRTOS:
Semaforen er av to typer.
- Binær semafor
- Teller Semaphore
1. Binær semafor: Den har to heltallverdier 0 og 1. Den ligner litt på køen til lengde 1. For eksempel har vi to oppgaver, oppgave1 og oppgave2. Oppgave1 sender data til oppgave2, slik at oppgave2 sjekker kontinuerlig køelementet hvis det er 1, så kan den lese dataene den må vente til den blir 1. Etter å ha tatt dataene, reduserer oppgave2 køen og gjør den til 0 Det betyr at oppgave1 igjen kan sende dataene til oppgave2.
Fra eksemplet ovenfor kan det sies at binær semafor brukes til synkronisering mellom oppgaver eller mellom oppgaver og avbrudd.
2. Counting Semaphore: Den har verdier større enn 0 og kan tenkes på kø med lengde mer enn 1. Denne semaphore brukes til å telle hendelser. I dette bruksscenarioet vil en hendelsesbehandler 'gi' en semafor hver gang en hendelse inntreffer (økning av semafortellingen), og en behandleroppgave 'tar' en semafor hver gang den behandler en hendelse (dekrementerer semafortellingen).
Telleverdien er derfor forskjellen mellom antall hendelser som har skjedd og antallet som er behandlet.
La oss nå se hvordan du bruker Semaphore i FreeRTOS-koden.
Hvordan bruke Semaphore i FreeRTOS?
FreeRTOS støtter forskjellige API-er for å lage en semafor, ta en semafor og gi en semafor.
Nå kan det være to typer API-er for det samme kjerneobjektet. Hvis vi må gi semafor fra en ISR, kan ikke normal semafor API ikke brukes. Du bør bruke avbruddsbeskyttede API-er.
I denne opplæringen vil vi bruke binær semafor fordi det er lett å forstå og implementere. Ettersom avbrytingsfunksjonalitet brukes her, må du bruke avbruddsbeskyttede API-er i ISR-funksjonen. Når vi sier å synkronisere en oppgave med et avbrudd, betyr det å sette oppgaven i kjører tilstand rett etter ISR.
Opprette en semafor:
For å bruke et hvilket som helst kjerneobjekt, må vi først opprette det. For å lage en binær semafor, bruk vSemaphoreCreateBinary ().
Denne API-en tar ikke noen parameter og returnerer en variabel av typen SemaphoreHandle_t. Et globalt variabelnavn sema_v opprettes for å lagre semaforen.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Å gi en semafor:
For å gi en semafor er det to versjoner - en for avbrudd og en annen for den normale oppgaven.
- xSemaphoreGive (): Denne API-en tar bare ett argument som er variabelnavnet til semafor som sema_v som gitt ovenfor mens du oppretter en semafor. Det kan kalles fra hvilken som helst normal oppgave du vil synkronisere.
- xSemaphoreGiveFromISR (): Dette er den avbruddsbeskyttede API-versjonen av xSemaphoreGive (). Når vi trenger å synkronisere en ISR og en normal oppgave, bør xSemaphoreGiveFromISR () brukes fra ISR-funksjonen.
Å ta en semafor:
For å ta en semafor, bruk API-funksjonen xSemaphoreTake (). Denne API-en tar to parametere.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Navnet på semaforen som skal tas i vårt tilfelle sema_v.
xTicksToWait: Dette er den maksimale tiden oppgaven vil vente i blokkert tilstand til semaforen blir tilgjengelig. I prosjektet vårt vil vi sette xTicksToWait til portMAX_DELAY for å få task_1 til å vente på ubestemt tid i blokkert tilstand til sema_v er tilgjengelig.
La oss nå bruke disse API-ene og skrive en kode for å utføre noen oppgaver.
Her er en trykknapp og to lysdioder grensesnitt. Trykknappen vil fungere som en avbruddsknapp som er festet til pin 2 i Arduino Uno. Når du trykker på denne knappen, genereres et avbrudd, og en LED som er koblet til pin 8 vil slås PÅ, og når du trykker på den igjen, vil den være AV.
Så når du trykker på knappen, vil xSemaphoreGiveFromISR () kalles fra ISR-funksjonen og xSemaphoreTake () -funksjonen vil bli kalt fra TaskLED-funksjonen.
For å få systemet til å se multitasking ut, koble til andre lysdioder med pin 7 som alltid vil blinke.
Semaforekode Forklaring
La oss begynne å skrive kode for ved å åpne Arduino IDE
1. Først må du ta med Arduino_FreeRTOS.h topptekstfilen. Nå, hvis noe kjerneobjekt brukes som køsemafor, må en topptekstfil også inkluderes for det.
# inkluderer # inkluderer
2. Erklær en variabel av typen SemaphoreHandle_t for å lagre verdiene til semaforen.
SemaphoreHandle_t interruptSemaphore;
3. I ugyldig oppsett (), opprett to oppgaver (TaskLED og TaskBlink) ved hjelp av xTaskCreate () API, og opprett deretter en semafor ved hjelp av xSemaphoreCreateBinary (). Opprett en oppgave med like prioriteringer og prøv senere å spille med dette tallet. Konfigurer også pinne 2 som inngang og aktiver den interne trekkmotstanden og fest avbruddspinnen. Til slutt starter du planleggeren som vist nedenfor.
ugyldig oppsett () { 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. Nå, implementer ISR-funksjonen. Lag en funksjon og gi den samme navn som det andre argumentet for attachInterrupt () -funksjonen. For å få avbruddet til å fungere ordentlig, må du fjerne avvisningsproblemet til trykknappen ved hjelp av millis- eller mikrofunksjonen og ved å justere avvisningstiden. Fra denne funksjonen, ring interruptHandler () -funksjonen som vist nedenfor.
lang debouncing_time = 150; flyktige usignerte lenge siste_mikroer; ugyldig debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
I interruptHandler () -funksjonen, ring xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Denne funksjonen vil gi en semafor til TaskLed for å slå PÅ LED.
5. Opprett en TaskLed- funksjon, og ring inn xSemaphoreTake () API inne i while- sløyfen og sjekk om semaforen er tatt eller ikke. Hvis det er lik pdPASS (dvs. 1), må du bytte LED som vist nedenfor.
ugyldig TaskLed (ugyldig * pvParameters) { (ugyldig) pvParameters; pinMode (8, OUTPUT); mens (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Opprett også en funksjon for å blinke annen LED som er koblet til 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. Funksjonen for ugyldig sløyfe forblir tom. Ikke glem det.
ugyldig sløyfe () {}
Det er det, fullstendig kode kan bli funnet på slutten av denne opplæringen. Last nå opp denne koden og koble lysdiodene og trykknappen til Arduino UNO i henhold til kretsskjemaet.
Kretsdiagram
Etter at du har lastet opp koden, vil du se at en LED blinker etter 200 ms, og når du trykker på knappen, vil den andre lysdioden straks lyse som vist i videoen gitt på slutten.
På denne måten kan semaforer brukes i FreeRTOS med Arduino hvor det må overføre dataene fra en oppgave til en annen uten tap.
La oss nå se hva som er Mutex og hvordan du bruker det FreeRTOS.
Hva er Mutex?
Som forklart ovenfor er semafor en signalmekanisme, på samme måte er Mutex en låsemekanisme i motsetning til semaforen som har separate funksjoner for økning og reduksjon, men i Mutex tar funksjonen og gir i seg selv. Det er en teknikk for å unngå korrupsjon av delte ressurser.
For å beskytte den delte ressursen tildeler man ressursen et token-kort (mutex). Den som har dette kortet kan få tilgang til den andre ressursen. Andre bør vente til kortet returneres. På denne måten kan bare én ressurs få tilgang til oppgaven, og andre venter på sjansen.
La oss forstå Mutex i FreeRTOS ved hjelp av et eksempel.
Her har vi tre oppgaver, en for utskrift av data på LCD, andre for sending av LDR-data til LCD-oppgave og siste oppgave for sending av temperaturdata på LCD. Så her deler to oppgaver den samme ressursen, dvs. LCD. Hvis LDR-oppgaven og temperaturoppgaven sender data samtidig, kan en av dataene bli ødelagt eller tapt.
Så for å beskytte tapet av data, må vi låse LCD-ressursen for oppgave 1 til den er ferdig med skjermoppgaven. Deretter vil LCD-oppgaven låse opp, og deretter kan task2 utføre sitt arbeid.
Du kan se hvordan Mutex og semaforer fungerer i diagrammet nedenfor.
Hvordan bruke Mutex i FreeRTOS?
Mutexs brukes også på samme måte som semaforer. Først oppretter du den, deretter gir og tar du ved hjelp av respektive APIer.
Opprette en Mutex:
For å opprette en Mutex, bruk xSemaphoreCreateMutex () API . Som navnet antyder at Mutex er en type binær semafor. De brukes i forskjellige sammenhenger og formål. En binær semafor er for synkronisering av oppgaver mens Mutex brukes til å beskytte en delt ressurs.
Denne API-en tar ikke noe argument og returnerer en variabel av typen SemaphoreHandle_t . Hvis mutex ikke kan opprettes, returnerer xSemaphoreCreateMutex () NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Tar en Mutex:
Når en oppgave ønsker å få tilgang til en ressurs, vil det ta en Mutex ved hjelp av xSemaphoreTake () API. Det er det samme som en binær semafor. Det tar også to parametere.
xSemaphore: Navnet på Mutex som skal tas i vårt tilfelle mutex_v .
xTicksToWait: Dette er den maksimale tiden oppgaven venter i blokkert tilstand for at Mutex skal bli tilgjengelig. I prosjektet vårt vil vi sette xTicksToWait til portMAX_DELAY for å gjøre task_1 til å vente på ubestemt tid i blokkert tilstand til mutex_v er tilgjengelig.
Gi en Mutex:
Etter tilgang til den delte ressursen, bør oppgaven returnere Mutex slik at andre oppgaver kan få tilgang til den. API for xSemaphoreGive () brukes til å gi Mutex tilbake.
Funksjonen xSemaphoreGive () tar bare ett argument som er Mutex som skal gis i vårt tilfelle mutex_v.
Ved å bruke ovennevnte API-er, la oss implementere Mutex i FreeRTOS-koden ved hjelp av Arduino IDE.
Mutex-kode Forklaring
Her er målet for denne delen å bruke en seriell skjerm som en delt ressurs og to forskjellige oppgaver for å få tilgang til den serielle skjermen for å skrive ut en melding.
1. Overskriftsfilene forblir de samme som en semafor.
# inkluderer # inkluderer
2. Erklær en variabel av typen SemaphoreHandle_t for å lagre verdiene til Mutex.
SemaphoreHandle_t mutex_v;
3. I ugyldig oppsett (), initialiser seriell skjerm med 9600 baudrate og opprett to oppgaver (Task1 og Task2) ved hjelp av xTaskCreate () API. Opprett deretter en Mutex ved hjelp av xSemaphoreCreateMutex (). Lag en oppgave med like prioriteringer, og prøv senere å leke med dette tallet.
ugyldig oppsett () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); hvis (mutex_v == NULL) { Serial.println ("Mutex kan ikke opprettes"); } xTaskCreate (Oppgave1, "Oppgave 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "Task 2", 128, NULL, 1, NULL); }
4. Lag nå oppgavefunksjoner for Oppgave1 og Oppgave2. I løpet av en periode med oppgavefunksjon, må vi ta en Mutex ved hjelp av xSemaphoreTake () , før du skriver ut en melding på seriell skjerm, og deretter skrive ut meldingen og deretter returnere Mutex ved hjelp av xSemaphoreGive (). Gi så litt forsinkelse.
ugyldig oppgave1 (ugyldig * pvParameters) { mens (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Hei fra oppgave1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
På samme måte implementerer du Task2-funksjonen med en forsinkelse på 500 ms.
5. Void loop () forblir tom.
Last nå opp denne koden på Arduino UNO og åpne seriell skjerm.
Du vil se at meldinger skrives ut fra oppgave1 og oppgave2.
For å teste hvordan Mutex fungerer, er det bare å kommentere xSemaphoreGive (mutex_v); fra enhver oppgave. Du kan se at programmet henger på den siste utskriftsmeldingen .
Slik kan Semaphore og Mutex implementeres i FreeRTOS med Arduino. For mer informasjon om Semaphore og Mutex, kan du gå til den offisielle dokumentasjonen til FreeRTOS.
Fullstendige koder og video for Semaphore og Mutes er gitt nedenfor.