- Wat is semafoor?
- Hoe gebruik je Semaphore in FreeRTOS?
- Semafoor Code Uitleg
- Schakelschema
- Wat is Mutex?
- Hoe gebruik je Mutex in FreeRTOS?
- Mutex Code Uitleg
In eerdere tutorials hebben we de basisprincipes van FreeRTOS met Arduino en het Queue-kernelobject in FreeRTOS Arduino behandeld. Nu, in deze derde FreeRTOS-zelfstudie, zullen we meer leren over FreeRTOS en zijn geavanceerde API's, waardoor je het multi-tasking-platform beter kunt begrijpen.
Semaphore en Mutex (Mutual Exclusion) zijn de kernelobjecten die worden gebruikt voor synchronisatie, resourcebeheer en het beschermen van resources tegen corruptie. In de eerste helft van deze tutorial zullen we het idee achter Semaphore zien, hoe en waar het te gebruiken. In de tweede helft gaan we verder met Mutex.
Wat is semafoor?
In eerdere tutorials hebben we het gehad over taakprioriteiten en kwamen we er ook achter dat een taak met een hogere prioriteit een taak met een lagere prioriteit in de weg staat, dus tijdens de uitvoering van een taak met een hoge prioriteit kan er een mogelijkheid zijn dat gegevensbeschadiging kan optreden bij taken met een lagere prioriteit. wordt nog niet uitgevoerd en er komen continu gegevens van een sensor naar deze taak, waardoor gegevens verloren gaan en de hele applicatie niet goed functioneert.
Er is dus een behoefte om bronnen te beschermen tegen gegevensverlies en hier speelt Semaphore een belangrijke rol.
Semafoor is een signaleringsmechanisme waarbij een taak in een wachttoestand wordt gesignaleerd door een andere taak voor uitvoering. Met andere woorden, wanneer een taak1 klaar is met zijn werk, zal hij een vlag laten zien of een vlag met 1 verhogen en dan wordt deze vlag ontvangen door een andere taak (taak2) die aangeeft dat hij zijn werk nu kan uitvoeren. Wanneer task2 zijn werk heeft voltooid, wordt de vlag met 1 verlaagd.
Het is dus in feite een "Give" en "Take" -mechanisme en semafoor is een integer-variabele die wordt gebruikt om de toegang tot bronnen te synchroniseren.
Soorten semafoor in FreeRTOS:
Semafoor is van twee soorten.
- Binaire Semafoor
- Semafoor tellen
1. Binaire semafoor: het heeft twee gehele getallen 0 en 1. Het lijkt enigszins op de wachtrij met lengte 1. We hebben bijvoorbeeld twee taken, task1 en task2. Task1 stuurt data naar task2 dus task2 controleert continu het wachtrij-item als er 1 is, dan kan het de data lezen, anders moet het wachten tot het 1 wordt. Na het nemen van de data verlaagt task2 de wachtrij en maakt het 0 Dat betekent weer task1 kan de gegevens naar task2 sturen.
Uit het bovenstaande voorbeeld kan worden gezegd dat binaire semafoor wordt gebruikt voor synchronisatie tussen taken of tussen taken en interrupt.
2. Semafoor tellen: het heeft waarden groter dan 0 en kan worden beschouwd als een wachtrij met een lengte van meer dan 1. Deze semafoor wordt gebruikt voor het tellen van gebeurtenissen. In dit gebruiksscenario zal een gebeurtenishandler een semafoor 'geven' elke keer dat een gebeurtenis plaatsvindt (waarbij de telwaarde van de semafoor wordt verhoogd), en een handlertaak zal een semafoor 'nemen' elke keer dat deze een gebeurtenis verwerkt (de telwaarde van de semafoor verlagen).
De telwaarde is daarom het verschil tussen het aantal gebeurtenissen dat is opgetreden en het aantal dat is verwerkt.
Laten we nu eens kijken hoe we Semaphore kunnen gebruiken in onze FreeRTOS-code.
Hoe gebruik je Semaphore in FreeRTOS?
FreeRTOS ondersteunt verschillende API's voor het maken van een semafoor, het nemen van een semafoor en het geven van een semafoor.
Nu kunnen er twee soorten API's zijn voor hetzelfde kernelobject. Als we een semafoor moeten geven vanuit een ISR, kan de normale semafoor-API niet worden gebruikt. U moet door interrupts beveiligde API's gebruiken.
In deze zelfstudie gebruiken we binaire semafoor omdat deze gemakkelijk te begrijpen en te implementeren is. Aangezien de interruptfunctionaliteit hier wordt gebruikt, moet u in de ISR-functie API's met interruptbescherming gebruiken. Wanneer we het synchroniseren van een taak met een interrupt zeggen, betekent dit dat de taak direct na de ISR in de status Running wordt gezet.
Een semafoor maken:
Om een kernelobject te gebruiken, moeten we het eerst maken. Gebruik vSemaphoreCreateBinary () om een binaire semafoor te maken .
Deze API accepteert geen enkele parameter en retourneert een variabele van het type SemaphoreHandle_t. Er wordt een globale variabelenaam sema_v gemaakt om de semafoor op te slaan.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Een seinpaal geven:
Voor het geven van een semafoor zijn er twee versies: een voor interrupt en een voor de normale taak.
- xSemaphoreGive (): Deze API heeft slechts één argument nodig, namelijk de variabelenaam van semafoor zoals sema_v zoals hierboven gegeven bij het maken van een semafoor. Het kan worden aangeroepen vanuit elke normale taak die u wilt synchroniseren.
- xSemaphoreGiveFromISR (): Dit is de onderbroken beveiligde API-versie van xSemaphoreGive (). Als we een ISR en een normale taak moeten synchroniseren, moet xSemaphoreGiveFromISR () worden gebruikt vanuit de ISR-functie.
Een seinpaal nemen:
Gebruik de API-functie xSemaphoreTake () om een semafoor te nemen. Deze API heeft twee parameters nodig.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Naam van de semafoor die in ons geval sema_v moet worden gebruikt.
xTicksToWait: Dit is de maximale tijd dat de taak in de status Geblokkeerd wacht totdat de semafoor beschikbaar is. In ons project zullen we xTicksToWait instellen op portMAX_DELAY om de task_1 voor onbepaalde tijd te laten wachten in de status Geblokkeerd totdat de sema_v beschikbaar is.
Laten we nu deze API's gebruiken en een code schrijven om een aantal taken uit te voeren.
Hier zijn een drukknop en twee LED's met elkaar verbonden. De drukknop fungeert als een onderbrekingsknop die is bevestigd aan pin 2 van Arduino Uno. Wanneer deze knop wordt ingedrukt, wordt een onderbreking gegenereerd en een LED die is aangesloten op pin 8 zal worden ingeschakeld en wanneer u deze opnieuw indrukt, wordt deze uitgeschakeld.
Dus als de knop wordt ingedrukt, wordt xSemaphoreGiveFromISR () aangeroepen vanuit de ISR-functie en de xSemaphoreTake () -functie wordt aangeroepen vanuit de TaskLED-functie.
Om het systeem er als multitasking uit te laten zien, verbindt u andere LED's met pin 7 die altijd knipperen.
Semafoor Code Uitleg
Laten we beginnen met het schrijven van code door de Arduino IDE te openen
1. Voeg eerst het Arduino_FreeRTOS.h header-bestand toe. Als een kernelobject wordt gebruikt zoals een semafoor in de wachtrij, dan moet er ook een header-bestand voor worden toegevoegd.
#include #include
2. Declareer een variabele van het type SemaphoreHandle_t om de waarden van semafoor op te slaan.
SemaphoreHandle_t interruptSemaphore;
3. Maak in void setup () twee taken (TaskLED en TaskBlink) met behulp van de xTaskCreate () API en maak vervolgens een semafoor met xSemaphoreCreateBinary (). Maak een taak met dezelfde prioriteiten en probeer later met dit nummer te spelen. Configureer ook pin 2 als een ingang en schakel de interne pull-up-weerstand in en bevestig de interrupt-pin. Start ten slotte de planner zoals hieronder weergegeven.
leegte 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. Implementeer nu de ISR-functie. Maak een functie en noem deze hetzelfde als het tweede argument van de functie attachInterrupt () . Om de onderbreking goed te laten werken, moet u het debounceprobleem van de drukknop verwijderen met de millis- of micros-functie en door de debouncing-tijd aan te passen. Roep vanuit deze functie de functie interruptHandler () aan zoals hieronder weergegeven.
lange debouncing_time = 150; vluchtige unsigned long last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
In interruptHandler () functie, bel xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Deze functie geeft TaskLed een seinpaal om de LED aan te zetten.
5. Maak een TaskLed- functie en roep binnen de while- lus de xSemaphoreTake () API aan en controleer of de semafoor met succes is genomen of niet. Als het gelijk is aan pdPASS (dwz 1), laat de LED dan schakelen zoals hieronder weergegeven.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, UITGANG); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Maak ook een functie om een andere LED te laten knipperen die is aangesloten op pin 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, UITGANG); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. De void loop-functie blijft leeg. Vergeet het niet.
lege lus () {}
Dat is alles, volledige code is te vinden aan het einde van deze tutorial. Upload nu deze code en verbind de LED's en drukknop met de Arduino UNO volgens het schakelschema.
Schakelschema
Na het uploaden van de code, zul je zien dat een LED knippert na 200ms en wanneer de knop wordt ingedrukt, zal onmiddellijk de tweede LED oplichten zoals getoond in de video aan het einde.
Op deze manier kunnen semaforen worden gebruikt in FreeRTOS met Arduino, waar het de gegevens zonder verlies van de ene taak naar de andere moet doorgeven.
Laten we nu eens kijken wat Mutex is en hoe we het FreeRTOS kunnen gebruiken.
Wat is Mutex?
Zoals hierboven uitgelegd is semafoor een signaleringsmechanisme, op dezelfde manier is Mutex een vergrendelingsmechanisme in tegenstelling tot de semafoor dat afzonderlijke functies heeft voor verhogen en verlagen, maar in Mutex neemt en geeft de functie op zichzelf. Het is een techniek om de corruptie van gedeelde bronnen te voorkomen.
Om de gedeelde bron te beschermen, wijst men een tokenkaart (mutex) toe aan de bron. Wie deze kaart heeft, heeft toegang tot de andere bron. Anderen moeten wachten tot de kaart is teruggegeven. Op deze manier heeft slechts één bron toegang tot de taak en wachten anderen op hun kans.
Laten we Mutex in FreeRTOS begrijpen met behulp van een voorbeeld.
Hier hebben we drie taken, een voor het afdrukken van gegevens op het LCD-scherm, een tweede voor het verzenden van LDR-gegevens naar een LCD-taak en de laatste taak voor het verzenden van temperatuurgegevens op het LCD-scherm. Dus hier delen twee taken dezelfde bron, namelijk LCD. Als de LDR-taak en de temperatuurtaak gelijktijdig gegevens verzenden, is een van de gegevens mogelijk beschadigd of verloren gegaan.
Dus om het gegevensverlies te beschermen, moeten we de LCD-bron voor taak 1 vergrendelen totdat de weergavetaak is voltooid. Vervolgens wordt de LCD-taak ontgrendeld en kan taak2 zijn werk uitvoeren.
U kunt de werking van Mutex en semaforen observeren in het onderstaande diagram.
Hoe gebruik je Mutex in FreeRTOS?
Mutexs worden ook op dezelfde manier gebruikt als semaforen. Maak het eerst en geef en neem vervolgens met behulp van de respectieve API's.
Een Mutex maken:
Gebruik de xSemaphoreCreateMutex () API om een Mutex te maken. Zoals de naam suggereert, is Mutex een soort binaire semafoor. Ze worden in verschillende contexten en doeleinden gebruikt. Een binaire semafoor is voor het synchroniseren van taken, terwijl Mutex wordt gebruikt voor het beschermen van een gedeelde bron.
Deze API accepteert geen enkel argument en retourneert een variabele van het type SemaphoreHandle_t . Als de mutex niet kan worden gemaakt, retourneert xSemaphoreCreateMutex () NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Een Mutex nemen:
Wanneer een taak toegang wil krijgen tot een bron, heeft deze een Mutex nodig met behulp van de xSemaphoreTake () API. Het is hetzelfde als een binaire semafoor. Er zijn ook twee parameters nodig.
xSemaphore: Naam van de Mutex die in ons geval mutex_v moet worden gebruikt .
xTicksToWait: dit is de maximale tijd dat de taak in de status Geblokkeerd wacht totdat de Mutex beschikbaar komt. In ons project zullen we xTicksToWait instellen op portMAX_DELAY om de task_1 voor onbepaalde tijd te laten wachten in de status Geblokkeerd totdat de mutex_v beschikbaar is.
Een Mutex geven:
Na toegang tot de gedeelde bron, moet de taak de Mutex retourneren zodat andere taken er toegang toe hebben. xSemaphoreGive () API wordt gebruikt om de Mutex terug te geven.
De functie xSemaphoreGive () heeft slechts één argument nodig en dat is de Mutex die in ons geval mutex_v moet worden gegeven.
Laten we met behulp van de bovenstaande API's Mutex implementeren in de FreeRTOS-code met Arduino IDE.
Mutex Code Uitleg
Hier is het doel van dit deel om een seriële monitor te gebruiken als een gedeelde bron en twee verschillende taken om toegang te krijgen tot de seriële monitor om een bericht af te drukken.
1. De header-bestanden blijven hetzelfde als een semafoor.
#include #include
2. Declareer een variabele van het type SemaphoreHandle_t om de waarden van Mutex op te slaan.
SemaphoreHandle_t mutex_v;
3. In void setup (), initialiseer seriële monitor met 9600 baudrate en maak twee taken (Task1 en Task2) met behulp van de xTaskCreate () API. Maak vervolgens een Mutex met xSemaphoreCreateMutex (). Maak een taak met dezelfde prioriteiten en probeer later met dit nummer te spelen.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Mutex kan niet worden aangemaakt"); } xTaskCreate (Task1, "Task 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "Task 2", 128, NULL, 1, NULL); }
4. Maak nu taakfuncties voor Task1 en Task2. In een while- lus van de taakfunctie, voordat we een bericht op de seriële monitor afdrukken, moeten we een Mutex nemen met xSemaphoreTake (), vervolgens het bericht afdrukken en vervolgens de Mutex retourneren met xSemaphoreGive (). Geef dan wat vertraging.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Hallo van Task1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Implementeer op dezelfde manier de Task2-functie met een vertraging van 500 ms.
5. Void loop () blijft leeg.
Upload deze code nu naar Arduino UNO en open de seriële monitor.
U zult zien dat er berichten worden afgedrukt van task1 en task2.
Om de werking van Mutex te testen, hoeft u alleen maar xSemaphoreGive (mutex_v) te becommentariëren; van elke taak. U kunt zien dat het programma blijft hangen aan het laatste afdrukbericht .
Dit is hoe Semaphore en Mutex kunnen worden geïmplementeerd in FreeRTOS met Arduino. Voor meer informatie over Semaphore en Mutex kun je de officiële documentatie van FreeRTOS bezoeken.
Volledige codes en video voor Semaphore en Mutes worden hieronder gegeven.