- Semafor nedir?
- FreeRTOS'ta Semafor nasıl kullanılır?
- Semafor Kodu Açıklaması
- Devre şeması
- Mutex nedir?
- FreeRTOS'ta Mutex nasıl kullanılır?
- Mutex Kod Açıklaması
Önceki eğitimlerde, FreeRTOS'un temellerini Arduino ile ve FreeRTOS Arduino'daki Queue kernel nesnesini ele aldık. Şimdi, bu üçüncü FreeRTOS eğitiminde, FreeRTOS ve çoklu görev platformunu daha derinlemesine anlamanızı sağlayacak gelişmiş API'leri hakkında daha fazla bilgi edineceğiz.
Semafor ve Mutex (Karşılıklı Dışlama) senkronizasyon, kaynak yönetimi ve kaynakları bozulmadan korumak için kullanılan çekirdek nesneleridir. Bu eğitimin ilk yarısında, Semafor'un arkasındaki fikri, onu nasıl ve nerede kullanacağımızı göreceğiz. İkinci yarıda Mutex ile devam edeceğiz.
Semafor nedir?
Önceki eğitimlerde, görev önceliklerini tartışmıştık ve ayrıca daha yüksek öncelikli bir görevin daha düşük öncelikli bir görevi önceden yerine getirdiğini biliyoruz, bu nedenle yüksek öncelikli görev yürütülürken daha düşük öncelikli görevde veri bozulmasının meydana gelme olasılığı olabilir çünkü bu henüz çalıştırılmadı ve bu göreve sürekli olarak bir sensörden veri geliyor, bu da veri kaybına ve tüm uygulamanın arızalanmasına neden oluyor.
Dolayısıyla, kaynakları veri kaybından korumaya ihtiyaç vardır ve burada Semafor önemli bir rol oynar.
Semafor, bekleme durumundaki bir görevin yerine getirilmesi için başka bir görev tarafından bildirildiği bir sinyalleşme mekanizmasıdır. Başka bir deyişle, bir görev1 çalışmasını bitirdiğinde, bir bayrak gösterecek veya bir bayrağı 1 artıracak ve ardından bu bayrak, şimdi işini gerçekleştirebileceğini gösteren başka bir görev (görev2) tarafından alınır. Görev2 çalışmasını bitirdiğinde bayrak 1 azaltılacaktır.
Yani, temel olarak, bir "Ver" ve "Al" mekanizmasıdır ve semafor, kaynaklara erişimi senkronize etmek için kullanılan bir tamsayı değişkendir.
FreeRTOS'ta Semafor Türleri:
Semafor iki türdendir.
- İkili Semafor
- Semafor Sayma
1. İkili Semafor: 0 ve 1 olmak üzere iki tamsayı değerine sahiptir. Uzunluk Sırası 1'e biraz benzer. Örneğin, iki görevimiz vardır, görev1 ve görev2. Görev1, verileri görev2'ye gönderir, böylece görev2, 1 varsa kuyruk öğesini sürekli olarak kontrol eder, ardından 1 olana kadar beklemesi gereken verileri okuyabilir. Verileri aldıktan sonra, görev2 kuyruğu azaltır ve 0 yapar Bu, görev1 anlamına gelir. verileri görev2'ye gönderebilir.
Yukarıdaki örnekten, ikili semaforun görevler arasında veya görevler ve kesme arasında senkronizasyon için kullanıldığı söylenebilir.
2. Semafor Sayma: 0'dan büyük değerlere sahiptir ve 1'den fazla uzunlukta olduğu düşünülebilir. Bu semafor olayları saymak için kullanılır. Bu kullanım senaryosunda, bir olay işleyicisi, bir olay meydana geldiğinde her seferinde bir semafor 'verir' (semafor sayım değerini artırarak) ve bir işleyici görevi, bir olayı her işlediğinde bir semafor 'alır' (semafor sayım değerini azaltır).
Bu nedenle sayı değeri, meydana gelen olayların sayısı ile işlenen sayı arasındaki farktır.
Şimdi, FreeRTOS kodumuzda Semaforu nasıl kullanacağımızı görelim.
FreeRTOS'ta Semafor nasıl kullanılır?
FreeRTOS bir semafor oluşturmak, bir semafor almak ve bir semafor vermek için farklı API'leri destekler.
Şimdi, aynı çekirdek nesnesi için iki tür API olabilir. Bir ISR'den semafor vermemiz gerekiyorsa, normal semafor API kullanılamaz. Kesinti korumalı API'ler kullanmalısınız.
Bu eğitimde, anlaşılması ve uygulanması kolay olduğu için ikili semafor kullanacağız. Burada kesme işlevi kullanıldığı için, ISR işlevinde kesme korumalı API'leri kullanmanız gerekir. Bir görevi bir kesinti ile senkronize etmeyi söylediğimizde, görevi ISR'den hemen sonra Çalışıyor durumuna getirmek anlamına gelir.
Semafor Oluşturmak:
Herhangi bir çekirdek nesnesini kullanmak için önce onu yaratmalıyız. İkili semafor oluşturmak için vSemaphoreCreateBinary () kullanın .
Bu API herhangi bir parametre almaz ve SemaphoreHandle_t türünde bir değişken döndürür. Semaforu depolamak için global bir değişken adı sema_v oluşturulur.
SemaphoreHandle_t sema_v; sema_v = xSemaphoreCreateBinary ();
Bir semafor vermek:
Bir semafor vermek için, biri kesme, diğeri normal görev için olmak üzere iki versiyon vardır.
- xSemaphoreGive (): Bu API, semafor oluştururken yukarıda verildiği gibi sema_v gibi semaforun değişken adı olan yalnızca bir argüman alır. Senkronize etmek istediğiniz herhangi bir normal görevden çağrılabilir.
- xSemaphoreGiveFromISR (): Bu, xSemaphoreGive () ' in kesme korumalı API sürümüdür. Bir ISR ile normal görevi senkronize etmemiz gerektiğinde, xSemaphoreGiveFromISR () ISR işlevinden kullanılmalıdır.
Bir semafor almak:
Bir semafor almak için xSemaphoreTake () API işlevini kullanın. Bu API iki parametre alır.
xSemaphoreTake (SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);
xSemaphore: Bizim durumumuzda alınacak semaforun adı sema_v.
xTicksToWait: Bu, semaforun kullanılabilir hale gelmesi için görevin Engellendi durumunda bekleyeceği maksimum süredir. Projemizde biz ayarlayacaktır xTicksToWait için portMAX_DELAY sema_v hazır olana kadar Engellenen halde sonsuza kadar beklemek task_1 yapmak.
Şimdi bu API'leri kullanalım ve bazı görevleri gerçekleştirmek için bir kod yazalım.
Burada bir düğme ve iki LED arabirimlidir. Basma düğmesi, Arduino Uno'nun 2. pinine takılı bir kesme düğmesi olarak işlev görür. Bu düğmeye basıldığında bir kesinti üretilecek ve pin 8'e bağlı bir LED AÇIK olacak ve tekrar bastığınızda KAPALI olacaktır.
Dolayısıyla, düğmeye basıldığında xSemaphoreGiveFromISR () ISR işlevinden ve xSemaphoreTake () işlevi TaskLED işlevinden çağrılacaktır.
Sistemin çok görevli görünmesini sağlamak için, her zaman yanıp sönen durumda olacak olan diğer LED'leri pim 7'ye bağlayın.
Semafor Kodu Açıklaması
Arduino IDE'yi açarak kod yazmaya başlayalım
1. İlk olarak, Arduino_FreeRTOS.h başlık dosyasını dahil edin. Şimdi, herhangi bir çekirdek nesnesi kuyruk semaforu gibi kullanılırsa, bunun için bir başlık dosyası da dahil edilmelidir.
#include #include
2. Semafor değerlerini depolamak için SemaphoreHandle_t türünde bir değişken bildirin.
SemaphoreHandle_t interruptSemaphore;
3. Void setup () 'da, xTaskCreate () API'yi kullanarak iki görev (TaskLED ve TaskBlink) oluşturun ve ardından xSemaphoreCreateBinary () kullanarak bir semafor oluşturun. Eşit önceliklere sahip bir görev oluşturun ve daha sonra bu numara ile oynamayı deneyin. Ayrıca, pin 2'yi bir giriş olarak yapılandırın ve dahili kaldırma direncini etkinleştirin ve kesme pimini takın. Son olarak, programlayıcıyı aşağıda gösterildiği gibi başlatın.
geçersiz kurulum () { pinMode (2, INPUT_PULLUP); xTaskCreate (TaskLed, "Led", 128, NULL, 0, NULL); xTaskCreate (TaskBlink, "LedBlink", 128, NULL, 0, NULL); interruptSemaphore = xSemaphoreCreateBinary (); eğer (interruptSemaphore! = NULL) { attachInterrupt (digitalPinToInterrupt (2), debounceInterrupt, LOW); } }
4. Şimdi ISR işlevini uygulayın. Bir işlevi yapın ve onu attachInterrupt () işlevinin ikinci bağımsız değişkeniyle aynı şekilde adlandırın. Kesmenin düzgün çalışmasını sağlamak için, milis veya mikro işlevini kullanarak ve geri bildirim süresini ayarlayarak basma düğmesinin geri çevrilme problemini gidermeniz gerekir. Bu işlevden, aşağıda gösterildiği gibi interruptHandler () işlevini çağırın.
uzun debouncing_time = 150; uçucu işaretsiz uzun last_micros; void debounceInterrupt () { if ((long) (micros () - last_micros)> = debouncing_time * 1000) { interruptHandler (); last_micros = micros (); } }
In kesme kotarıcı () fonksiyonu, çağrı xSemaphoreGiveFromISR () API.
void interruptHandler () { xSemaphoreGiveFromISR (interruptSemaphore, NULL); }
Bu fonksiyon, TaskLed'e LED'i AÇMAK için bir semafor verecektir.
5. Bir TaskLed işlevi oluşturun ve while döngüsünün içinde, xSemaphoreTake () API'yi çağırın ve semaforun başarıyla alınıp alınmadığını kontrol edin. PdPASS'a (yani 1) eşitse, aşağıda gösterildiği gibi LED'i değiştirin.
void TaskLed (void * pvParameters) { (void) pvParameters; pinMode (8, ÇIKIŞ); while (1) { if (xSemaphoreTake (interruptSemaphore, portMAX_DELAY) == pdPASS) { digitalWrite (8,! digitalRead (8)); } } }
6. Ayrıca, pim 7'ye bağlı diğer LED'leri yakıp söndürmek için bir işlev oluşturun.
void TaskLed1 (void * pvParameters) { (void) pvParameters; pinMode (7, ÇIKIŞ); while (1) { digitalWrite (7, HIGH); vTaskDelay (200 / portTICK_PERIOD_MS); digitalWrite (7, DÜŞÜK); vTaskDelay (200 / portTICK_PERIOD_MS); } }
7. Boşluk döngü işlevi boş kalacaktır. Unutma.
boşluk döngüsü () {}
İşte bu, tam kod bu eğitimin sonunda bulunabilir. Şimdi, bu kodu yükleyin ve LED'leri ve butonu devre şemasına göre Arduino UNO ile bağlayın.
Devre şeması
Kodu yükledikten sonra, 200 ms sonra bir LED'in yanıp söndüğünü göreceksiniz ve butona basıldığında, sonunda verilen videoda gösterildiği gibi ikinci LED hemen yanacaktır.
Bu sayede semaforlar FreeRTOS'ta Arduino ile birlikte verinin bir görevden diğerine kayıpsız olarak aktarılması gereken yerlerde kullanılabilir.
Şimdi Mutex'in ne olduğunu ve FreeRTOS'u nasıl kullanacağımızı görelim.
Mutex nedir?
Yukarıda açıklandığı gibi semafor bir sinyalleşme mekanizmasıdır, benzer şekilde Mutex, semafordan farklı olarak artırma ve azaltma için ayrı işlevleri olan ancak Mutex'te işlev alır ve kendi içinde verir. Paylaşılan kaynakların bozulmasını önlemek için bir tekniktir.
Paylaşılan kaynağı korumak için kaynağa bir token kartı (muteks) atanır. Bu karta sahip olan kişi diğer kaynağa erişebilir. Diğerleri kart geri gelene kadar beklemelidir. Bu şekilde, göreve yalnızca bir kaynak erişebilir ve diğerleri şanslarını bekler.
En anlayalım freertos içinde Mutex bir örnek yardımıyla.
Burada üç görevimiz var: Biri LCD'ye veri yazdırmak için, ikincisi LDR verilerini LCD görevine göndermek için ve son görev LCD'de Sıcaklık verilerini göndermek için. Yani burada iki görev aynı kaynağı paylaşıyor, yani LCD. LDR görevi ve sıcaklık görevi aynı anda veri gönderirse, verilerden biri bozulabilir veya kaybolabilir.
Bu nedenle, veri kaybını korumak için, görüntüleme görevini bitirene kadar görev1 için LCD kaynağını kilitlememiz gerekir. Ardından LCD görevi açılır ve görev2 işini yapabilir.
Aşağıdaki diyagramda Mutex ve semaforların çalışmasını gözlemleyebilirsiniz.
FreeRTOS'ta Mutex nasıl kullanılır?
Muteksler de semaforlarla aynı şekilde kullanılır. Önce onu oluşturun, ardından ilgili API'leri kullanarak verin ve alın.
Mutex Oluşturmak:
Mutex oluşturmak için xSemaphoreCreateMutex () API'sini kullanın. Adından da anlaşılacağı gibi Mutex bir tür İkili semafordur. Farklı bağlamlarda ve amaçlarla kullanılırlar. Bir ikili semafor, görevleri senkronize etmek için kullanılırken Mutex, paylaşılan bir kaynağı korumak için kullanılır.
Bu API herhangi bir bağımsız değişken almaz ve SemaphoreHandle_t türünde bir değişken döndürür. Muteks oluşturulamazsa, xSemaphoreCreateMutex () NULL döndürür.
SemaphoreHandle_t mutex_v; mutex_v = xSemaphoreCreateMutex ();
Mutex almak:
Bir görev bir kaynağa erişmek istediğinde, xSemaphoreTake () API kullanarak bir Mutex alacaktır. İkili semafor ile aynıdır. Ayrıca iki parametre alır.
xSemaphore: Bizim durumumuzda alınacak Mutex'in adı mutex_v .
xTicksToWait: Bu, Mutex'in kullanılabilir hale gelmesi için görevin Engellenmiş durumda bekleyeceği maksimum süredir. Projemizde, task_1'in , mutex_v kullanılabilir olana kadar Engellenmiş durumda süresiz olarak beklemesi için xTicksToWait'i portMAX_DELAY olarak ayarlayacağız .
Mutex Vermek:
Paylaşılan kaynağa eriştikten sonra, görev Mutex'e geri dönmelidir, böylece diğer görevler ona erişebilir. xSemaphoreGive () API, Mutex'i geri vermek için kullanılır.
XSemaphoreGive () işlevi, mutex_v durumumuzda verilecek olan Mutex olan yalnızca bir argüman alır.
Yukarıdaki API'leri kullanarak, Arduino IDE kullanarak FreeRTOS kodunda Mutex'i uygulayalım.
Mutex Kod Açıklaması
Burada bu parçanın amacı, bir Seri monitörü paylaşılan bir kaynak olarak kullanmak ve bir mesajı yazdırmak için seri monitöre erişmek için iki farklı görev kullanmaktır.
1. Başlık dosyaları semaforla aynı kalacaktır.
#include #include
2. Mutex'in değerlerini saklamak için SemaphoreHandle_t türünde bir değişken bildirin.
SemaphoreHandle_t mutex_v;
3. Geçersiz kurulumda (), 9600 baud hızıyla seri monitörü başlatın ve xTaskCreate () API kullanarak iki görev (Task1 ve Task2) oluşturun . Ardından xSemaphoreCreateMutex () kullanarak bir Mutex oluşturun . Eşit önceliklere sahip bir görev oluşturun ve daha sonra bu sayıyla oynamaya çalışın.
geçersiz kurulum () { Serial.begin (9600); mutex_v = xSemaphoreCreateMutex (); if (mutex_v == NULL) { Serial.println ("Muteks oluşturulamaz"); } xTaskCreate (Task1, "Task 1", 128, NULL, 1, NULL); xTaskCreate (Task2, "Task 2", 128, NULL, 1, NULL); }
4. Şimdi, Task1 ve Task2 için görev işlevlerini yapın. Bir süre görev fonksiyonu döngüsünde, seri monitörde bir mesaj yazdırmadan önce, xSemaphoreTake () kullanarak bir Mutex almalı ve ardından mesajı yazdırmalı ve ardından Mutex'i xSemaphoreGive () kullanarak döndürmeliyiz. O zaman biraz geciktirin.
void Task1 (void * pvParameters) { while (1) { xSemaphoreTake (mutex_v, portMAX_DELAY); Serial.println ("Task1'den Merhaba"); xSemaphoreGive (mutex_v); vTaskDelay (pdMS_TO_TICKS (1000)); } }
Benzer şekilde, Task2 işlevini 500 ms gecikmeyle uygulayın.
5. Boş döngü () boş kalacaktır.
Şimdi bu kodu Arduino UNO'ya yükleyin ve seri monitörü açın.
Görev1 ve görev2'den mesajların yazdırıldığını göreceksiniz.
Mutex'in çalışmasını test etmek için, xSemaphoreGive (mutex_v) yorumunu yapmanız yeterlidir; herhangi bir görevden. Programın son yazdırma mesajında takıldığını görebilirsiniz .
Semaphore ve Mutex, FreeRTOS'ta Arduino ile bu şekilde uygulanabilir. Semaphore ve Mutex hakkında daha fazla bilgi için, FreeRTOS'un resmi belgelerini ziyaret edebilirsiniz.
Semafor ve Sessizler için tam kodlar ve video aşağıda verilmiştir.