Gömülü C – 8 : Gömülü C: Koşullamalar

Koşullamalar bildiğiniz üzere programın akışını kontrol eder. Şu dünyada en güzel akışkan su olduğundan, bu yazının teması su olacak.

KOŞULLAMALARIN DÜZGÜN KULLANILMASI

Su akar yolunu bulur demişler. Güzel kod da bu misal. İyi yazılım akışı, iyi koşullamaya çok bağlıdır. Koşullamalar düzgün yapılmadıysa, kodda mutlaka birtakın hatalar çıkacak, o kodun ne yapacağını Laplace Şeytanı dışında kimse hesaplayamayacaktır. Şimdi ilk okul yıllarımızdan tanıdığımız parantez ile başlayalım.

1. Parantez

Muhterem dostum, koşullamalar mutlaka ama mutlaka çengelli parantez ile kullanılmalıdır. Parantezsiz if-else yapmak da mümkündür ama o kod su gibi akmaz, viskozitesi yüksek olur, katranlaşır. Bir kodun genişletilebilirliği, geliştirilebilirliği, parantez kullanılmadığında çok azalır. Ayrıca hata çıkma olasılığı da artar.

Kötü Örnek:

Şimdi bu kötü örnekte acaba kodu yazan kişi, USB’ye bağlanıldığında iki iş de yapılsın istemiş de hatalı mı yazmış, yoksa birinci fonksiyon koşula bağlı, diğeri her türlü yapılsın diye mi yazılmış belli değil. Belki de ilk aşamada koşulun içinde yalnızca bir fonksiyon vardı. Kodu güncelleyen kişi, “aaa bu da yapılsın” diyerek diğer satırı eklediğinde bir felaketle karşılaşacaktır.

İyi Örnek:

Su gibi kod. İki tanecik parantezle, koşulun sınırları çizildi, akan su yolunu bulmuş oldu.

2. Karşılaştırma Sırası

Hep söylüyorum, söylemeye de devam edeceğim ki, koşullamalarda en sık yapılan hatalardan biri de == yerine = yazmaktır. Bunun çözümü, yine söylüyorum basittir. Diyelim ki, beşer şaşar misali kodu yazarken bir hata yaptık if(deger == 3) diyecekken, if(deger = 3) dedik. Bu kod hata vermez ne yazık ki ve degere 3 atanır, sular sel olur evleri barkları yıkar. Ama alışkanlığımız if(3 == deger) yazmak ise, hata yapıp if(3=deger) yazsak bile derleyici bize hata verir. Bu da ev bark kurtarır. Tabi burada 3 sayısını da kullanmamak lazım. Magic number coding konusundan bahsetmiştik. Şimdi örnekleri verelim…

Kötü Örnek:

 İyi Örnek:

 3. Koşul Derinliği

Çok derin olan her mevzu gibi, derin koşullamalar da kötü olaylara delalet eder. İnsan beyni ortalama 7 derinliğe kadar ilişki saklayabiliyormuş, bundan fazlası kafa karıştırıyor ne yazık ki. Suyu bulandırmanın anlamı yok, böyle bir ilişki varsa bile olayı basitleştirmeye çalışmakta faideler var.

Kötü Örnek:

 İyi Örnek:

 4. Koşullamalarda Makro Kullanımı

Makrolarla koşullamaları yanyana kullanmak ateşle barutu yan yana tutmaya benzer. Çok çok dikkatli olmak gerekir. Makrolar’ın do, while ya da benzer bir yapıda kullanılmasında faideler vardır. Lafı çok uzatmadan iki örnekle açıklayacağım.

Kötü Örnek:

Bu kod, derleyici tarafından aşağıdaki şekilde genişletilir:

Ki bu yazım zaten dilin tabiatına yani doğanın kanunlarına aykırıdır. Bu durumda, görülen else, artık if’e ait değildir. Bu da öngörülemez felaketlere sebep olabilir. If’de parantez kullanılmaması da hatalara çanak tutmuştur.

İyi Örnek:

Yukarıdaki kod ise, parantezler konmasa bile çalışacaktır. Kodu genişlettiğimizde hiç bir hata oluşamayacağını görmek mümkündür. Fonksiyonel makroların, do while içinde tanımlanması, su gibi aziz bir alışkanlıktır.

 5. Parametre Dönüşümü

Bazen koşul, ister istemez karmaşık olur ve çok sayıda koşulun hikmetini çözmek hakikaten zordur. Bu durumda parametre dönüşümü yapmakta sayısız feyizler vardır. Bu parametreler toplanarak anlamlı tek bir parametreye aktarılabilmektedir.

Kötü Örnek:

 İyi Örnek:

Artık o kadar koşulun ne işin gerektiğini anlayabiliyoruz. Adam araba sürebilir mi süremez mi basitçe buna karar veriliyor.

 Burada anlaşılabilirlik uğruna bir byte’ın kurban edildiğinin bilincinde olmakta fayda var. Değer mi? Bence değer. 

Şimdi devam!

Önceki Sayfa   Sonraki Sayfa

 

Gömülü C – 7 : Fonksiyonlar ve Temiz Kodlama

İngilizce’de “clean coding” (temiz kodlama) denilen mevzu ile ilgi, cüz-i irademden dökülen bir takım fikir ve ilmi bilgileri,  siz değerli okurlarla paylaşacağım.

Temiz Fonksiyonlar

Malumunuz fonksiyonlar (methodlar) bir programın eli kolu değildir ama programın hayata dokunması sürecidir. Fonksiyonlar doğru kullanılmazsa, çok yanlış anlaşılabilir, büyük felaketleri doğurabilir.  Buradaki ibret doğal olarak fonksiyonlar için de aynen geçerlidir.

Bir program bir problemi çözmek üzere bir takım işler yapacağından, programda işi yapan, haliyle fonksiyonlardır. Bu sebepten ötürüdür ki fonksiyon isimlerinin ve tanımlamalarının iyi yapılması, programın işini hakkı ile yapmasında yadsınamaz bir paya sahiptir. Bir fonksiyon okunduğunda, okuyan kişinin beyninde bağlantı kuracağı bir şeyleri uzun süreler aramamalı, hemen gideceği yere gitmelidir. Bu da fonksiyonu yazanın pekala da yardımcı olabileceği bir konudur. Çiçek gibi kod yazmanın bazı basit, temel kuralları vardır. Bu kuralların bazıları biliniyor bazılarını ben uydurdum. Aklınıza yatmayanı zaten boş veriniz. Söz konusu kurallar şöyle:

  • Fonksiyonun adı, fonskiyonun sorumluluğunu ve söz konusu mevzuyu çok^5 temizlik seviyesinde açıklamalıdır. Temizlik, isimden başlamaktadır.
  • Fonksiyon, mümkünse tek bir iş yapmalıdır. Mümkün değilse bile fonksiyon tek bir iş yapmalıdır.
  • Fonksiyon çok uzun olamamalıdır. Bir fonksiyonu okurken afedersiniz skrol yapmak gerekiyorsa,  o fonksiyon hemen parçalanmalıdır.21″ monitörde tek fonksiyon içine skrol gerektirecek kadar kod yazmak hol değildir.
  • Fonksiyon pahalı bir olgudur. Bu sebeple fonksiyonun tekrar kullanılacağı düşünülmelidir.
  • Yazılan bir fonksiyon, makine için değil insan için yazılır. Fonksiyon makine üzerinde çalışacak diye bir tek makinenin anlayacağı şekilde yazmak kalite ve anlaşılabilirlik kaybına sebep olur. Kod çalışmak zorunda olduğundan makine zaten onu anlayacaktır. Asıl mesele, insanın anlayacağı şekilde kod yazmaktır.
  • Fonksiyon, dağıtılmadan önce doğrulanmalıdır ki kullanan kişilere aktarılmasının bir manası olsun.
  • Kolaya kaçılıp, afedersiniz kötü kod, yorum satırlarıyla sıvanmamalıdır. Yorum satırları geçici iken, fonksiyonun kendisi kalıcıdır. Madem ki yorum satırı yazılabiliyor, ondan önce fonksiyonun kendisi temiz yazılmalıdır. Kod yazarak yapılamayacak şeyler, yorum satırları ile yapılmaldır.
  • Bir fonksiyonun adında -meclisten dışarı- “ve(and), veya(and), oyleyse(then)” gibi kelimelerin geçmesi, o fonksiyonun birden fazla iş yaparak dünyayı kurtarmaya çalıştığını göstermektedir. Bu çok yanlıştır. Dünyayı kurtarmak  bir takım işidir. Bu fonksiyonlar derhal parçalanmalı ve temizlenmelidir.
  • Bir fonksiyonu her kullanan kişi, o fonksiyonun tepesindeki yorumları değil, bizzat ismini görecektir. Yorum satırını görebilmek için fonksiyonun implementasyonunu açmak gerekmektedir. Bu, orta çağdan kalma bir işkence yöntemidir ve yapılmamalıdır. Reçete olarak, temiz, yaptığı işi ismiyle anlatan fonksiyonların yazılması şifa verecektir.
  • Bir yazılımdaki tüm isimler, derleyici tarafından adres sembollerine çevirileceğinden, uzun ve derdi açıkça anlatan fonksiyon isimleri seçmek hiç kimseye zarar vermeyeceği gibi yazarın yaptığı işin kalitesini artıracaktır. Uzun isimli fonksiyon, kısa isimli fonksiyondan daha fazla bellek tüketmeyeceğinden eli korkak alıştırmanın lüzumu yoktur.

Maddeler bunlar. Şimdi kalem kalem bu başlığın önemli konularına değinelim.

Anlaşılabilirlik ve Modülarite

Hiç laf kalabalığı yapmadan, somut örneklerle mevzuya dalacağım. Zaten eski bir ahitte yazdığım bir örneği sizlerle bu perspektifte paylaşacağım.

Kötü Örnek:

Yukarıdaki rezil kodu hatırlarsınız. Bu tür kodlama yüzünden nice çok da olumlu yorulmlar alınmayacağı anlaşılmakta. Bir kere fonksiyonun adından “Ve” yazıyor. Bu fonksiyon dünyayı tek başına kurtarma gayretini, belki iyi niyetle ancak saf bir cehaletle göstermektedir. Cehalet kötüdür. Fonksiyonun adı hele iyice faciadır çünkü C dilini az çok bilen herkes bu fonksiyonun iki değeri toplayarak, bellekte bir yerlere yazdığını anlayabilecektir. Asıl soru bu işin neden, ne zaman, ne amaçla yapıldığıdır ki bu da gerçeğin altındaki hakikatin ta kendisidir.

Bir diğer şık olmayan davranış ise fonksiyon isminin Türkçe, yorumların ise Almanca + İngilizce olmasıdır. Bu yorumları kimse – belki İlber Ortaylı hariç – anlayamayacaktır (İlber Hoca da yorumları anlasa da kodu anlayamayacaktır). Dolayısıyla kodda tek bir insan dili tercih etmek daha hoş olacaktır.

Güzel Örnek:

Bu yukarıdaki kod da tanıdık. Ama ben kodu yeni görmüş gibi, “aaa ne güzel kod O_o” demek istiyorum. Paralel evrenin, bu kodu içeren, pek de ücrada kalmamış bu güzel köşesinde, kötü örnek kodunda yapılan işler bu defa çiçek gibi tertemiz bir şekilde, amme hizmeti bilinci ile yapılmıştır.  Kötü örnekteki kod derin kod olduğundan ne iş yaptığı bilinmemekte idi. Güzel örnekteki kod temiz kod olduğundan, kodun aslında ticaret işleri ile uğraştığı anlaşılmaktadır.

Gönderilecek veri serileştirilmek zorunda olduğundan, union kullanılarak +1 mühendislik puanı kazanılmıştır.

Değişken adları da mevzu hakkında çok sayıda açık bilgiyi, yazılım tutkunlarının hizmetine sunmaktadır. Bu kodu okuyunca u16BazUcretTL’nin TL cinsinden, 65536 TL’den küçük bir meblağ olduğu anlaşılmaktadır. Bu da, kodlamada şeffaflık ve temizliktir.

Kötü kod, kötülükle sıkı bağlar kurmuştur. Seri porttan veri gönderme işi er ya da geç başka yerlerde de kullanılacağından, kötü örnekteki yapıda kopyala yapıştır yapmak gerekcektir ki bu tekrar kullanılabilirlik kavramından çok uzaktır. Güzel örnekte ise bu işi yapan bir fonksiyon, ayrı bir bireydir ve tekrar kullanılabilir yapıdadır. Yani benzetme yapılacak olursa; hem dünyası, hem ahireti güzeldir.

Rekürsif Fonksiyonların Kullanımı

Mümkünse kullanmayalım efendim. Stack derinliği sabit olmayan rekürsif fonksiyonlar, gömülü sistemlerde kullanılmamalıdır. Zaten ters gidebilecek bir sürü şey varken neden bir şey daha ekleyelim ki?

Sayın muhterem… Eveeeet, bir programın daha sonuna geldiik.

Şimdi devam!

Önceki Sayfa   Sonraki Sayfa

 

Gömülü C – 6 : Lezzetli Dosya Formatı

Opratörlerden sonra, sizlere de bizlere de bir nefes aldıracak bir konuya değineyim istedim. Kafalarımızı dinlendirmek için araya güzel bir konu sıkıştırıyorum. C dilinde yazılacak bir yazılım için dosya formatı nasıl olursa yakışıklı olur ona değineceğim. Malumunuz C’de, terbiyemizi takınarak yazdığımız yazılımlarda, uzantısına göre iki çeşit dosya oluyor: c ve h dosyaları. Bunların adam akıllı organizasyonu, seneler içinde edinilen bilgi ve tecrübeler vesilesiyle, ibretlik bilgiler içeren bir hal alıyor. Ben de bu bilgiyi burada sizlere aktarmaya çalışacağım.

Her yiğidin bir yoğurt yiyişi vardır demişler. Dosya formatı mevsusu da bu mevzuya epeyce benzer. Her yiğit yoğurdu başka yiyebilir ama güzel yoğurt her zaman güzel yoğurttur. Ben de güzel bir yoğurdu, kendi tarzımla sizlere sunacağım. Öyleyse haydi başlayalım!

Not:Bu vesileyle bu yazının temasının yoğurt olduğunu bilgilerinize arz etmek isterim.

Source (Kaynak) Dosyaları

  • Kaynak dosyalarının uzantısı: *.c olmalıdır. Örneğin yogurt.c
  • Dosya isminde Türkçe karakter kullanmazsanız, yoğurdunuz ekşimez.
  • Dosya adları açık ve anlaşılabilir olmalıdır. Dosya adını kısa yazmanın Murphy dışında kimseye bir faydası yoktur.
  • Dosya adı küçük harflerle başlamalıdır. Örneğin YaRaLiKaRiZmA34.c gibi ergen isimlendirmeleri ile yapılan yoğurttan cacık bile yapılmaz.
  • Dosyanın uzantısı da küçük c olamalıdır. ayran.C gibi isimlendirmeler, cehalete delalet eder. Yapmamak lazım, çirkinleşmeye hiç gerek yok.
  • Yerel, lokal, private(umumi olmayan) elemanlar statik tanımlanmalıdır.
  • Extern sözcüğü, yoğurttaki zararlı bir bakteri çeşididir. Onsuz da güzel yoğurt yapılabilir.

Çok güzel ahkam kestim ama farkındayım ki vakit, lafa karnı tok olan siz değerli okuyucuma lezzetli bir yoğurt sunma vaktidir.

Sevgili okuyucu, yukarıdaki kod şablonunu ingilizce yazdım ama lütfen kırılmayın darılmayın. İngilizce bilmiyorsanız bile çeviri ile anlamlarını öğrenmek çok çok kolay. Öyle kompleks şeyler yazmadım. Önceden dediğim gibi bir yazılımı yazarken tek bir insan dilinde yazmak lazım. Ben, terminoloji İngilizce olduğundan ve profesyonel hayatımda da kodları İngilizce istediklerinden formatı İngilizce olarak paylaşıyorum. Dana yoğurdu deyip Angus yoğurdu göstermiş olabilirim ama beni anlayacağınızı biliyorum. Kızmayın, darılmayın, trip atmayın.

Yukarıdaki formatı incelediğimizde, formatın sayısız ibretler içerdiğini görebilirsiniz. Bir kere başlıklar filan bir ilginç. Bazılarınıza bu güzel yoğurt tanıdık geldiyse dilinizin ucundaki sözcüğü ben söyleyeyim. DOXYGEN!!! Kanlıca yoğurdunu, pudra şekeri olmadan yemek nasıl tarihi bir hata ise, bir dosyadaki yorumları DOXYGEN’i sallamadan yapmak da o derecede talihsiz bir hatadır. Nitekim Doxygen ile yazan bir üstad’ın yoğurdu, camış yoğurdu gibi taş gibi olacaktır. Kodu yazan bu büyük üstad, kodun dökümantasyonunu da aradan çıkarmış olacaktır. Şimdi ben Doxygen dedim ama burada onu anlatmayacağım. Çünkü tonla kaynak var ve öğrenmek çok kolay. Sadece haberiniz yoksa, duyduysanız ama çok önemsemediyseniz diye bir değineyim istedim.

Yukarıdaki define satırı da aslında yoğurttaki zararsız bir bakteri gibi görünüyor ama aslında bu zararsız bakteri, sayısız hastalığa iyi gelir. Eğer modüler bir tasarım yaptıysanız her bir dosyayı bir mini modül gibi düşünmek isteyebilirsiniz. Bu yukarıdaki define size, bir modül projenizde var mı yok mu anlama imkanı sunar. Başka bir kaynak dosyasında #ifdef SAMPLE_C diyerekten o varsa şunu yap, yoksa da yoğurt yapmaktan vazgeçme bunu yap diyebilirsiniz. Bu gibi nice ibret, bu yoğurttaki bu bakterinin, faydalı bir bakteri olduğunu bizlere göstermektedir.

Devam edecek olursak, defines & macros kısmı görürüz. Bi dakka! Define ifadelerinin h dosyasında olması gerekmez mi? Evet ne yazık ki yanlışlıkla koymuşum onu C dosyasına demeyeceğim çünkü bunun da ibretlik bir sebebi var. bazı makrolar ya da define’lar, diğer modülleri de ilgilendirecek onların da işine yarayebilecek şeyler olduklarından, umuma hizmet açısından bu define’lar h dosyasına konur. Ama bazı makrolar, define ifadeleri de vardır ki, onlar yiğidin yatak odası gibidir, dışarı açılmaları hoş olmaz (?). Neyse konuyu saptırmayayım, tıpkı değişkenler ve fonksiyonlar gibi, makroları da dışarıya açmamayı seçebilirsiniz, bu durumda bu makrolar C dosyasında tanımlanır.  Mevzuyu daha da uzatmayacağım, anladınız siz onu.

Dosyanın geri kalanını çok detaylı açıklamayacağım. Static olan fonksiyonlar umuma açık olmayan fonksiyonlar oluyor. Yoğurdu yemek için içindeki bakterilerle tanışıp çay partisi yapmaya gerek yok. Static olan bu fonksiyonlar, modülün iç meseleleri. Static olmayanlarsa umuma hizmet olarak, dışarıya açılan elemanlar. Bunlar da yoğurdun kaymağı. Static olmayan fonksiyonların prototipleri, h dosyasında yer alacak. Fonksiyon prototipini yazmak, derleyiciye “bak haberin olsun bro, böyle bi fonksiyon var sonra duymadım etmedim deme” demek olduğundan, umuma duyurulacak fonksiyonların prototipleri doğal olarak h dosyasında yer alır. Static fonksiyonların prototipleri de c dosyasında yer alır. Static fonksiyon prototipinin h dosyasında yer aldığı durum da vardır ve ibretlik bir durumdur ama çok çok istisnai olduğundan onu yok saymak hakkımızdır. Nedenleriniz çok kuvvetli değilse yapmayın etmeyin. Kanlıca yoğurdu gibi dosya formatı var. Monalisa tablosunun kopyası gibi, al evine duvarına as.

Header (Başlık) Dosyası

  • Başlık dosyalarının uzantısı: *.h olmalıdır. Örneğin yogurt.h
  • Kaynak dosyalarının isimlendirilmesindeki maddeler, 2. maddeden itibaren aynen geçerlidir.

Yukarıdaki başlık dosyasının hikmetlerinin bir çoğunu artık anlayabiliyorsunuz. Bir tek aşağıdaki kısmı açıklayayım:

Bazen, dostlar; bir kaynak dosyası istemeden de olsa aynı header dosyasını iki defa çağırabilir. Örneğin bizim kaynak dosyamız olan cacik.c, yogurt.h ve hiyar.h’ı include etsin. Eğer hem yogurt.h hem de hiyar.h dosyaları içerde yemek.h dosyasını include ediyorsa, bizim cacik.c aslında yemek.h dosyasını iki defa include etmiş olur. Bu da çok tatsız bir yoğurt yiyiş şeklidir ve besin zehirlenmesine sebep olabilir. Bunu engellemek için yukarıdaki ifdef yapısı kullanılır. Bu durumda header dosyası iki defa çağırılsa dahi ilk çağırmada defined olmayan H_SAMPLE, defined olacağından bir daha bu header dosyasının içine girilmez yani aslında bu header iki defa include edilemez. Bu da bir yiğide yakışan bir yoğurt yeme şeklidir.

Bendeniz, zat-ı alilerinize, dosya formatları hususundaki bakışımı aktardım. Artık bunu yorumlamak veya ilerletmek sizlere kalmış. Yazıyı okuduğunuz için teşekkürler 🙂

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

 

Gömülü C – 5 : Operatörler

Önceki yazılarda, bir C programı hakkında feyizli bir takım konular üzerinde konuşmuştuk. Merhaba dünya yazılımının anatomisinden sonra ibretlik veri tiplerini, değişkenler ile onlara atanacak sabitleri, ardından da alımlı kodun olmazsa olmazı yorum satırlarını incelemiştik. Bu defa ise, C dilindeki bir diğer önemli başlık olan operatörleri inceleyeceğiz.

Dünyada çeşit çeşit operatör vardır ancak ne mutlu bize ki C dilindeki operatörler çeşitleri sınırlı sayıda. 🙂 Neyse, “operatörlere özgü bir yazı” olması, ilk bakışta biraz garip guraba gelebilir ama, operatörler özellikle gömülü programlamada “çok bilinçli şekilde kullanılmak zorunda” olduğundan değer verilmesi gereken önemli bir konu. Bizim de niyetimiz operatörlerin hak ettiği saygınlığı kazanmasına yardımcı olabilmek.

Bazen aynı amaç, farklı operatörler ile gerçeklenebilir. Hatta genelde durum böyledir. Bu aşamada, hangisini seçmenin daha akıllıca olacağına karar vermenizi sağlayacak birtakım ince noktalara burada değinmeye çalışacağım. Operatörlerin doğru kullanımında dahi bazı kazan/kaybet (trade-off) durumları olduğundan, yine ibretlerle dolu bir yazının şanlı başlangıcında olduğumuzu hissediyorum. Haydi hayırlısı…

Aritmetik Operatörler

C dilinde aritmetik operatörler, bizleri çocukluğumuza, ilkokul sıralarına götürür 🙂

Not: Operatörlerin uygulandığı değişken veya sabitlere genel olarak operand denebilir. A=10, B=20 için;

Operatör Tanım Örnek
+ iki operandı toplar A + B = 30
Soldaki operanddan sağdaki operandı çıkarır. A – B = -10
* operandları çarpar A * B = 200
/ soldaki operandı sağdaki operanda böler B / A = 2
% Mod operatörü, bölmeden sonra kalanı verir B % A = 0
++ Bir artırma operatörü A++ = 11
Bir azaltma operatörü A– = 9

Aritmetik operatörleri etkin şekilde öğrenmenin en iyi yolu, bu operatörleri kullanarak programlar yazmak ve bunları çalıştırarak sonuçları görmektir. Benim derdim, operatörlerin ne iş yaptığını anlatmak olmamalı diye düşünüyorum. Herhangi bir C dersinde/kitabında bu zaten alenen anlatılıyor. Aşağıdaki örnek yazılımı yazmamın ardından asıl anlatılması gerken ince noktalara geçeceğiz.

Yukarıdaki kod çalıştırıldığında, çıktısı aşağıdaki gibi olur:

Toplama: 30.000000 Cikarma : -10.000000 Carpma: 200.000000 Bolme: 0.500000 Mod: 10
++ sonra kullanilinca i:30
bir sonraki adimda i:31
++ once kullanilinca i:31
bir sonraki adimda i:31

Önemli bir nokta olarak i++ yazdırıldığında, önce i’nin değerinin yazdırılıp ardından bir artırıldığında dikkat ediniz. Aynı şekilde ++i yazdırıldığında, önce i’nin değerinin bir artırıldığında ardından bu değerin yazdırıldığına çok dikkat ediniz. İnananlar için burada büyük ibretler var. ++ opratörünü bu bilinçle kullanmakta faydalar var.

Aritmetik operatörlerle ilgili önemli bir diğer nokta da öncelik sırası kavramı. Öncelik sırasına takılmamak ve kodu okunur kılmak adına, ardışıl aritmetik işlemlerde parantez kullanaraktan sorunu teğet geçmekte fayda var. Örneğin 13+227-12/3 ile ne demek istediğimi anlayamayabilirsiniz, demek istediğimi yanlış diyor da olabilirim ama 12+(227)-(12/3) ile ne demek istediğimiz çok açık olur. C dilinde malum her zaman parantezin içi öncelikli oluyor. Hem hataları önlemek için hem de toplum içerisinde anlaşılabilir olmak için bu alışkanlığı edinmekte faiderler, feyizler var.

Aritmetik operatörlerle ilgili bir diğer önemli nokta da, yerli yersiz kullanılmamaları. Kimi zaman (hatta çoğu zaman), bir işi yapmanın birden fazla yolu oluyor. Örneğin 8 bitlik iki sayıyı alıp yanyana koyup 16 bitlik tek bir sayı elde etmek istediğimiz yerler oluyor. Bunu yapmak aşağıdaki şekillerde mümkün:

Bazı platformlarda toplama, çarpma gibi aritmetik işlemler atomik değildir, yani bu operatörlerin işletilmesi 1 saat darbesi süresinden (1 cycle) fazla sürer. Halbuki her işlemcide AND,OR işlemleri tek cycle’da yapılır. Buna göre eğer yukarıdaki kodu MSP430 serisi mikrokontrolörler için yazıyorsanız, yöntem 0 sizi canınızdan bezdirir çünkü eğer HW multiplicator olmayan bir modelde çarpma 130 cycle kadar sürer, HW multiplicator olan bir modelde bile çarpma 8 cycle sürer. Daha bunun toplaması ve ataması da var. Halbuki Yöntem 3’te tüm olay hepi topu 3 cycle’da biter. Yöntem 4’ün ise çok sanatsal bir amacı var, umarım anlamışsınızdır ancak anlamadıysanız dahi ileride detaylıca anlatacağım zaten.

Neyse demek ki yerli yersiz aritmetik operatör kullanmamak lazımmış. Gerçi Arm Cortex M3 mimarisinde aritmetik operatörler de 1 cycle sürüyor (non-fp için). Orada elinizi korkak alıştırmayabilirsiniz ama yine de gün gelir o kodu 8 bitlik bir mikrokontrolöre taşımanız gerekirse başınız ağrır. En güzeli, en baştan çiçek gibi bilinçle yazmak 🙂

İlişkisel Operatörler/ Karşılaştırma Operatörleri

İlişkisel operatörler sağ ve solundaki operandlar belirli bir mantıksal ilişkiye göre karşılaştırılır ve eğer ilişki doğru ise doğru döndürülür, yanlışsa yanlış. 😀

Operatör Tanım Örnek
== İki operand birbirine eşitse, sonuç doğru(true,1) olur (A == B)
!= İki operand birbirine eşit değilse, sonuç doğru(true,1) olur (A != B)
> Soldaki operand sağdakinden büyük ise, sonuç doğru(true,1) olur (A > B)
< Soldaki operand sağdakinden küçük ise, sonuç doğru(true,1) olur (A < B)
>= Soldaki operand sağdakinden büyük ya da eşit ise, sonuç doğru(true,1) olur (A >= B)
<= Soldaki operand sağdakinden küçük ya da eşit ise, sonuç doğru(true,1) olur (A <= B)

Bu kısımla ilgili verilecek ibretlik mesajı önceki kısımlarda vermiştik. Ancak tekrar bir hatırlatma yapmak gerekirse;karşılaştırma operatörleri genellikle if,while gibi koşul/döngü işlemlerinde kullanılır ve doğal olarak en çok hata da buralarda yapılır. Örneğin sık yapılan hatalardan biri if(age == 18)  yazacakken yanlışlıkla if(age=18) yazmaktır. Bu durumda age değişkenini 18 ile karşılaştırmak yerine, age değişkenine 18 atamış ve dönüş olarak true almış olursunuz. If içinde atama yapmak mümkün olduğundan derleyici buna hata vermez, uyarı verebilir. Bu hatayı hiç yapmamanın yolu if(18 == age) formatını kullanmaktır. Yani sabiti sola, değişkeni sağa almaktır. Bu durumda yanlışlıkla if(18 = age) yazsanız bile, derleyici size sabite değişken atanmaz diye hata verir. Bu arada 18 yazdım ama, örnek anlaşılsın diyerekten sayıyı doğrudan yazdım 🙂 Yoksa MAGIC_NUMBER kullanmak yok 😉 Önerdiğim formatta yazacak olursam eğer if(ageOfDriverCandidate == AGE_LIMIT_TO_DRIVE) yerine if(AGE_LIMIT_TO_DRIVE == ageOfDriverCandidate) yazmakta fayda var. Ya da bu hususta sık hata yapıyorsanız, bir makro yaratarak, ve onu bir kez test ederek bu sorunu tarihin tozlu sayfalarına gömebilirsiniz :

 

Mantıksal Operatörler

Bazen birden fazla koşul aynı anda değerlendirilmek istenebilir. Bu durumda mantıksal ilişkiye göre (ve,veya,değil) koşullar değerlendirilebilir.

Operatör Tanım Örnek
&& Mantıksal VE(AND) operatörü çağırılır. Eğer iki taraf da sıfırdan farklı ise, sonuç doğru(true) olur. (A && B)
|| Mantıksal VEYA(OR) operatörü çağırılır. Eğer iki taraftan herhangi biri sıfırdan farklı ise, sonuç doğru(true) olur. (A || B)
! Mantıksal DEĞİL(NOT) operatörü çağırılır. Operandın mantıksal tersi alınır; operand 1 ise sonuç 0, operand 0 ise sonuç 1 olur. !(A && B)

Mantıksal operatörler birleştirilerek kullanılabilir. Ancak insan aklının almayacağı uzunlukta bir koşul söz konusu ise, o koşul ya indirgenmeli, ya da yorum satırları ile açıklanmalıdır. İnsan aklı derinlik olarak ortalama 7 seviye ilişkiye kadar hafızada tutabilir. Fazlası olmaz 🙂 if içinde 9 tane koşul yazmamakta fayda var.

Bitsel/İkili (Bitwise/Binary) Operatörler

Esasen bitsel operatörlerin ciğerini BURADA, çiçek gibi açıkladık. Hatta elektronik olarak nasıl gerçeklendiğini de konuştuk. Ancak tabi ki olayı bir de yazılım perspektifinde incelemekte sayısız feyizler var. Zira gömülü sistemlerde kimi zaman bir bitin bile hesabı yapılır. Hesabının yapılmadığı yerde bile, kullanılan mikrokontrolörün bir registerındaki bir biti 1 ya da 0’a çekmek için bitsel operatörler kullanılabilir. Bu söylediğim ilk aşamada bir anlam ifade etmemiş olabilir, ancak ileride değineceğimiz üzere mikrokontolördeki donanımı kullanabilmek için, genişliği genelde işlemcinin adres yolu genişliği kadar olan (8/16/32 bit), ayırılmış özel bellek alanlarına (register) bir takım veriler yazmak gerekir. Belleği verimli kullanmak için bir register içindeki her bitin ayrı ayrı işlevi olur. Bu nedenle registerlara bitsel olarak erişmek gerekir. Burada da genellikle bitsel operatörler kullanılır. Yani mevzu, o kadar önemli ki, ateş ediyor =)

Misal A = 60; ve B = 13; olsun. Bunların ikili tabanda gösterimi şöyle olur:

A = 0011 1100

B = 0000 1101

Buna göre

A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001

~A  = 1100 0011

Operatör Tanım Örnek
& Bitsel VE Operatörü (A & B) = 12, yani 0000 1100
| Bitsel VEYA Operatörü (A | B) = 61, yani  0011 1101
^ Bitsel XOR Operatörü (A ^ B) = 49, yani  0011 0001
~ Tümleyen operatörü. Operandın 1 olan bitlerini 0, 0 olan bitlerini de 1 yapar. (~A ) = -61, yani  1100 0011
<< Sola kaydırma operatörü. Soldaki operandın bitleri, sağ operand kadar sola kaydırılır ve her kaydırmada en sola 0 eklenir. (Karışık oldu, örnek üzerinden görmekte fayda var) A << 2 = 240 yani 1111 0000
>> Sağa kaydırma operatörü. Soldaki operandın bitleri, sağ operand kadar sağa kaydırılır ve her kaydırmada en sağa 0 eklenir.(Karışık oldu, örnek üzerinden görmekte fayda var) A >> 2 = 15 yani 0000 1111

Şimdi gelelim fasülyenin faydalarına 🙂 Yani her yerde yazmayan önemli bilgilere…

& (VE) operatörü ile maskeleme yapılarak bir sayının 1 olan bitlerini 0’a çekmek yani temizlemek mümkündür. Misal:

 

| (VEYA) operatörü ile maskeleme yapılarak bir sayının 0 olan bitlerini 1’e çekmek yani set etmek! (kurmak) mümkündür. Misal:

^ (XOR) operatörü candır! Bu operatör ile maskeleme yaparak bir sayının herhangi bir bitini (ya da bitlerini) terslemek mümkündür.

Ek olarak kaydırma operatörünün, cyclic olup olmadığı her platformda mutlaka test edilmelidir. Normalde cyclic olmaz, ama test etmekte fayda var =)

Yazdığım örneklerde zaten kaydırma operatörünün kullanımı da var. Bir de ^=, +=, |= gibi örneklerden atama operatörünün diğer bitsel ve aritmetik operatörlerle birleştirilebildiğini de göstermiş olduk diye düşünüyorum.

Bu gün bu yazımı Mustafa Keser üstadımızın sözü ile sonlandırıyorum.

“Eveeeeet, bir proğramın daha sonuna geldiiik. Tekrar görüşmek üzere efeem.”

 

Önceki Sayfa   Sonraki Sayfa

 

Gömülü C – 4 : Sabitler ve Yorum Satırları

Önceki yazıda veri tipleri, değişkenler ve isimlendirmeden bahsetmiştim. Bu yazıda yorum satırlarından ve sabitlerden kısaca bahsedeceğim.

NŞA’da sabitler ve yorumlarla ilgili bahsedilebilecek şeyleri zaten okuduğunuzu / bildiğinizi varsayıyorum. Nitekim herhangi bir C kitabında ya da tutorial’inde mutlaka bir takım temel şeyler yer alıyor. Bu kısımda yine birazcık daha fazlasına değinmeye çalışacağım 🙂

YORUMLAR

C dilinde yorum satırları iki şekilde yazılır. Her iki duruma da örnek olarak aşağıdaki kod parçası örnek gösterilebilir:

Gördüğünüz gibi tek satırlık bir yorum yazmak için “//yorum”, çok satırlı yorum yazmak için ise “/* yorum */” kullanılır. Yıldızlı olan blok yorum esasen C++’da olan bir özellik olup sonradan faideleri görülüp C’ye de aktarılmıştır. Yorumun program için açıklayıcı olmasına dikkat edilmelidir.

SABİTLER

C dilinde yaratılan değişkenler, bellekte bir adreste saklanırlar. Bu değişkenlerin saklanış şekillerini bir önceki yazıda incelemiştik. Bu değişkenleri yaratırken, değişkenlerin bir işe yaramasını istiyorsak, değişkenlerin değer alması mecburi olacaktır. İşte bu değer atamasında, bir değişkeni başka bir değişkene atayabileceğimiz gibi, bir değişkene sabit bir değer de atayabiliriz. Misal,

Değişkene sabit değer ataması yapılırken, sabitin boyutunun değişkenin boyutu ile uyumlu olmasına dikkat etmek gerekir. Misal 8 bitlik bir değişkenin alabileceği azami değer 255 iken ona gidip 65372 gibi bir değeri atamaya çalışmamak gerekir. Ama oldu ki atadık, ne olacağını da bilmemiz gerekir. Bu durumda değişkene 65372’nin 255 modundaki değeri atanır. Bu da değerin bellekteki yerleşiminin düşük öncelikli byte’ı demektir. Tabi bu derleyiciden derleyiciye değişir.

AĞZIMDAKİ BAKLA

Şimdi gelelim asıl mevzuya… Esasen yorum satırları yazılım dünyasında büyük tartışmalara sebep olmuştur. Hatta benim de bir mensubu olduğum “birileri” yorum satırlarının mecburi durumlar dışında kullanılmaması gerektiği, gerektiği yerde de kullanılmamasının hata olduğu yönünde görüş bildirmiştir. Okulda ders aldığım hocamın da mensubu olduğu “birileri” ise, yorumsuz kodu koddan saymamış, reddetmiş, sıfır vermiş, onu hakir görmüştür.

Şimdi mevzu aslında yorum olup olmaması değil bence. Her ne kadar bu konuda bir taraf olsam da, görüşümü detaylıca açıklayacağım. Sonrasında yolunuzu seçmek üzere karar size ait 🙂 Ben derim ki, bir yazılım, baştan iyi tasarlanmış olmalıdır, iyi implemente edilmiş olmalıdır, iyi yazılmış olmalıdır. Yani kod öyle yazılmalıdır ki, ne iş yaptığını zaten anlatmalıdır. Bunu sadece ben de söylemiyorum “self-descriptive” yani kendini ifade eden kodlama, bir yazılım kalitesi unsuru olarak literatürde bakidir. Sonuçta C,C++,Java ne olursa olsun söz konusu araç bir dildir ve dilin kuralları herkes için ortaktır. int i=5 derken, dili bilen herkes i diye bir değişkenin, int cinsinden yaratıldığını ve ona 5 değerinin atandığını bilir. Sorun, bu 5 değerinin ne olduğudur ve i’ye niye atandığıdır. i’nin neden int olduğu ve neden 5’in ona atandığı da diğer sorular tabi ki. Şimdi kimisi der ki ben pis pis kodumu yazarım, üstünü de yorumla sıvarım. Nasıl mı? Misal:

Şimdi bu kodu yazan adamın arkasından nasıl konuşulur? Şöyle… Kardeş, kafan çok güzelmiş kodu da öyle yazmışsın, zahmet edip yorum da yapmışsın ama bu dizi kim? 255 dizinin neden son indeksi? Madem bu değişken bir dizinin son indeksi, değişkenin adını felancaDizininSonIndeksi filan diye koysan ne olurdu? Madem bu dizinin son indeksi 255, sen o bilgiyi başka bir yerde de kullanacaksın ve yine ne olduğunu kimse bilmeyecek. O bilgiyi etiketlesen canın mı çıkardı?

Ve nihayetinde o bildiğiniz acı son ile kodu yazan kişinin arkasından bir güzel dualar edilirdi.Bir de aşağıdaki satırı yazan kodcu var tabi…

Önce hiç bir şey denmez, çünkü herşeyi anlamış olmanın verdiği aydınlanma hissi bedeni kaplar. Yani neticede bir kodda bir sürü yerde 255 değerinde bir şeyler olabilir ama hangisinin ne anlama geldiğini anlatmak etiketler olmadan çok zordur. Etiket dediğim de #define ile yazdığım şey. Bu sayede kodda 25 geçtiği zaman o 25’in oda sıcaklığı mı yoksa, amerikanın bir eyaletindeki ehliyet alma yaşı mı olduğunu anlayabiliriz. Bu olaya “MAGIC NUMBER CODING” deniyor. Kodda nereden peydah olduğunu bilmediğimiz sabit değerler olmamalı 🙂 Yazılımda zaten mevzuyu anlamak zor, bir de yorumlarla sabitler doğru kullanılmadı mı olay epey işgenceye dönüyor doğrusu.

Şimdi bir diğer önemli konu da veri ve bilgi kavramlarının farklılığı.Programlamada birbirine en çok karıştırılan kavramlardan biri de bilgi ve veri kavramlarıdır. Eee haliyle çok sayıda yanlışlık da yapılıyor. Örneğin bir 3. sınıf öğrencisininn ‘A’ sınıfında olması bir bilgidir. O bilgi, sayısal karşılığı 0x41 yani 65 olan veri ile ifade edilebilir. Buradaki 65 sayısı, konu içeriği bilinmeyen bir veridir. Keza ülkemizde erkekler için emeklilik yaşı da 65’tir. Yani bu bilgi de ‘A’ ile aynı şekilde bellekte saklanmaktadır. Aaaaaa?

i=’A’, i=65, i=0x41;

Bunların hepsi aynı değerde olan atamalar. Demek ki biz söz konusu içeriği farklı şekilde sunsak, anlamlandırsak da (bilgi), bu anlamlandırmanın bellekteki yerleşimi aynı olabiliyormuş. Biz sadece okuyan adam daha iyi anlasın diye sınıfı gösteren değişkene ‘A’ verisini(değerini) atadık, emeklilik yaşına da 65’i atadık. Bu yaklaşımın değişkenin adını düzgün koymaktan bir adım öteye gittiğini lütfen kaçırmayalım. Sonuçta her iki durumda da derleyici bu ifadeleri 65 sayısına çevirecek ve bu veriyi bellek gözüne yazacak. Demek ki neymiş? Sabit’i atarken bile o bilgiyi ifade etme şeklimiz, iyi kod yazmak için, anlaşılabilir olmak için çok çok önemliymiş. Buradan çıkarılacak sayısız ibretler var. Parçaları birleştirecek olursak aslında ‘A’,65 gibi sayıları kodun çeşitli yerlerinde tekrar tekrar yazacağımıza, #define ile etiket oluşturup o etiketi kullanmakta büyük feyizler var.

Bu konunun bir örneği de printf ile verilebilir. Gömülü sistem yazılımcıları, çok pahalı bir fonksiyon olabildiği için printf’e karşı çok mesafeli olsa da, konsepti anlatabilmek adına örneğimizi onun üzerinden vermemiz mümkün.

Bu kodu çalıştırdığınızda her seferinde aynı veriyi yazdırmanıza rağmen, sırayla ‘A’, 65 ve 41 görürsünüz. Bu bellekteki verinin bilgiye dönüşümüdür. Üzerinde düşünüldüğünde burada da sayısız ibretler vardır.

Neticede ağzımdaki baklayı çıkardım. Yorum satırlarının kullanımı ile ilgili felsefemi aktardım. Karar sizin. Kararınıza sadık kalınız.Ardından sabitlerle ilgili çok önemli olduğunu düşündüğüm kavramları aktarmaya çalıştım. Umuyorum bir şeyler katmıştır, şimdi devam 🙂

Önceki Sayfa   Sonraki Sayfa

Gömülü C – 3 : Veri Tipleri, Değişkenler ve İsimlendirme

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.Martin Fowler

Gömülü sistemler yazı dizisinin güzide yazılarından bir olan “Merhaba Dünya” yazısından sonra geldik artık veri tipleri ve değişkenlere. Tekrara düşmek manasız olduğundan sürekli söylemek istemesem de, bu yazı da bilinenlere referans vererek önemli başka konuları içerecek. Yani herhangi bir tutorial‘deki gibi bir bilgi seti beklemeyiniz 🙂

Şimdi malumunuz genelde bir program, bir problemi çözmek için yazılır. Problem demişken mevzu ille de negatif olacak diye bir şey yok tabi. Sadizmi de bir kenara koyuyorum. Anlatmak istediğim şu ki her programın bir amacı var. Bu amaç belli nedenlerle tetiklenir ki buna da problem diyelim. Her program, problemi farklı bakış açılarıyla modeller. C bunu yapısal bakış açısıyla modellemeye uygundur, C++ nesne yönelimli modellemeye uygundur, SequenceL misal fonksiyonel modellemeye uygundur falan filan.

Bu demek değil ki, C ile nesne yönelimli modelleme yapamayız, C++ ile yapısal modelleme yapamayız. Yaparız de bazen lezzetli olmaz. Bazen de çiçek gibi olur; evine, iş yerine koy seyret.

Neyse konuya dönecek olursak her program mantıken bir problemi ama öyle ama böyle modeller. İş o ki, programlama yapan adam bu modellemeyi kaliteli yapsın. Gel gelelim bu iş, hakikaten zor iş. Niye? Çünkü çok fazla parametreyi düşünmek gerekiyor ve bu parametrelerden bazıları birbirleriyle çatışıyor. Misal kodu daha efektif yapmak çoğu zaman anlaşılırlığı azaltır. Taşınabilirliği artırmak için kodun boyutu artar falan fişman. Madem bu kadar çok parametre var, nasıl state of art yazılım yapacağız? Hem de gömülü yazılım? Cevabı kısaca şu: güzel pratikleri takip ederek ve trade-off’ların (bir şeyi kazanmak için bir şeylerden fedakarlık etmeyi gerektiren durumların) FARKINDA olarak.

Sözün kısası yazının bu kısmında, güzel pratiklerin bir nevi besmelesi anlatılacak. Hemen her problemin modellenmesinde değişkenler kullanılır. Değişkenleri doğru kullanmak ve doğru isimlendirmek, anlaşılırlığa çok büyük katkı sağlıyor. Pek bir şeye de zarar vermiyor. Yani değişkenleri doğru kullanıp, doğru adlandırdığımızda hiç bir şey kaybetmeden bayağı bir kâra geçiyoruz. Bolca sevap kazanıyoruz 🙂

 

C dili için genelde şöyle bir tablo yapılır:

Tip Boyut(inş cnm yha) Değer aralığı(inş cnm yha)
char 1 byte -128 to 127 or 0 to 255
unsigned char 1 byte 0 to 255
signed char 1 byte -128 to 127
int 2 ya da 4 byte -32,768 to 32,767 or -2,147,483,648 to 2,147,483,647
unsigned int 2 ya da 4 byte 0 to 65,535 or 0 to 4,294,967,295
short 2 byte -32,768 to 32,767
unsigned short 2 byte 0 to 65,535
long 4 byte -2,147,483,648 to 2,147,483,647
unsigned long 4 byte 0 to 4,294,967,295

Yukarıdaki tablo şerdir, güvenilmezdir, kemdir. Zaten boyut kısmında inş cnm yazmaktadır.

Bu görmüş olduğunuz tipler C dilinin temel tipleri malumunuz. Şimdi size bir önerim olacak. Bu veri tiplerini gömülü sistemlerde ASLA KULLANMAYIN. Aklınızdan iki soru geçti hemen… Birincisi “niye?”, ikincisi “ne kullancaz o zaman?”. İkisine de nedenleriyle cevap vermeye çalışacağım.

Niye?

Çünkü standard veri tiplerinin boyutu platform bağımlıdır. Ne demek istiyorum? Örneğin X mikrokontrolöründe int veri tipi 2 byte iken, Y mikrokontrolöründe int veri tipi 4 byte olabilir. Yine Z mikrokontrolöründe int veri tipi 8 byte olabilir ve eşşeğin kulağına su kaçırabilir. Örneğin sizin bilgisayarlarınızda muhtemelen int 8 byte olacaktır. Bunun sebebi işlemcinin adres yolunun genişliğinin platformdan platforma farklı olabilmesidir. Bu kavram “void*” veri tipinde daha detaylı anlatılacaktır. Ancak şu söylenebilir ki 32-bit’lik platformlarda int veri tipi 32 bit yer kaplar ki bu da malumunuz 4 byte’a tekabül eder. Benzer şekilde 64 bitlik sistemlerde int 8 byte yer kaplar. Tabi bunlar, normalde olması gereken durumdur, derleyici; Ali Cengiz oyunları ile burada başka işler yapabilir. Sözün kısası ölçmeden veri tiplerinin boyutunu bilemezsniz. Bu sebeple int, long, short gibi değişkenler “platform safe (platform-ül emin)” değildir. Peki efendim nasıl ölçücez?

Ahanda böyle:

İsterseniz kodu BURADAN da derleyip çalıştırabilirsiniz.

Neyse derdimizi anlattık. Bu arada bir değişkenin limitlerini bilmiyorsanız yardımcı kütüpanelerle onlara da ulaşabilirsiniz. Misal float için şöyle işler de yapılabilir:

Gömülü sistemlerde, belleğin bilinçli şekilde kullanılabilmesi çok önemlidir. Programın bellek kullanımının platformdan platforma değişmemesi ise, bir çok hatanın önüne geçmemizi sağlar. Örneğin iki adet gömülü sistemin haberleştiğini düşünün. Birinde(X) int 32 bit olsun diğerinde(Y) ise 64 bit olsun. X’den Y’ye olan haberleşmede aynı kodları kullanmak istediğimizde Y’de daha az değer okumaya başlarız. Aynı sayıda değer okumaya kalkarsak da yanlış değer okuruz. Keza X’de bir struct’ı olduğu gibi sd carda yazarsak, ve Y’de o sd cardı aynı struct ile okumaya kalkarsak, eğer struct; int, float gibi platform safe olmayan değişkenler içerirse verileri yanlış okuruz. Misal aşağıdaki struct’ın kaç byte yer kaplayacağını soralım:

Kaç byte kaplar? Cevap aşağıda…

ÖNEMLİ BİLGİ
Platformdan platforma kaç byte kaplayacağı değişir. Ama misal 2+4+4+8+8 = 26 Byte kaplaması şaşırtmazdı. Yine de platform bilinmiyorsa net bit şey söylenemez.

Bu gibi bir çok sebeple, öntanımlı veri tiplerini gömülü sistemlerde kullanmamaya gayret etmekte fayda var. Ama tabi yeri gelir, ne yaptığımızı bilerek, bedelini göze alarak ve sisteme zarar vermesini önleyerek kullanabiliriz.

Neyse… Niye sorusunun cevabını verdik gibi. Şimdi diğer soruya geçelim 🙂

Nasıl?

Ne kullanacağız öyleyse? Bu sorunun cevabını iç dünyamızda aramayacağız. Cevap C’nin standard kütüphanelerinden birinde gizli. Anlı şanlı “stdint.h” !!! Evet bu mübarek kütüphane, bir çok problemi tarihin tozlu sayfalarına gömüyor.

Cengaver gibi stdint tablosuna hemen bir göz atalım.

Degisken İşareti Bits Bytes Minimum Değer Maksimum Değer
int8_t Signed 8 1 −27 = −128 27 − 1 = 127
uint8_t Unsigned 8 1 0 28 − 1 = 255
int16_t Signed 16 2 −215 = −32,768 215 − 1 = 32,767
uint16_t Unsigned 16 2 0 216 − 1 = 65,535
int32_t Signed 32 4 −231 = −2,147,483,648 231 − 1 = 2,147,483,647
uint32_t Unsigned 32 4 0 232 − 1 = 4,294,967,295
int64_t Signed 64 8 −263 = −9,223,372,036,854,775,808 263 − 1 = 9,223,372,036,854,775,807
uint64_t Unsigned 64 8 0 264 − 1 = 18,446,744,073,709,551,615

Önceki tabloda, yani int,char gibi öntanımlı tiplerin yer aldığı tabloda, boyut kısmında “inş cnm” vardı. Sıkıyorsa burada da olsun… Olmaz çünkü bu değişkenlerin hepsinin boyutu SABİT. Hangi platforma giderseniz gidin, uint32_t, işaretsiz olan 32 bitlik bir değişkendir. İşte bu! Yani biz bu değişkenleri kullanarak bir yazılım ortaya koyduğumuzda, yarın öbür gün o kodu başka platforma taşısak da yazılımımızın bellek kullanımı aynı olacak. Buna ne denir? Çiçek çiçek…

ÖNEMLİ BİLGİ
Gömülü yazılımlarda, yalnızca stdint kütüphanesinden değişkenleri kullanmak bir nevi sevaptır.

O çok sevilen char bile, platformdan platforma işaret değiştirebilmekte. Velhasılkelam bunların hiç birine güven olmaz. İyisi mi stdint kullanın. Hiç zararı yok desem yeridir, ama sayısız faideleri mevcuttur.

Bununla birlikte, ikinci sorumuzu da yanıtladık. Şimdi geldik değişkenlere.

DEĞİŞKENLER

Şimdi değişken dediğimiz nane, bellek adreslerinin güzelce etiketlenmesinden başka bir şey değildir. Misal int i=32 diye bir değişken yarat…. Şeytana uymayın int filan yok 🙂 Baştan alalım. Misal şöyle bir değişken yaratalım.

Derleyici biz bu değişkeni yarattığımızda ne yapar? Gider ram’e şunu der: “Abi müsaitse ben bi 4 byte yer alayım içine de arkadaşlar bi zahmet 1988=0x000007c4 yazsınlar.” Bunu duyan ram cevap verir “Abi bakalım varsa itin olsun.” Bunu aslında ram’im kendisi değil de yöneticisi (işletim sistemi, vs) söyler. Ve neticesinde atıyorum 0x10208032 adresine sizin 1988 sayısı 4 byte olarak yerleşir. Big endian veya little endian olmasına göre verinin belleğe yerleşim sırası değişebilir. değişken uint32_t yerine int32_t olsa yine aynı muhabbet olacaktı 🙂 Ama int16_t olsa idi veri 0x07C4 olarak belleğe yerleşecekti. Neyse.

Bu değişkenin değişmeyen çeşidi de var ilginçtir. Misal aşağıdaki değişken, değişmeyen bir değişkendir ve değeri derleme aşamasında bir kez belirlenir ve o değişkenin değeri bir daha asla değişmez.

Değişkenin başına const koyduğumuz zaman derleyiciye deriz ki, bak bilader bu değişkenin değeri asla değişmeyecek. Dolayısıyla sen bunu zırt pırt değişmeyecek bir bellekte saklayabilirsin. Derleyici de gider o değişkeni ram’e değil flash’a ya da hard diske ya da genel adıyla NVM’ye(non-volatile memory) koyar. Hmm demek ki değişken demekle aslında bellekteki bir veri gurubunu etiketlemekten söz ediyoruz. Nitekim belleğin adresinden ziyade değişkenin ismini akılda tutmak daha kolay 🙂

Peki kardeşim bu değişkenin değeri değişeni var değişmeyeni var. Yeri değişmeyeni istersek ne yapacağız biz? O zaman da değişkene durduğun yerde dur diyeceğiz. Nasıl mı? Böyle:

Şimdi gelelim başka bir mevzuya. Peki ya bizim değişkenimiz öyle ansızın değişebilmeliyse? Ram’e erişmek görece çok hızlı olsa da sonuçta işlemcinin dışındaki bir bellekten söz ediyoruz ve haberleşme için bir miktar zamanı paşa paşa kaybediyoruz. Öyle bir imkan olsa ki çok acil durumlar için daha hızlı bir çözüm olsa. Var efendim, işlemcinin register’ları var. Biz derleyiciye şunu diyebiliriz. “Abicim eğer yer varsa lütfen bi zahmet bunu işlemcinin register’inda sakla. Yoksa da optimize filan etme kolayda tut lazım olunca hemen çekelim.” Derdinizi anlatmayı bilirseniz derleyici dileğinizi yerine getirir. Bunun için ona şunu yazmalısınız:

Bunları karıştırmak mümkün. Misal static const dediğinizde iki isteğiniz aynı anda gerçekleştirilir. Ancaak birbiriyle çatışan istekler aynı anda kullanılamaz. Mesela const volatile diyemezsiniz 🙂 İşin temel mantığı budur. Bunu bildikten sonra bu operasyonların felsefi yorumlarını yapmak hakikaten zor değil. Misal static olan bir değişkenin yeri bellekte değişmediğinden, o değişken son değerini her zaman korur. Bütün C tutorial’larnda static değişkenin fonksiyon içinde yalnızca bir kez initialize edildiğini herkes anlatır. İşte onun altına yatan nedeni de burda buldunuz. Artık aynı mantıkla hepsini yorumlamak mümkün 🙂 Misal şu bilgi de her yerde var; bir c dosyasında global olara tanımlanmış statik bir değişken yalnız o dosyada kullanılabilir. Neden? Çünkü derleyici her  dosyayı bir obje olarak derler. Sonra da objeler linker dayı tarafından birleştirilir. Bir objede bir değişkenin yeri sabit tutuluyorsa o bellek alanı, sinemada rezerve edilen koltuğa benzer. Başkasına satmazlar 🙂 Neyse işte bu bilgi setiyle tüm bu yorumları yapmak mümkün.

Bir de extern ön eki var ama kendisini hiç mi hiç sevmem. Kullanımı bence kötü mimarinin habercisidir. Uzaklardaki, başka dosyalardaki değişkene erişmek için de, fonksiyona erişmek için de header dosyası kullanımı daha mantıklıdır. Zamanı gelince ona da daha detaylı değineceğim.

Bizim yazdığımız kod derlendiğinde kodun işletilecek olan kısmı ile, değeri sabit olan değişkenler flash’a atılır. Değişecek değişkenler, fonksiyon stack’leri ise ram’de tutulur. Burda dikkat edilmesi gereken konu fonksiyonun kendisinin genelde flash’ta tutulduğudur. Ama fonksiyon argümanları filan ram’den kopyalanabilir. Neticede fonksiyon da bellek içindeki bir veri. C dilinde fonksiyonlar dahil olmak üzere her şey değişken çeşidirir demek esasen mümkün. Fonksiyon farklı olarak işletilebiliyor. Yani bellek içindeki veri, işlemci için anlamlı bir operasyon koduna (opcode’a) tekabül ediyor. Ama sonuçta değişken mi? Paşa paşa değişken 🙂 Sadece daha pahalı bir değişken 🙂 O zaman benzer kurallar fonksiyonlar için de geçerli 😉

Neyse bu kısmı da çeşitli ibretlerle atlattık. Gelelim bir diğer önemli kısıma. İsimlendirme….

Sen bellekten yer ayırmışsın, adını düzgün koymadıktan sonra neye yarar? Bir programda eğer, devreye bağlı olan LED’in açık mı kapalı mı olduğunu anlatan değişkenin adı fiveVFlag ise ühüüüü yandı onu okuyan, yandı onu geliştirmeye çalışan… Ama asıl onun adını  fiveVFlag koyan yandı çünkü kulakları çok çınlar. isCommErrorLedOn koysan değişkenin adını eline mi yapışır. Bu yazının başında bir meşhur söz paylaşmıştım. Dayı ne güzel demiş… Kodu öyle yazmak lazım ki, insan anlasın insan… Kodu okuyan kimse, kodlama yapan kişinin niyetini çok açık şekilde anlamalıdır. Ancak anlamalıdır derken, anlamasını sağlama sorumluluğu kodu yazandadır. Hatta sorumluluk öylesine kodu yazandadır ki, okuyanın işini kolaylaştıran hemen her şeyi düşünmek zorundadır kodu yazan.  Aynı zamanda kod self-documenting olmalıdır yani dökümana gerek kalmaksızın işlevini açıkça anlatabilmelidir. Kod, konuyla alakası olmayan biri tarafından bile anlaşılabilmelidir. Sadece başkası için düşünmeyin, yazdığınız koda 2 sene sonra bakmanız gerekirse siz de tam olarak sıfırdan başlayacaksınız. Her zaman bunun korkusu ile ve bilinci ile kod yazmakta sayısız faideler var. Şimdi bunları biraz somutlaştıralım.

Macar Notasyonu

Macar notasyonu (Hungarian Notation) der ki değişkenin ismini öyle güzel yaz ki hem derdini anlatsın hem de tipini. İyi demiş ne güzel demiş ama Macar Notasyonu bile çoğu kişi tarafından farklı farklı yorumlanmış, temeli aynı kalmak kaydıyla farklı estetiklerle uygulanmıştır. İnternette arattığınızda Macar notasyonu ile ilgili çok farklı örneklere rastlayacaksınız. Ama dediğim gibi amacı hep aynıdır, tip bilgisini vermek ve açıklayıcı olmak. Misal:

Buna rağmen notasyon seçimde tecrübeler der ki, çalışılan platform çok çok önemlidir. Mesela çalışma ortamı Visual Studio olacaksa, ve hep öyle kalacaksa tip bilgisini değişken ismine gömmek ahmakçadır. Çünkü kodu yazan kişi değişkeni çağırırken tipi zaten hintBox’da otomatik görünmektedir. Keza o değişkeni başka bir değişkene atayacakken değişkenin adını yazmaya başladığında değişkenin tipi zaten hintBox’da çıkmaktadır. O zaman ne gereği var? Bir de değişkenin tipini değişkenin başına koymak auto code completition (otomatik kod tamamlama) olan geliştirme ortamlarında sorun yaratacaktır. Çünkü siz ip adresini kullanmak istediğinizde onun string olmasından daha çok ip adresi olmasıyla ilgileneceğinizden değişkeni yazmaya ipAddress diye başlayacaksınız. Eğer ortamınız VisualStudio ise yine yırttınız ama Eclipse’in bu günkü bir versiyonu ise yandınız çünkü değişken ipAddress ile değil de str ile başladığı için auto code completition yapılamayacak değişken bulunamayacaktır. Ama Keil uVision gibi taş gibi, ama taş devrinden kalmış izlenimi veren dümdüz ama delikanlı bir IDE ile çalışıyorsanız tip bilgisini değişkene ekleseniz iyi edersiniz 🙂

Benim isimlendirme stilimi bu yazı dizisi boyunca yeterince tanıyacağınızdan şimdi detaylıca anlatmıyorum 🙂 Siz de kendinizinkini oluşturmakta özgürsünüz, ya da bir standardı kullanabilirsiniz [ki belki daha iyi bir seçim olabilir, tartışılır] ama her durumda kafanızdakini Blale anlatırcasına açıkça koda aktarmakla yükümlüsünüz.

Efendim değişken derken ben fonksiyonları da ayırmadığımı söylemiştim. Fonksiyon, asıl işi yapacak olan yazılım bloğu olduğundan çok doğru isimlendirilmeli, çok doğru yaratılmalıdır. Çoğu zaman fonksiyon isimleri, hatalı kodları ele vermektedir. Nasıl ki yazılan değişken neyi temsil ettiğini açıkça anlatmalı ise, fonksiyon da yapacağı işi açıkça anlatmalıdır. Aynı zamanda her fonksiyon yalnızca ama yalnızca bir işi yapmalıdır. Bunu ilerde fonksiyonlar kısmında daha detaylı anlatacağım ama misal şu fonksiyonun mesela isminde hayır olmadığından kendinin de hayırlı bir iş yapmadığı açıktır:

Arkadaş bir kere bu fonksiyonun adı o kadar saçma ki, C dilini ve bu hayali mikrodenetleyiciyi bilen hemen her kes bu kodun iki sayıyı toplayıp seri porta yazdığını görebilir. İyi de niye yazdı? Fonksiyonun adının bunu anlatması gerekir. Ki yazımı da çok kötü zaten ya neyse. Hadi biraz düzeltelim madem.

Artık her fonksiyon adına yanaşır şekilde bir iş yapıyor ve kodun iki sayıyı toplayıp seri porttan yollamakla hangi problemi çözdüğünü anlayabiliyoruz. Ayrıca belli ki auto code completition olmayan bir yerde kodu yazmışız. Bir diğer önemli nokta ise kodun dili. Kodun yarısı İngilizce yarısı Türkçe olmamalı. Hangisini seçtiyseniz ona sadık kalın. Normalde C dilinin anahtar sözcükleri İngilizce olduğundan ben tamamını İngilizce yazmayı tercih ediyorum. Ancak İngilizce bilmeyen arkadaşlar da anlatmak istediğim şeyi anlayabilsin diye bu defa Türkçe yazdım. Union kısmını anlamadıysanız şimdi ona çok takılmayın. İbret alınacak başka noktalara bakalım. İlk fonksiyonun adının ne kadar amaçsız olduğu konusunda anlaştığımızı sanıyorum. Bir diğer hata da isminden de anlaşıldığı üzere iki işi birden yapmaya çalışması. İşte bu tekrar kullanılabilirliği öldürüyor. Üstteki koddaki adam seri porta başka bir şey yazmak istediğinde kod tekrarı yapacak ve birinde hata yaparsa belki hatayı bulamayacak. Alttaki kodda ise bilgisayaraByteGonder fonksiyonu çiçek gibi çalıştığından çağırıldığı her yerde hataya mahal vermeksizin çalışmaya devam edecek. Ayrıyeten bilgisayara bilgi gönderecek her fonksiyon onu tekrar tekrar kullanabilecek. Bu nedenle fonksiyon isminde and, or, if filan var ise bilin ki o fonksiyon çok çirkin bir fonksiyondur. Ayrıca isimlendirme, dilin anlatamadığını anlatmayı hedeflemelidir. Değişkenin adını uzun yazmakta bir sakınca yoktur. Derleyici zaten derleyince onu adrese dönüştürecektir. Bir harfle isimlendirilen değişken de, uzunca ismi olan değişken de tipi aynı olduğu takdirde aynı miktarda yer kaplayacaktır.

Güzel kodlama pratikleri üzerinde ileriki konularda da konuşacağız. Şimdilik görüşmek üzere 🙂

 

Önceki Sayfa   Sonraki Sayfa

Gömülü C – 2 : Gömülü C’ye Giriş : Merhaba dünya

Yol haritamız malumunuz, karanlıkta klavuzumuz. Efendim ne diyor harita, sanatsal programlama pratikleri diyor. Peki nerden başlayacağız? C dilinden başlayacağız. C dili ile ilgili bir çok yorum yapılır;  orta seviye bir dil,  hem makinaya, hem insana yakın gibi… Hepsi geyik muhabbeti! Şu yıllarda, C dili hala varsa ve hala bu kadar popülerse bunun sebebi dilin gömülü sistemlerle olan şahane uyumudur. Mevzuya aşkın bu penceresinden bakalım. Gömülü C, en nihayetinde özünde bildiğimiz C dili. O sebeple bu kısım aman aman gömülü yazılıma özgü değil, ama merhaba dünya kodunu gömülü sistemci kafasından pestil edeceğiz. Sıksak ne kadar suyu çıkar? Göreceğiz…

C dili malumunuz,  ISO/IEC 9899 standardları ile tanımlanmış bir ifade biçimi. Bir dil bir insan derler ya, o misal bir dil 🙂 Kendisi diller familyasında,”yapısal” diller sınıfında yer alıyor. Yani ne diyor? Benim tasarımım yapılar (struct, enum, union) üzerine dayanır diyor. Benimle konuşacaksan, yapılar üzerinden konuşsan sen karlı çıkarsın diyor. İyi de neymiş bu yapılar? Struct, enum, union dedin, de ne bunlar? Hemen döndük yol haritasına, ne yazıyordu bilinene referans ver kıymetliyi anlat. O zaman bu yazıyı okumadan önce C dilini bir yerden tanımış olmanız gerekiyor. Üç beş örnek kodu çalıştırmanız gerekiyor. Yani dostlar, dost acı söyler, struct, enum, union nedir bilmeniz gerekiyor ki sonraki adım olarak işin felsefesini konuşalım. Çok da tatava yapmayacağım, inanıyorum ki bilmeyen de gitti okudu öğrendi geldi. Bilmeyen ama öğrenmek isteyenleri de duyar gibiyim; iyi de kaynak önermedik? Kitap olarak: Deitel&Deitel. Online kaynak olsun ve Türkçe olsun: ahanda burada.  Online olsun İngilizce olsun eyvallah o da burada. Şimdi heyecanıma yenik düşüp hemen struct, enum, union dedim ama onlardan daha önce çok önemli sorular var. İbretlerle dolu “Merhaba Dünya” var. Ezber bozan Merhaba Dünya!

Yazdığınız kodları derlemek için program olarak, dev C++’ı, codeblocks’u, eclipse cdt’yi önerebilirim. Sadistseniz, komut satırından Gcc ile de derleme yapabilirsiniz, ki çok keyiflidir, bilen bilir. 🙂

Şimdi, bir C kitabının gittiği sıra ile mevzunun inceliklerini konuşacağız. Anlı şanlı, Sanatsal Gömülü C başlıyor!

SANATSAL GÖMÜLÜ C!

Yanlış Bildiklerimizi Bir Kenara Koyabilmek, Öğrenmeye Açık Olmak…

C diliyle yazılan en basit kod diye tanıtılan merhaba dünya kodu ile başlayalım. Dakika bir, gol bir olsun. Acaba bin defa gördüğümüz merhaba dünya kodundan ne kadar ibret çıkarabiliriz ki? Üç beş satır kod. E hadi bakalım, suyunu sıkacağız demiştik. Kodun suyunu sıkalım neler neler çıkacak, görelim 🙂

Şimdi bir kere, iş yapan, bir şey döndüren en basit C kodu bu mu? Merhaba dünya hakikaten temelin temeli mi? Hiç alakası yok. Ahanda size, Bahçeli’ye yardımcı iş yapan çok daha basit C kodu. Koda hiç bir şey eklemeye gerek yok, derleyicide aynen çalışır. Bir şey eksik diye düşünmeyin.

Ee ne farketti ki? Çok şey farketti. Bir kere <stdio.h> kütüphanesini kullanmadık. “Yahu o, olmazsa olmaz değil miydi ya?”. Hayır efendim değildi. <stdio.h> yalnızca adı üstünde standart giriş çıkış (i/o) işleri yapılacaksa kullanılır. Merhaba dünya kodunda printf fonksiyonu çağırıldığından elimiz kolumuz bağlandı, <stdio.h> kütüphanesini göreve çağırdık. Peki niye <> ile çağırdık, bazen “selam.h” gibi de çağırılıyor bunun hikmeti nedir? Derleyici, çağırılacak bazı header dosyalarının konumunu ezbere bilir. Derleyicinizin “include directories” ayarında bunları görebilirsiniz. Deleyicinin yerini ezbere bildiği, uzak yerlerde de bulunabilen headerlar #include <selam.h> şeklinde çağırılır. Eğer selam.h dosyası, bu header’ı çağırdığımız selam.c dosyası ile aynı dizinde(klasörde) ise onu #include “selam.h” ile çağırırız. Peki #include nasıl bişi? C dilinde “#” ile başlayan komutlar preprocessor komutları diye adlandırılır ve yalnızca derleme zamanında çalışır. Yani biz selam.c fonksiyonunda selam.h’ı include edince derleyici derleme esnasında gider _selam.c diye geçici bir dosya oluşturur, selam.h’da ne var ne yoksa gider selam.c’nin tepesine koyarak toplam içeriği _selam.c’de saklar.

İyi de int veri tipi var kodda, o nerden geliyor? Ahanda ibret detected, int veri tipi C dilinin standard tanımlamalarında bulunan bir veri tipi olduğunadan, derleyici adam olacak, onu kütüphane olmaksızın tanıyacak.

Ama main olmazsa olmaz dmi? Hiç alakası yok. En sevmediğim genellemelerden biri de budur. Ezberdir, kötüdür, kemdir. Örneğin ARM Cortex mimarisinde kod her zaman main’den değil ResetInterruptHandler’dan başlar. İnanmazsınız, bu platformda main, reset interrupt handler tarafından herhangi bir fonksiyon gibi çağırılır. Nasıl yani main demesek ecmain desek kod ecmain fonksiyonuna mı gidecek? Evet. Ne derseniz ona gider. Ama alışanları hayal kırıklığına uğratmamak adına yine yalandan bir main çağırılır. Hmmm. Belki bu birilerini şaşırtmıştır. Hemen aydınlanmış hissetmemek lazım çünkü mevzu biraz daha derin. Main fonksiyonu aslında linker dayının, kodun başlangıç noktası olarak adreslediği bir fonksiyon. Main her şeyin başlangıç noktası olsa, işletim sistemi nasıl çalışırdı 🙂 Siz kodu çalıştırmadan önce, onu çalıştıracak sistemin çalışması gerekiyor. Demişler ki “aga, biz bu dili bulduk, elemanlar da kod yazsın da nerden başlayacak olm bu kod?” Biri demiş ki “ilk satırdan başlasın işte”, öbürü demiş “yok aga ön bilgiler lazım bir sabit bir fonksiyon adı belirleyelim, derleyici gitsin en azından o fonksiyonu arasın onun adresini bulsun bizim işletim sistemi de gitsin programı da ordan başlatsın nasıl ama”? Ve akla yatmış tamam demişler. Biz bi linker yazsak, windows üzerinde çalışsa ne yazmamız gerekirdi? Window’s bunu nasıl çalıştırırdı? Mevzuyu anlamamızı sağlayan soru bu? C programı nasıl çalışır değil. Bu ters yönden hedefe varmamızı sağlayacak. Compiler gitti bizim dosyaları derledi biz de birleştirerek çalıştırılabilir hale getireceğiz. Ne yapacağız? Diyeceğiz ki; misal bizim program “bigbang” fonksiyonundan başlasın. Ben bir dil yazsam her şey bigbang ile başlardı çünkü. Neyse bizim c dosyalarında kullanılan sembolleri, başka dosyalarda da arayıp bulup birleştirdik. Adam düzgün yazdı diye varsayım yapıyoruz ki bigbang fonksiyonu da yazmış bir tane. Süper! İşletim sistemi ne yapacak? Gidecek bakacak; bu program ne kadar ram kullanacaksa, o kadar yeri ayıracak ayarlayacak. Sembollere adresler atayacak, kendi task scheduler’ının stack’ini filan ayarlayıp bizim programı “bigbang” fonksiyonunun bellekteki adresinden başlatacak. Sonra da malumunuz bizim merhaba dünya kodu çalışacak. Vay beee. Ne yolculuk görüyor musunuz? Bunun da öncesi var, bilgisayar nasıl ayağa kalkıyor filan? Ama onlar sonranın mevzusu. Almamız gereken ibret şu: main de neticede bir fonksiyon… Linker, bizim elemanların aldığı karar yüzünden onu arıyor. Arasın dursun madem. Şimdi ibretlere devam.

int main()” yazdık. Bu ne demek; “int main(void)” demek aslında ama biz kısa olsun diye öyle yazdık dmi? Değil. İkisi arasında ince bir fark var. Buna da geleceğiz ama, önce sırayla gidelim. Bir kere, fonksiyonu soldan sağa inceleyelim. int döndürmesi ne demek fonksiyonun? Şu demektir ki; fonksiyon bir makine gibi çalışırsa, işini bitirince bize bir sayı verecek demektir. İyi de bizim program çalıştı bitti 0(sıfır) döndürdü, bu 0(sıfır) kime, niye gitti, ne anlatmaya çalıştı? Burada main özel bir fonksiyon etiketi olduğundan verdiği mesaj işletim sistemine gider. Yani bizim Merhaba dünya, doğru düzgün çalışıp işini bitirirse, bir yerde takılmazsa işletim sistemine 0(sıfır) sayısını gönderir. Ama niye sıfır? Çünkü bir çok hata çıkabilir, ama programın hatasız tamamlanma ihtimali bir tanedir. Biz her bir hataya sıfırdan farklı bir sayı değeri atarsak, hatasızlığa da tek bir şekilde(tek bir sebeple) gerçekleşeceğinden sıfır değerini atarsak ne olur? Çiçek olur çiçek. Bizim programda diyelim bellek ayrılamadı… O hataya 1 desek, printf ekrana bir şey yazamadı; o hata 2 desek, başkasına 3 desek filan tüm hataları indeksleyebiliriz. Hata kodunun 0(sıfır) olması, hata yok demektir. O nedenle 0(sıfır) döndürülür. Hata kodu da sayı olacağından dolayı da main int döndürür, ki hakkıdır yapar.  Önceden main’in adı niye main onu anlatmıştık.Şimdi şu “()” vs “(void)” farkına gelelim. Bir fonksiyon parametre olarak “()” alıyorsa, bu şu demektir: sen parametre olarak ne verirsen ver ben sallamayacağım ve onu kullanmayacağım. “(void)” ise daha otoriter bir mesaj veriyor: sen parametre olarak bana bir şey veremezsin. Parametre olarak (void) alan bir “seviyorumDe(void)” fonksiyonu uydurun, çağırırken “seviyorumDe();” şeklinde çağırın derleyici hiç kızmaz. Ama “seviyorumDe(“Sevmiyorum”);”  şeklinde çağırırsanız derleyici size kızar. Sonra gidin fonksiyon tanımını  “seviyorumDe(void)” yerine “seviyorumDe()” olarak tanımlayın derleyici size hiç kızmaz. Ama fonksiyonun içinde “Sevmiyorum” argümanına doğrudan erişemezsiniz. Demek ki neymiş, bunun bile bir nedeni varmış. Bu bile aslında fonksiyonu çağıracak kişiyi yönlendiriyor, kısıtlıyormuş. Yeni ibret; ezber yok.

Dostlar, gördüğünüz gibi “hello world” kodu bile bize neler neler gösterdi. Ki mevzuyu kısa kestiğimizi de belirtmek isterim. printf’in nasıl çalıştığını da anlatmak isterdim, ama şimdi öyle yapsam burası çok uzun olacak, belki sıkılacaksınız. İleride yeri geldi mi ondaki ibretleri de anlatacağım 🙂 O zamana kadar sabırla okuyun olur mu? Bu daha başlangıç, mücadeleye devam!

Dosya uzantıları: Selam.c ve selam.h örneklerini anlatmıştım. C programlamada muhakkak kaynak dosyaları .c uzantılı, header dosyaları .h uzantılı olmalı dimi? Alakası yok. selam.h dosyasını gidin selam.ozkaya dosyası olarak adlandırın. Sonra selam.c’de gidin #include “selam.ozkaya” yazın ve cayır cayır çalıştığını görün. gcc kullanmayı bilen varsa selam.c’yi de selam.ozen diye adlandırıp derlemeyi deneyebilir 🙂 Peki niye .c ve .h diye adlandırıyoruz? Çünkü derleyici bir dosyayı açtığında uzantısından, ona yapacağı muameleyi otomatik anlayabilsin istiyoruz. Aynı dili konuşalım istiyoruz.

DİPNOT: Sırf değiştirebiliyoruz diye header işlevi gören bir dosyanın uzantısını adınız soyadınız yapıp milleti kanser etmeyin 🙂 İnsan okuyacak o kodu, insan 🙂

Yukarıdaki yorum satırı hakkında ne düşünüyorsunuz? Ben kendisini iğrenç buluyorum çünkü bir amaca hizmet etmiyor. Koda bakan herkes o kodun C dili ile yazılmış bir merhaba dünya kodu olduğunu anlayabilir. Böyle yorum satırı olmaz, hiç bir işe yaramaz, sadece kalabalık yapar. Gereksiz kalabalığı da Gömülü C’ci sevmez. Buradan yorum satırlarına karşı olduğumu çıkarmayın, yorum satırları da sanatsal şekilde kullanılabilir. Yeri gelince detaylı anlatacağım 🙂

Şimdi devam…

Önceki Sayfa   Sonraki Sayfa

Gömülü C – 1 : Gömülü Programlama Yol Haritası

YOL HARİTASI

Düşündüm yazının başlığını, daha güzel ifade edemedim. Bu yazıda, bu yazı dizisine ilişkin hayallerimi, yol haritasını anlatacağım ve umuyorum ki hepsi adım adım gerçekleşecek. Kararlılıkla, sabırla gerçekleşecek. Ve diliyorum ki  hayatımıza bir katkı sağlanabilecek. Eğer bu yol haritasını sonuna kadar okuduysanız, ama heyecanlanmadıysanız lütfen yazı dizisine devam etmeyin.

Önceki yazılarda gömülü sistemler için gerekli donanım altyapısını tahsis etmeye katkı sağlamaya çalışmıştım. Nihayetinde heyecanla beklediğim an geldi çattı 🙂 Yazı dizisinin bu bölümünde her yerde bulunabilecek bilgilere referans vererek, her yerde bulanamayacak bilgileri paylaşmaya çalışacağım. Paylaşırken de öyle sıkıcı şekilde değil de, sohbet muhabbet tadında paylaşmaya çalışacağım. Neyse şimdi laga-lugayı kısa kesip konuya gireyim 🙂

Malumunuz gömülü sistemler kısıtlı kaynaklı sistemler. Gel gelelim bu ifade artık, her durumda doğru bir ifade sayılmaz. Hakikaten bir çok görev için son derece “kısıtlı kaynaklı” olarak değerlendirilebilecek sistemlerin yanında bayağı bayağı “bol kaynaklı” gömülü sistemler de mevcut. Bir “gömülü webserver” yapmak için Microchip firmasının PIC16F84A mikrokontolörü çok kısıtlı kaynaklı kalabilir, öte yandan bu iş ARM Cortex A15 barındıran, doğru tasarlanmış bir gömülü sistem için genelde çocuk oyuncağının bir kaç mertebe altında bir iştir. Sözün özü, kısıtlı kaynak, spesifik bir görev için söz konusudur. Ancak yine de, rekabette var olabilmek için genelde bir işi olabilecek en ucuz ve en kaliteli şekilde yapmak, amaçtır. Bu da kaynakların kısıtlı kalmasına neden olur.

Kaynaklar kısıtlı olduğu gibi, zaman da çoğunlukla kısıtlıdır. Bu sebeple yazılan kodların son derece optimize, kolay anlaşılabilir, tekrar kullanılabilir, yani aslında “kaliteli” olması gerekmektedir. Efendim yazılım neye göre kaliteli olacak? Meselenin aslı ortaya kondu mu, cevap bulmak kolay olur demişler(N.E.). Cevap basit, “yazılım tasarımı kalitesi standartlarına” göre kaliteli olacak. Yani isim verecek olursak ISO/IEC-14598, ISO/IEC 250×0 standart serisine göre. Evet, sizi duyar gibiyim ve ben de aynı fikirdeyim ama var işte yazılım kalitesinin de standardı 🙂

Efendim neymiş bu standartlar? Standartlar mı yönetecek bizi? Hayır efendim, standartlar yönetmeyecek, standartlar tanımları yapacak, ortak dili belirleyecek sonra biz o ortak dilden konuşacağız ve yazılımlarımızın kalitesini ölçülebilir hale getireceğiz. İyi tamam, ama gömülü sistemler ne alaka? Gömülü sistemler, belki de kaliteli tasarıma en çok ihtiyaç duyulan yazılım tasarım ortamıdır. Zaten zaman kısıtlı, kaynaklar kısıtlı, işin içinde donanım olduğundan değişkenlerin sayısı çok fazla, ürün sürekli güncelleniyor gibi bir ton sebep, rekabette öne geçmek için tasarımın kaliteli olmasını zorunlu kılıyor. İyi de madem standard var, ben ne konuşuyorum? Dedim ya standardlar sadece ortak dili anlatıyor ve bazı ölçüm yöntemlerini belirliyor. İyi olana gitmek bize kalıyor. Dil ortak ama ne konuşacağımızı yine biz belirliyoruz.

Bu yazı dizisinde C dili, C++ dili anlatılmayacak. Deitel&Deitel’in kitabı o işi çok çok iyi yapıyor. Ancak bu yazı dizisinde onun yerine, sanatsal seviyede programlama pratikleri, gömülü sistemlere özel tasarım kalıpları, modelleme yöntemleri ve iyi programlama pratikleri anlatılacak. Bunların bir kısmının, benim bildiğim bir kaç düzine önemli kitaplarda dahi yer almadığını belirtmek isterim. Uzmanlar için bile faydalı olabilecek bir iki numaram olduğunu düşünüyorum 🙂 Peki nasıl olacak? Her şeyden önce sabırla, sebatla, ilim irfan aşkıyla, azimle olacak. Her zaman yaptığımız gibi, bitirememek üzere başladığımız 1632. iş olmayacak bu. Önce eğer C bilmiyorsanız, bir kaynaktan C öğreneceksiniz. Önerdiğim kaynak Deitel&Deitel’in kitabı. Ardından bu yazı dizisini okuduğunuzda hakikaten keyif alacaksınız. Gerçi bir dili bilmek iddiası fazla özgüvenlice bir iddia bence, her zaman birbirimizden öğrenecek şeylerimiz var o ayrı mevzu ama C dilini zaten biliyorsanız doğrudan yazılara devam edebilirsiniz. Böylelikle gömülü sistemler için çok net, taş gibi bir yazılım alt yapısı edinmiş olacaksınız. Zaten varsa da onu cilalamış olacaksınız. Yazıda C ile birlikte C++ konseptlerine de değineceğim.

Sonra ne olacak? Yazı dizisi nasıl devam edecek? Donanım bilgisi oluştu, yazılım bilgisi kültürü oluştu, sırada ne olacak. Sırada artık ellerin kirlenmesi var. Microchip firmasının 8 bitlik PIC mikrodenetleyicilerini programlama’yı ince detaylarla anlatacağım. Bu sayede küçük bir sistemde çalışmanın faydalarını,  temelleri ve detayları tertemiz görmeyi öğreneceğiz. Aynı zamanda basit sürücüler yazacağız, sensörleri ve komponentleri tanıyacağız. Ardından Atmel firmasının Atmega serisi ile analoji kuracağım, onu her şeyiyle kullanmayı da anlatacağım. Burada araya Arduino’nun iç yapısını, anlatılmayanlarını sıkıştıracağım, malum çoğunun içinde Atmega var. Sonra 16 bitlik bir mikrodenetleyici serisine geçeceğiz. Aynı zamanda bu ultra-low power bir seri olacak; Texas Instruments MSP430 serisi. Buradan hem 16-bit’lik bir mikrodenetleyici nasıl kullanılır, farkı nedir, low-power sistem tasarlamanın püf noktaları nedir onları da anlatacağım. Sonra geleceğiz ARM Cortex M3-M4 serisi işlemci barındıran 32-bit’lik mikrodenetleyicilere. Burada da STM32 ve LPC17 serisinden örnekler vereceğim. Hemen hemen aynı şeyler, ama aynılıkları farklılıkları bilmek bence önemli bir bilgi seti olacak. Aynı zamanda DMA gibi, EMC gibi yepyeni modülleri tanıyacağız. Bu sayede mikrokontolörlerin tüm çevresellerine hükümdar olacağız. TFT ekran, thermal printer, manyetik kart okuyucu, rf modem gibi cihazlar ile çalışacağız, sensörler kullanacağız. Seviye atlamış olacağız. Burada RTOS kullanmanın inceliklerine de değineceğim. Çok çok önemli bir bilgi seti RTOS. Ya sonra? Bitmedi mi bu mikrokontrolörler filan? Hayır efendim bitmedi. Çok çekirdekli sistemler nasıl programlanır onu anlatacağım. 64-bit’lik platformlara değineceğim. Malum son IPhone’larda 64 bitlik işlemciler var, neticede o da gömülü sistem. Ya sonra? Sonra 32-bit’e döneceğiz ama mikrobilgisayar seviyesindeki çekirdekleri inceleyeceğiz. Burada gömülü linux ile platform bazında kapanışı yapacağım. Kafama eserse VxWorks de yazabilirim ama elzem değil. En sonunda açık kaynaklı yazılımlarla çalışma, açık kaynak lisans tiplerine değineceğiz.

Burada yazı dizisi sonlanacak sanıyorsanız yanılırsınız. Neticesinde sadece gömülü sistem temeli tahsis edilmiş olacak. Ama öyle bir temel ki, üstüne artık ne kurarsan kur. Sonra her telden çalacağız, başına gömülü etiketi koyacağız. Ne demek istiyorum? Gömülü görüntü işleme yapacağız. Gömülü kontrol sistemi tasarlayacağız. Gömülü haberleşme tekniklerini ağlatacağız, havadan karadan giden gelen veriye hükmedeceğiz. Gömülü sistemlerde pastanın üstüne çilekler koyacağız, kriptopgrafi yapacağız. Sonra FPGA konuşuruz. Sonra PSOC konuşuruz. Ve daha nicesi 🙂

Bu yol uzun olacak, zaman alacak, fedakarlık isteyecek ama yol bu olacak. Her türlü öneriye açık olduğumu da belirtmek isterim. Ülkemizin, teknoloji üretiminde ileriye gitmesine, yani sizlere, hayatımıza bir nebze katkıda bulunabilirsem ne mutlu bana.

Şimdi devam.

Önceki Sayfa   Sonraki Sayfa

 

Gömülü Sistemler – 11 – Mikrodenetleyici I/O Modları

Mikrodenetleyicinin genel amaçlı giriş çıkış (general purpose input/output (GPIO)) pinleri, giriş çıkış bakımından farklı modlarda konfigure edilebilir. Giriş modları arasında pull-up, pull-down dirençleri, hysteresis, ya da benzeri kombinasyonlar söz konusu olabilir. Benzer şekilde çıkış modları push-pull, high-drive ya da open-drain olabilir.

gpio-output-types

Resim 1

ÖNEMLİ BİLGİ
Gömülü sistemler üzerinde çalışacak bir kişinin I/O modlarını mutlaka bilmesi gerekmektedir. 

Giriş Modları

GPIO giriş modları genellikle, yüksek empedans(high impedance, high-z), pull-up, pull-down ve tekrarlayıcı(repeater) olarak tasarlanır. Yine bazı mikrodenetleyiciler I/O arayüzlerinde histerizis (hysteresis) özelliği de barındırır ve bu sayede suni (bouncing gibi) durum değişiklikleri önlenir.

 

Pull Up/Down

Eğer bir giriş, dahili (mikrodenetleyicinin içindeki) pull-up modunda ise o pin içeride bir direnç ile lojik bir seviyesine çekilmiştir. Bu da şu demektir ki, o pin dışarıdan lojik sıfıra sürülene kadar, lojik 1 durumunda kalmaya devam edecektir. Tam tersi durum da pull-down için geçerlidir. Eğer bir giriş, dahili  pull-down modunda ise o pin içeride bir direnç ile lojik sıfır seviyesine çekilmiştir. Bu da şu demektir ki, o pin dışarıdan lojik bire sürülene kadar, lojik sıfır durumunda kalmaya devam edecektir.

Repeater: Bazı mikrokontrolörlerde bazı pinlerin durumu dinamik olarak pull-up ya da pull-down olarak değiştirilebilmektedir ve bunun için repeater mod kullanılır.

Floating, High Impedance, Tri-Stated

High Impedance: Bir giriş pini  yüksek empedans(high impedance, high-z) durumunda olduğunda pinin durumu, o pin lojik sıfıra ya da lojik bire bağlanana kadar bilinemez.

Floating: Yüksek empedans yapısındaki bir pin, lojik sıfıra ya da lojik bire bağlanmadıysa, pin floating (belirsiz, salınan) moddadır denir.

Tri-stated: Üç durumlu anlamındaki bu mod, floating ile aynı durum için kullanılır.

ÖNEMLİ BİLGİ
Mikrokontrolörün dışında herhangi bir yere bağlanmamış bir pinin floating durumda kalmasını önlemek için,mikrokontrolörün içerisindeki pull up/down dirençleri aktive edilir.  

Hysteresis

Bir pinin lojik sıfırda mı lojik birde mi olduğuna karar verebilmek için genelde bir eşik değeri kullanılır. Ancak bu eşik değeri bir aralık ifade etmediğinden eşik değerinin çok az üstü de lojik bir olur, epey üstü de lojik bir olur. Benzer şekilde eşik değerinin çok az altı da lojik sıfır olur, epey altı da lojik sıfır olur. Gerçek hayatta bu durum bouncing (sekme, salınma) problemini doğurur. Siz lojik birdeki bir pini lojik sıfıra çekerken pin eşik değerinin etrafında kararsız bir duruma girer ve ardışıl olarak 1,0,1,0,1,0 durumları görülür. Bunu önlemek için güvenli bir aralık belirlenmiştir.

Çıkış Modları

 

Push-Pull

Push-pull bir çıkış hem source hem de sink akımı akıtabilir. Bu da pin çıkışı sıfıra da çekilse, bire de çekilse o pin üzerinden akım akıtabilmeyi sağlar. TTL ve CMOS devreler push-pull çıkış kullanır.

Open-Drain

Resim 1’de “open-drain” yazan kısmı görebilirsiniz. Gerilimle sürülen bir transistör türü olan MOSFET’i tanıyanlar için “open-drain” kavramı zaten açıklamaya mahal gerek bırakmayacak kadar anlaşılırdır. Ancak MOSFET’i bilmeyenler için anlatmak gerekirse bir MOSFET üç pine sahiptir: gate(kapı, giriş), source (kaynak), drain(akaç). Open-drain durumda source toprağa bağlıdır,gate içeriden sürülmüş durumdadır ve drain açıktadır. Open-drain çıkış yalnızca sink akımı akıtabilir yani dışarıdan akım çekebilir. Dışarıya doğru akım basamaz. Esasen bu da iki durumda kalabilmesine imkan verir: düşük empedans ve yüksek empedans.

High Drive

High Drive (yüksek sürüşlü) pinler esasen yüksek akım verebilen push-pull pinlerdir. Normal bir push pull pin +/- 8mA akım akıtabilirken high drive pinler 40mA’e kadar akım akıtabilir. Ancak genelde elektriksel karakteristik datasheette belirtilir. Eğer pin ile doğrudan yüksek akım çeken elemanlar sürülecekse, pinin üst sınırına kadar akım çekecek cihazlar, ek bir devre olmasızın high drive pinler ile sürülebilirler.