Mikrodenetleyici Programlamaya Başlamak – 3

Mikrodenetleyicilere ait temel kavramları ve mikrodenetleyicilerin nasıl programlanacağını  anlatacağım Mikrodenetleyici Programlamaya Başlamak yazı dizisinin ilk bölümünde temel mikrodenetleyici kavramlarını açıklamıştık. İkinci bölümde ise mikrodenetleyicilerin temel çevre birimlerini incelemiştik

Bu üçüncü bölümde ise mikrodenetleyici programlamaya kısa bir giriş yapacağız. Şunu belirterek başlayayım. Bu dizi bir eğitim materyali olmayacak. Yani A’dan Z’ye mikrodenetleyici programlama öğretilmeyecek.  Bunun yerine anlaşılması zor olan bazı kavramlar incelenecek ve mikrodenetleyici programlama nasıl öğrenilir sorusunun cevabı ele alınacak. Ayrıca mikrodenetleyici programlama öğrenebilmek için temel düzeyde C/C++ bilgisi gerektiğini de unutmayın.

Öncelikle denetleyici ve platformu belirleyelim. Dünyada ve ülkemizde özellikle miroişlemciler derslerinde en çok kullanılan mikrodenetleyici ailelerinden ikisi PIC ve AVR ile ilerleyeceğiz. Mümkün olduğu kadar her iki aile için de örnekler vererek devam etmeye çalışacağım.

PIC ailesi için CCS C derleycisini kullanacağız Bu derleyici ile birlikt gelen kütüphaneler ile PIC programlamak oldukça kolaylaşıyor.

İlk örneğimiz mikrodenetleyicilerin temel çevre birimlerinden biri olan GPIO ile ilgili olacak. Tabi ki gömülü sistemler dünyasının “Hello World!”u olan led yakıp söndürme uygulaması ile başlayacağız.

Bilgisayar veya mkrodenetleyicileri programlarken unutmamamız gereken bir gerçek var. Program yazarken aslında bir makinayla konuşuyoruz ve makinaya neler yapacağını söylüyoruz. Dolayısıyla hiç bir detayı atlamadan herşeyi makinaya söylememiz gerekiyor.

İlk programımız;

Satırları tek tek açıklayarak devam edelim;

Öncelikle hangi mikrodenetleyiciyi programlayacağımızı derleyiciye belirtmemiz gerekiyor. Bu sayede derleyici o mikrodenetleyiciye ait kütüphane dosyalarından ilgili fonksiyonları, saklayıcı adreslerini çekerek programı derleyecektir. Bu işi ilk satırda #include önişlemci komutu (daha sonra açıklayacağız) ile yapıyoruz. Sadece kullanacağımız mikodenetleyiciye ait kütüphaneyi çağırmak için değil kullanmamız gereken farklı kütüphaneleri de yine #include komutu ile çağıracağız.

İlk üç satırın diyez (#) işareti ile başlaması dikkatinizi çekmiştir. Bu şekilde diyez ile başlayan komutlara önişlemci komutları diyoruz. Çünkü bu komutlar sadece derleme sırasında bir kez çalıştırılıyor ve bazı derleme parametreleri veya mikrodenetleyicinin çalışma parametreleri belirleniyor. 

#fuses da bunlardan bir tanesi. Fuses mikrodenetleyicinin çalışması sırasında değiştirilemeyen sadece programlama esnasında belirlenebilen bazı kritik parmetrelerini (örneğin kullanılacak osilatör tipi) seçmek için kullanılan bir komut. En çok kullanılan parametreleri aşağıda bulabilirsiniz.

HS / XT: Sırasıyla yüksek hızlı (>4 Mhz) veya düşük hızlı harici (<4 Mhz) oslatör seçimi yapabilirsiniz.

NOWDT: Watchdog timer ‘ı kapatır. (Belilri aralıklarla mkrodenetleyicinin reset atmasını sağlayan bir çevre birimi)

Kullandığımız son önişlemci komutu olan #use komutu ile program içerisinde kullanacağımız gecikme fonksiyonu delay’in zamanı doğru bir şekilde hesaplayabilmesi için kullandığımızı osilatörün frekansını belirtiyoruz.

Artık asıl programımızı yazmaya başlayabiliriz;

Mikrodenetleyiciye yaptırmak istediğimiz her işi ana fonksiyonun yani void main() içerisine yazacağız. Yakıp söndüreceğimiz ledleri mikrodenetleyicinin D portuna aşağıdaki şekilde bağlı olduğunu varsayalım. 

Mikrodenetleyiciye ait giriş-çıkış portlarının hem giriş hem çıkış olarak kullanılabildiğini (adından da anlaşıldığı gibi) biliyoruz. Peki biz led yakmak için nasıl kullanacağız? Tabi ki çıkış olarak fakat mikrodenetleyici bunu bilmiyor, dolayısıyla bunu mikrodenetiyiciye belirtmemiz gerekiyor. Bu işlemi set_tris_x(); fonksyonu ile yapıyoruz (x hangi port olduğunu belirtiyor).  PIC mikrodenetleyicilerinde bir pini/portu çıkış olarak ayarlamak için o porta ait TRIS saklayıcısına 0, giriş olarak ayarlamak için de 1 yazmamız gerekiyor.

Sonuç olarak set_tris_d(0x00); fonksiyonu ile D portundaki tüm pinleri çıkış olarak ayarlamış oluyoruz. Peki 0x00 tam olarak ne anlama geliyor. Burada sayı tabanlarını biraz hatırlamamız gerekiyor. Pogramlama dillerinde 16’lık sayı tabanındaki sayılar 0x ile başlayarak belirtilir. 2’lik sayı tabanındaki sayılar ise 0b ile gösterilir. Yani biz D portunun ilk 4 pinini giriş son 4 pinini çıkış olarak ayarlamak istersek fonksiyon içerisine 0xF0 ya da 0b11110000 yazmamız gerekir.

Bilgisayar veya mikrodenetleyici programlarında program son satıra geldiğinde çalışmayı durdurur ve artık bir işlem yapmaz. Ledlerin sürekli yanıp sönmesini istediğimiz için bir döngü içerisinde bunları yaptırmamız ve programın son satıra gelmesini engellememiz gerekiyor.  while(1) döngüsü parantez içerisine bir koşul yerine 1 değeri yazıldığı için program bu satıra geldiği andan itibaren çalışma süresi boyunca bu döngü içerisindeki işlemleri tekrarlayacaktır. 

output_x() fonksyonu x portunun pinlerine çıkış olarak lojik 1 (elektriksel olarak 5V) veya lojik 0 (elektriksel olarak 5V) verilmesini sağlar. Yani 1 değerini atadığımız her bit için bir pin aktif olacak ve çıkışına bağlı led üzerinde bir gerilim oluşturarak ledin ışık vermesini sağlayacaktır. 0xFF değeri ikilik tabanda 0b11111111 değerine karşılık geldiği için tüm ledler ışık verecektir. Ardından delay_ms() komutu ile 1000ms =1s ‘lik bir bekleme süresinin ardından output_d(0x00) ile tüm ledler sönecek ve ardından 1 s’lik bekleme ile döngü tekrar edecektir.

İşin biraz daha temeline inelim;

Peki tüm bu fonksiyonlar nasıl işliyor, içeride neler oluyor? Kullandığımız bu fonksiyonlar CCS C derleyicisinde öntanımlı olarak gelen kütüphanelerde bulunuyor. Aslında yaptıkları çok basit bir işlem ve mikrodenetleyicilerin nasıl çalıştığını daha iyi anlayabilmek için ipuçları içeriyor.

set_tris_d(); fonksiyonunu ele alalım. Aslında yaptığı tek iş içerisine yazılan değeri mikrodenetleyicinin ilgili saklaycısına yazmak. Bu işlemi bu fonksiyonu kullanmadan yapmak istersek tek yapmamız gereken mikrodenetleyicinin salayıcı hafızasında 88h satırında bulunan TRISD saklayıcısının içerisine 0x00 değerini yazmak. Aynı şekilde output_d() fonksyonu yerine 8h satırında bulunan PORTD saklayıcısına 0xFF ya da 0x00 yazarak led yakma/söndürme işlemlerini yapabiliriz.

PIC C’ de tanımlı port okuma/yazma komutlarını kullanmadan porttan veri okuma ve yazma işlemleri için portun bulunduğu saklayıcıya (register) doğrudan ulaşılması gerekmektedir. PIC C derleyicisine ait kütüphanelerde port saklayıcıları tanımlı olmadığı için öncelikle porta ait saklayıcıların hafızadaki adresleri tanımlanmalıdır. Hafızada B portunu kontrol eden PORTB 0x06 numaralı saklayıcıda tutulmaktadır. B portunun modunu tutan TRISB saklayıcısı ise 0x86 numaralı satırdadır. Diğer saklayıcıların hafızadaki yerlerini öğrenmek için kullanılacak mikrodenetleyicinin bilgi sayfası (datasheet) incelenmelidir.  Bu tanımlamalar yapıldıktan sonra porta ya da diğer saklayıcılara doğrudan veri yazmak ve okumak için atama operatörü (=) kullanılır.

Bu durumda programımız aşağıdaki gibi görünürdü;

Bu iki fonksiyonda olduğu gibi diğer tüm fonksiyonlarda da yapılan işlemler aslında saklayıcılara veri yazmak ve veri okumaktan ibarettir. Her şey saklayıcılar ile kontrol edilir her çevre biriminin kendine ait saklayıcıları vardır. Bu saklayıcıların nasıl çalıştığını, birimleri nasıl kontrol ettiğini mikrodenetleyici üreticilerinin yayınladığı bilgi sayfalarından (datasheet) öğreniriz. Fakat çoğunlukla derleyicilerin ön tanımlı kütüphanelerinde bulunan bu işlemleri daha basit ve anlaşılır kılan fonksiyonları kullanırız.