Merhaba,
Bir süredir çeşitli sebeplerden ötürü bir şeyler karalamaya vaktim olmadı. Umarım iyisinizdir! Bu yazımda uzun süredir sizlerle paylaşmak istediğim FreeRTOS hakkında mütevazı bilgiler vermek istiyorum.
Lisans dönemimde merak edip ARM tabanlı mikro işlemcilerde basit denemeler yaparak mantığını anlamaya çalıştığım CMSIS RTOS’ u, lisans bitirme projemde kullanmaya karar vererek gerçek zamanlı işletim sistemleri hakkında araştırma yapmaya başladım. İleriki zamanlarda ise CMSIS RTOS’ un aslında FreeRTOS tabanlı olduğunu görerek, oturup, yaklaşık 400 sayfalık bir “Mastering FreeRTOS” dokümanını kurcalamaya başladım.
Peki uzatmadan konumuza dönelim. Nedir bu RTOS? Standart bir C ya da gömülü sistem yazılımını ele alalım. Yazdığımız komutların yukarıdan aşağıya ve teker teker icra edildiğini hepimiz biliyoruz. Gömülü uygulamalarda, olmazsa olmaz ?, bir güzel gecikme fonksiyonlarını da kullanıyoruz. Fakat zamanın ya da bir sürecin kritik olduğu uygulamalarda gecikme fonksiyonları, örneğin t anında gerçekleşmesi gereken süreci sekteye uğratabiliyor.
int(void)
{
while(1)
{
sensor_oku();
if (insan) {
dur();
} else{
ilerle();
}
cesitli_gecikmeler();
}
}
Yukarıdaki kod örneğinin bir trenin ya da bir aracın “insan” tespit ettiğinde durmasını ve eğer insan yoksa hareketine devam etmesini sağladığını düşünelim. Biliyorum biraz fazla çiğ bir örnek fakat işe yarayacağına inanıyorum. Sormamız gereken soru şu: araç ya da trenin hangi hızda olursa olsun insana çarpmadan durabilmesi için gereken süre nedir? Bir bu süreyi “t” olarak ele alalım. Eğer dur fonksiyonunun bu t zamanı içinde işini bitirmesini ister ve yukarıdaki kötü örneği implemente edersek -kötü haber- çok bekleriz! Bunun nedeni sensörün okunması, ilerleme ve aklınızda canlandırabileceğiniz tüm süreçlerin zaten kendi içlerinde gecikmelere sahip olabileceğidir. Bu gecikmeler yalnızca “delay” olarak düşünülmemelidir. Süreç bir kontrol algoritması olabilir ve bir periyoda sahip olabilir, süreç içerisinde donanımsal ya da yazılımsal gecikmeler bulunabilir. Tüm bunları topladığınızı ve son adımda gerekli olduğunu düşündüğünüz çeşitli gecikmeleri de bu hesaba katarsak tam olarak anlatmaya çalıştığım durma/frenleme örneğinde ne yazık ki kritik bir süreci kritik bir zaman içerisinde tamamlayamayız.

Tam üzerine bastığımız sorunun çözümü zaman kritik bir uygulamanın sürecimize dahil olmasıdır. Yarattığımız her süreç şeklen birer “main” fonksiyonuna benzer ancak her biri için işlemci hafızasında ayrı alan ayrılır ve kullandığınız RTOS birimi tarafından bu alandan çağırılır. İlk bakışta her biri aynı anda çalışan farklı süreçler yarattığımızı düşünebiliriz. Ama eğer çok çekirdekli bir sistemimiz yoksa bunun yerine RTOS belirli zaman kesitlerinde dizayna bağlı olarak belirlediğiniz süreç “önceliklerini” dikkate alarak işlemleri gerçekleştirir.


Şu ana kadar süreç olarak adlandırdığımız kod alanları terminolojide Task, yani görev (İş Parçacığı) olarak anılır. Dilerseniz dizayn fazında belirlediğiniz sürelerde tasklarınız periyodik olarak koşturulabilir. Fakat burada unutmamanız gereken nokta birinin periyodunun diğerini etkilemeyeceğidir. Periyodik tasklara örnek olarak Şekil 2 ile paylaşmış olduğum grafiği dikkatlice inceleyebilirsiniz. Başlangıç ve durma anlarının dışında task bir nevi uyku modunda kalır ve zamanı geldiğinde tekrar görevini yerine getirir. Taskların periyodik olmalarına ek olarak event-triggerd yani olay tabanlı çalışanlarıyla da karşılaşmanız daha olasıdır. Bu tarz durumları yaratmak için kesme rutinleri kullanılabilir.
Anlatmaya çalıştığım bu sistemlerde, tasklar kendi başlarına bir bütün olmak zorunda değillerdir. Birbirileriyle aynı verilere erişim sağlayabilir ve hatta bir veriyi birbirilerine gönderebilirler. Bu haberleşme mantığına Inter Task Communication adı verilir. Taskların ortak erişim sağladığı verilere paylaşılan kaynak (shared resource) denir. Aynı kaynağa erişim sağlayan tasklar varsa bunun bir kuralı olmak zorunda olduğunu hemen araya sıkıştırayım. Düşünsenize, aynı yazıcıyı donanım olarak paylaşan iki task! İkisinin gönderdiği veriler keyfe keder karmaşık bir çıktı sunacaktır. Bu ve benzer karmaşıklıklardan kurtulmak ve kuralına göre oynamak adına Semaphore, Counting Semaphore, Mutual Exclusion, Event Group, Memory Pool, Queue gibi konseptler yaratılmıştır. Tüm bu konseptlere adım adım uygulama yaparak değineceğim. Şekil 4′ te genel olarak taskların program içinde nasıl yer aldığı gösterilmektedir.
Yaratılan taskların her biri, her gömülü yazılımda bulunan main fonksiyonu içinde yazmış olduğumuz gibi bir sonsuz döngü içerir. Böylece task çalışmasını istediğiniz süre boyunca veya bir olayın gerçekleşmiş olması durumunda sürekli çalışacaktır. Tabi bunun bir garantisi yoktur. Çünkü belirli durumlarda t anında çalışmakta olan task bir başka yüksek öncelikli task tarafından durdurulabilir. Yüksek öncelikli task işini bitirdikten sonra ise durdurulan task (pre-empt edilen) çalışmasını bitirmek adına tekrar devreye girer.

Konumuza basit ama hızlı bir giriş yapmış bulunmaktayız. İleriki yazılarımda FreeRTOS’ u daha ayrıntılı ele alarak uygulamalar gerçekleyeceğiz. Uygulamalar için ise daha önce edindiğiniz ARM geliştirme kitleriniz başlı başına yeterli olacaktır.
Tekrar görüşmek üzere! 🙂