- Co je Semafor?
- Jak používat Semafor ve FreeRTOS?
- Vysvětlení semaforového kódu
- Kruhový diagram
- Co je Mutex?
- Jak používat Mutex ve FreeRTOS?
- Vysvětlení kódu Mutex
V předchozích tutoriálech jsme probrali základy FreeRTOS s Arduino a objekt jádra Queue ve FreeRTOS Arduino. V tomto třetím výukovém programu FreeRTOS se nyní dozvíme více o FreeRTOS a jeho pokročilých API, díky kterým budete moci lépe porozumět platformě pro více úkolů.
Semaphore a Mutex (Mutual Exclusion) jsou objekty jádra, které se používají pro synchronizaci, správu zdrojů a ochranu zdrojů před poškozením. V první polovině tohoto tutoriálu uvidíme myšlenku Semaphore, jak a kde ji použít. Ve druhé polovině budeme pokračovat v Mutexu.
Co je Semafor?
V předchozích tutoriálech jsme diskutovali o prioritách úkolů a také jsme poznali, že úkol s vyšší prioritou předchází úkolu s nižší prioritou, takže při provádění úkolu s vysokou prioritou může existovat možnost, že v úkolu s nižší prioritou může dojít k poškození dat, protože ještě není spuštěno a data k této úloze přicházejí nepřetržitě ze senzoru, který způsobí ztrátu dat a nefunkčnost celé aplikace.
Je tedy třeba chránit zdroje před ztrátou dat a zde hraje důležitou roli Semaphore.
Semafor je signalizační mechanismus, ve kterém je úkol ve stavu čekání signalizován jiným úkolem k provedení. Jinými slovy, když úkol1 dokončí svou práci, zobrazí příznak nebo zvýší příznak o 1 a poté tento příznak obdrží jiný úkol (úkol2), který ukazuje, že nyní může vykonávat svou práci. Když task2 dokončí svou práci, příznak se sníží o 1.
V zásadě se tedy jedná o mechanismus „Dej“ a „Vezmi“ a semafor je celočíselná proměnná, která se používá k synchronizaci přístupu k prostředkům.
Typy semaforu ve FreeRTOS:
Semafor je dvou typů.
- Binární semafor
- Počítám semafor
1. Binární semafor: Má dvě celočíselné hodnoty 0 a 1. Je poněkud podobný frontě délky 1. Například máme dva úkoly, úkol1 a úkol2. Task1 odesílá data na task2, takže task2 průběžně kontroluje položku fronty, pokud je 1, pak může číst data, jinak musí počkat, až se stane 1. Po převzetí dat task2 sníží frontu a nastaví ji 0 To znamená znovu task1 může odeslat data na task2.
Z výše uvedeného příkladu lze říci, že binární semafor se používá pro synchronizaci mezi úkoly nebo mezi úkoly a přerušením.
2. Počítání semaforu: Má hodnoty větší než 0 a lze jej považovat za frontu o délce větší než 1. Tento semafor se používá k počítání událostí. V tomto scénáři použití obslužná rutina události „dá“ semafor pokaždé, když dojde k události (zvýší hodnotu počtu semaforů) a úloha obslužné rutiny „vezme“ semafor pokaždé, když zpracuje událost (zmenší hodnotu počtu semaforů).
Hodnota count je tedy rozdíl mezi počtem událostí, ke kterým došlo, a počtem, který byl zpracován.
Nyní se podívejme, jak použít Semaphore v našem kódu FreeRTOS.
Jak používat Semafor ve FreeRTOS?
FreeRTOS podporuje různá rozhraní API pro vytváření semaforu, převzetí semaforu a poskytnutí semaforu.
Nyní mohou existovat dva typy API pro stejný objekt jádra. Pokud musíme dát semafor z ISR, nelze použít normální API semaforu. Měli byste používat API chráněná proti přerušení.
V tomto kurzu použijeme binární semafor, protože je snadno pochopitelný a implementovatelný. Protože se zde používá funkce přerušení, musíte ve funkci ISR použít API chráněná proti přerušení. Když říkáme synchronizace úkolu s přerušením, znamená to uvedení úkolu do stavu Běh hned po ISR.
Vytvoření semaforu:
Chcete-li použít jakýkoli objekt jádra, musíme jej nejprve vytvořit. Pro vytvoření binárního semaforu použijte vSemaphoreCreateBinary ().
Toto API nebere žádný parametr a vrací proměnnou typu SemaphoreHandle_t. Pro uložení semaforu je vytvořen globální název proměnné sema_v.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Dát semafor:
Pro poskytnutí semaforu existují dvě verze - jedna pro přerušení a druhá pro normální úkol.
- xSemaphoreGive (): Toto API má při vytváření semaforu pouze jeden argument, kterým je název proměnné semafor, jako je sema_v, jak je uvedeno výše. Lze jej volat z jakékoli běžné úlohy, kterou chcete synchronizovat.
- xSemaphoreGiveFromISR (): Toto je verze API chráněná proti přerušení xSemaphoreGive (). Když potřebujeme synchronizovat ISR a normální úkol, pak by měla být z funkce ISR použita xSemaphoreGiveFromISR ().
Užívání semaforu:
Chcete-li zaujmout semafor, použijte funkci API xSemaphoreTake (). Toto API má dva parametry.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Název semaforu, který má být v našem případě sema_v.
xTicksToWait: Toto je maximální doba, po kterou bude úloha čekat ve stavu Blokováno, než bude k dispozici semafor. V našem projektu nastavíme xTicksToWait na portMAX_DELAY , aby task_1 počkal neomezeně dlouho v blokovaném stavu, dokud nebude k dispozici sema_v.
Nyní pojďme použít tato rozhraní API a napíšeme kód k provedení některých úkolů.
Zde je propojeno jedno tlačítko a dvě LED. Tlačítko bude fungovat jako tlačítko přerušení, které je připojeno ke kolíku 2 Arduino Uno. Po stisknutí tohoto tlačítka se vygeneruje přerušení a rozsvítí se LED připojená ke kolíku 8 a po dalším stisknutí se vypne.
Takže, když je tlačítko stisknuto xSemaphoreGiveFromISR () bude volána z ISR funkci a xSemaphoreTake () funkce se nazývá z TaskLED funkce.
Chcete-li, aby systém vypadal multitasking, připojte další LED diody s kolíkem 7, který bude vždy blikat.
Vysvětlení semaforového kódu
Začněme psát kód otevřením IDE Arduino
1. Nejprve přidejte soubor záhlaví Arduino_FreeRTOS.h . Nyní, pokud se používá jakýkoli objekt jádra jako semafor fronty, musí být pro něj zahrnut také soubor záhlaví.
# zahrnout # zahrnout
2. Deklarujte proměnnou typu SemaphoreHandle_t pro uložení hodnot semaforu.
SemaphoreHandle_t interruptSemaphore;
3. Ve void setup () vytvořte pomocí API xTaskCreate () dva úkoly (TaskLED a TaskBlink) a poté vytvořte semafor pomocí xSemaphoreCreateBinary (). Vytvořte úkol se stejnými prioritami a později se pokuste hrát s tímto číslem. Nakonfigurujte také pin 2 jako vstup a povolte interní pull-up rezistor a připojte přerušovací pin. Nakonec spusťte plánovač, jak je znázorněno níže.
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. Nyní implementujte funkci ISR. Vytvořte funkci a pojmenujte ji stejně jako druhý argument funkce attachInterrupt () . Aby přerušení fungovalo správně, musíte odstranit problém s odrazem tlačítka pomocí funkce millis nebo micros a úpravou doby odrazu. Z této funkce zavolejte funkci interruptHandler (), jak je znázorněno níže.
dlouhý debouncing_time = 150; volatilní nepodepsaný dlouhý last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
Ve funkci interruptHandler () volejte xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Tato funkce poskytne TaskLedu semafor, aby rozsvítil LED.
5. Vytvořte funkci TaskLed a uvnitř smyčky while zavolejte xSemaphoreTake () API a zkontrolujte, zda je semafor úspěšně přijat nebo ne. Pokud se rovná pdPASS (tj. 1), přepněte LED podle obrázku níže.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, VÝSTUP); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Vytvořte také funkci pro blikání jiné LED připojené ke kolíku 7.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, VÝSTUP); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Funkce neplatné smyčky zůstane prázdná. Nezapomeň na to.
void loop () {}
To je vše, kompletní kód najdete na konci tohoto kurzu. Nyní nahrajte tento kód a připojte LED diody a tlačítko k Arduino UNO podle schématu zapojení.
Kruhový diagram
Po nahrání kódu uvidíte, že po 200 ms bliká LED dioda a po stisknutí tlačítka se okamžitě rozsvítí druhá LED, jak je vidět na videu na konci.
Tímto způsobem lze ve FreeRTOS s Arduinem použít semafory, kde je třeba bez ztráty předávat data z jednoho úkolu do druhého.
Nyní se podívejme, co je Mutex a jak jej používat FreeRTOS.
Co je Mutex?
Jak je vysvětleno výše, semafor je signalizační mechanismus, podobně jako Mutex je blokovací mechanismus na rozdíl od semaforu, který má samostatné funkce pro přírůstek a úbytek, ale v Mutexu tato funkce sama o sobě dává a dává. Jedná se o techniku, jak se vyhnout poškození sdílených zdrojů.
K ochraně sdíleného prostředku jeden přiřadí ke zdroji kartu tokenu (mutex). Kdokoli má tuto kartu, má přístup k druhému zdroji. Ostatní by měli počkat, dokud se karta nevrátí. Tímto způsobem může k úkolu přistupovat pouze jeden zdroj a ostatní čekají na svou příležitost.
Pojďme pochopit Mutex ve FreeRTOS pomocí příkladu.
Zde máme tři úkoly, jeden pro tisk dat na LCD, druhý pro odesílání dat LDR na LCD a poslední úkol pro odesílání dat o teplotě na LCD. Takže zde dva úkoly sdílejí stejný zdroj, tj. LCD. Pokud úkol LDR a úkol teploty odesílají data současně, může dojít k poškození nebo ztrátě jednoho z dat.
Abychom ochránili ztrátu dat, musíme uzamknout prostředek LCD pro úlohu1, dokud nedokončí úlohu zobrazení. Poté se úkol LCD odemkne a úkol 2 pak může vykonávat svou práci.
Fungování Mutexu a semaforů můžete sledovat v následujícím diagramu.
Jak používat Mutex ve FreeRTOS?
Mutexy se také používají stejným způsobem jako semafory. Nejprve jej vytvořte a poté použijte a použijte příslušná rozhraní API.
Vytvoření Mutexu:
Chcete-li vytvořit Mutex, použijte xSemaphoreCreateMutex () API . Jak název napovídá, Mutex je typ binárního semaforu. Používají se v různých kontextech a pro různé účely. Binární semafor slouží k synchronizaci úkolů, zatímco Mutex se používá k ochraně sdíleného prostředku.
Toto API nebere žádný argument a vrací proměnnou typu SemaphoreHandle_t . Pokud mutex nelze vytvořit, vrátí xSemaphoreCreateMutex () hodnotu NULL.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Užívání Mutexu:
Když chce úkol získat přístup k prostředku, bude trvat Mutex pomocí xSemaphoreTake () API. Je to stejné jako binární semafor. Trvá také dva parametry.
xSemaphore: Název Mutexu, který má být v našem případě mutex_v .
xTicksToWait: Toto je maximální doba, po kterou bude úloha čekat ve stavu Blokováno, než bude k dispozici Mutex. V našem projektu nastavíme xTicksToWait na portMAX_DELAY , aby task_1 počkal neomezeně dlouho v blokovaném stavu, dokud nebude k dispozici mutex_v .
Dát Mutex:
Po přístupu ke sdílenému prostředku by úkol měl vrátit Mutex, aby k němu mohly přistupovat další úkoly. xSemaphoreGive () API se používá k vrácení Mutexu zpět.
Funkce xSemaphoreGive () trvá pouze jeden argument, kterým je v našem případě Mutex, který má být uveden v našem případě mutex_v.
Pomocí výše uvedených API implementujeme Mutex v kódu FreeRTOS pomocí Arduino IDE.
Vysvětlení kódu Mutex
Tady je cílem této části použití sériového monitoru jako sdíleného prostředku a dvou různých úkolů pro přístup k sériovému monitoru pro tisk nějaké zprávy.
1. Soubory záhlaví zůstanou stejné jako semafor.
# zahrnout # zahrnout
2. Deklarujte proměnnou typu SemaphoreHandle_t pro uložení hodnot Mutexu.
SemaphoreHandle_t mutex_v;
3. Ve void setup () inicializujte sériový monitor s přenosovou rychlostí 9600 a pomocí API xTaskCreate () vytvořte dva úkoly (Task1 a Task2). Poté vytvořte Mutex pomocí xSemaphoreCreateMutex (). Vytvořte úkol se stejnými prioritami a později se pokuste hrát s tímto číslem.
void setup () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Mutex nelze vytvořit"); } xTaskCreate (Task1, "Task 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "Task 2", 128, NULL, 1, NULL); }
4. Nyní vytvořte funkce úloh pro Task1 a Task2. Ve smyčce while funkce task, před tiskem zprávy na sériovém monitoru musíme vzít Mutex pomocí xSemaphoreTake (), poté vytisknout zprávu a poté vrátit Mutex pomocí xSemaphoreGive (). Pak udělejte nějaké zpoždění.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Ahoj z Task1"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Podobně implementujte funkci Task2 se zpožděním 500 ms.
5. Prázdná smyčka () zůstane prázdná.
Nyní nahrajte tento kód na Arduino UNO a otevřete sériový monitor.
Uvidíte zprávy, které se tisknou z task1 a task2.
Chcete-li otestovat fungování Mutexu, stačí komentovat xSemaphoreGive (mutex_v); z jakéhokoli úkolu. Vidíte, že program visí na poslední tiskové zprávě .
Takto lze implementovat Semaphore a Mutex ve FreeRTOS s Arduino. Další informace o Semaphore a Mutex najdete v oficiální dokumentaci FreeRTOS.
Kompletní kódy a videa pro Semaphore a Mutes jsou uvedeny níže.