Arduino’da PORT Manipülasyonu(Register Seviye Programlama)

Bugünkü yazımızda Arduino’da port manipülasyonu konusunu göreceğiz. Bildiğimiz gibi Arduino IDE aracılığıyla elimizdeki Arduino’ları programlamak mümkündür. Buna ek olarak yine bildiğimiz gibi Arduino IDE aracılığıyla aslında Arduino geliştirme kartımızın içinde bulunmakta olan mikrodenetleyicimizi programlamaktayız. Bu programlama işlemi esnasında hali hazırda bize sağlamış oldukları kütüphaneler aracılığıyla insan diline daha yakın bir şekilde bu işlemleri gerçekleştirebilmekteyiz.

Hatta basitçe biz buna yüksek seviyeli bir mikrodenetleyici programlama yöntemi de diyebiliriz. Çünkü direkt olarak port manipülasyonlarını kullanmadan hazır kütüphaneler kullanarak Arduino’yu programlarsak, yazdığımız kod daha okunabilir olacaktır. Fakat işte burada karşımız çok büyük bir problem çıkmaktadır. Aslında piyasada bu işle hobi olarak uğraşan bir çok insan bu handikabın farkında değildir. Belki de bunun en büyük sebebi, o kişilerin projelerini daha çok hobi seviyesinde tutmaları ile alakalıdır. Hazır kütüphaneler kullanarak mikrodenetleyicimizi programladığımızda karşımıza çıkan bu problem, mikrodenetleyicimizin hafızasını gereksiz yere fazla kullanmasıdır. Bir diğer olumsuz durum ise, hazır kullanılan kütüphaneler yüzünden olması gerekenden daha fazla işlem yaparak, işlem hızımızın azımsanamayacak kadar düşmesi olacaktır.

Bu noktada aslında bu problemler kişiye bağlı yol izlemektedir. Projelerini daha çok hobi seviyesinde tutan kişiler, zaten fazla hıza ihtiyaç duymayacağı için bu tarz port manipülasyonlarına pek fazla gerek duymamaktadır. Çünkü o kişilerin amacı zaten projesini en anlaşılır ve hızlı şekilde yapmaktır. Fakat işini bizler gibi profesyonel bir şekilde sürdürmek isteyen kişiler, yeri geldiğinde mikrodenetleyicisinin hafızasındaki her bir bayt’a ayrı bir ihtiyaç duymaktadır.

Peki sizce neden hazır oluşturulmuş bu kütüphaneleri kullanmak, mikrodenetleyicimizin hafızasında fazladan yer kaplamakta ve üstüne üstelik hızımızı da yavaşlatmaktadır?

Bildiğimiz gibi biz aslında Arduino ide’de kodumuzu direk C dilinde yazmamaktayız. Bu noktada Arduino ide’nin bize sunmuş olduğu hazır kütüphaneleri kullanmaktayız. Burada yazmış olduğumuz kod aslında hazır oluşturulmuş fonksiyonlardan oluşmaktadır. Ve kodumuzu derlediğimizde aslında yazmış olduğumuz kod direkt olarak mikrodenetleyicimizin hafızasına yüklenmemektedir. Çünkü bildiğiniz gibi mikroişlemcimizin işlemleri gerçekleştirebilmesi için ilk önce yazılan kodun onun anlayabileceği bir formda olması gerekmektedir. Bu noktada yine aklınıza bazı sorular gelmiş olabilir. Peki bu kodlar nasıl çalışıyor.

İşte bu noktada hazır fonksiyonları kullanrak hazırlamış olduğumuz arduino kodumuz:

void setup() {
  pinMode(2, OUTPUT);  // 2 numaralı pin çıkış olarak ayarlandı
  pinMode(3, INPUT);   // 3 numaralı pin giriş olarak ayarlandı
}

void loop() {
  if(digitalRead(3) == 1) // 3 numaralı pin girişi yüksek durumuna geçerse 
  { 
  digitalWrite(2, HIGH); //Led'in bağlı olduğu pine güç verildi
  }
  else
  {
  digitalWrite(2, LOW); //Led'in bağlı olduğu pine verilen güç kesildi
  }
}

Aslında gördüğümüz gibi projemizi bu şekilde hazırlamak çok daha anlaşılır ve kolay şekilde olacaktır. Ancak bu durumda kullanmış olduğumuz bu hazır fonksiyonlar ilk önce kullanmış olduğunuz ide aracılığıyla Register seviyesinde programlanmış olan C diline çevrilmektedir. Çünkü her kütüphane içerisinde, fonksiyonun aslında register seviyesinde programlamadaki karşılıklarını görmekteyiz. Sonrasında C diline çevrilen bu kodlar assembly diline çevrilmektedir. Yani hepimizin isim olarak aşina olduğu makine diline çevrilmektedir. İşte düşündüğünüz biraz düşündüğünüz de mantığı basitçe kavrayabilmekteyiz. Eğer kodumuzu direk register seviyesinde yazarsak veya kendi kütüphanemizi oluşturup gereksiz fonksiyonlardan ve tanımlamalardan kaçarsak, kodumuzun hafızada tutacağı yer azalacaktır.

Arduino

Bildiğimiz gibi Arduino, multidisipliner projelerde kullanımınızı kolaylaştırmak için tasarlanmış olan ve ücretsiz geliştirme ortamına sahip olan bir geliştirme kartıdır. Bu noktada bir sürü Arduino çeşidi bulunmaktadır. Bu sebepten de ötürü Arduinoların çeşidine göre içinde bulunan mikrodenetleyici de değişmektedir. Buna verebileceğim iki örnek şöyle olacaktır:

  • Arduino Uno -> İçerisinde 32 KB Flash Memory’e sahip ATmega328P kullanılmaktadır.
  • Arduino Mega -> İçerisinde 256 KB Flash Memory’e sahip olan ATmega2560 kullanılmaktadır.
Arduino'da PORT Manipülasyonu
Arduino UNO

İşte buradan da anlıyoruz ki hangi kartı programladığımıza bağlı olarak bazı önemli parametreler değişebilmektedir. Peki yavaştan konumuzun asıl kısmına gelmekteyiz. O zaman gelin led yakma kodumuzu hem Arduno kütüphaneleri ile yazalım hem de port manipülasyonu tekniğini kullanarak yazalım. Bu sayede syntax olarak da aralarındaki farklarını görelim.

Bu noktada ilk olarak şunu bilmelisiniz ki biz bu yazımızda örnek kodumuzu Arduino UNO’ya göre yazmaktayız. Bu kodlarımız mıkrodenetleyiciden mikrodenetleyiciye göre bazı küçük farklılıklar gösterebilmektedir.

Arduino’da Hazır Kütüphanelerle LED Yakma

Bu noktada aslında işimiz zaten çok basittir. Bununla ilgili zaten detaylı bir yazımız bile mevcut. Oradan da tekrar yapıp konuyu pekiştirebilirsiniz. Gelin direkt kodumuzu inceleyelim.

int ledPin=2;               // Led'i 2 numaralı pine bağladık
void setup() {  
pinMode(ledPin, OUTPUT);    // Led'imizi çıkış olarak ayarladık
}
void loop() {

digitalWrite(ledPin, HIGH); // LED'e güç verdik
delay(1000);                // Bir saniyeliğine bekledik
digitalWrite(ledPin, LOW);  // LED'in gücünü kestik
delay(1000);                // Bir saniyeliğine bekledik

Arduino’da Port Manipülasyonu İle LED Yakma

Bu noktada ilk olarak bilmemiz gereken şey programlayacağımız mikrodenetleyicinin ne olduğudur. Biz burada Arduino Uno kullanacağımız için ATmega328P’nin datasheetini incelememiz bizim avantajımıza olacaktır. Bu noktada bunun en büyük sebebi mikrodenetleyici programlarken asla ezber yapmamanız gerektiğidir. Biz size direk uygulanması gereken komutları, hazır bir şekilde verebiliriz. Ancak bu size gerçekten mikrodenetleyici programlamayı anlamada pek fazla bir şey katmayacaktır. Eğer o komutları neden kullanacağınızı da öğrenirseniz, işte bu noktada işiniz ileride çok daha kolay olacaktır.

Arduino'da PORT Manipülasyonu
datasheet

Bu noktada size datasheet’ten küçük bir görsel paylaşacağız ancak bu datasheet’i sizin indirip incelemeniz avantajınıza olacaktır. Çünkü birazdan aşağıdaki alanda göreceğimiz kodların aslında nereden geldiğini buradan görebilirsiniz. Bu datasheet’i biraz daha incelerseniz tam olarak nereden geldiği veya diğer komutların da neler olduğunu öğrenebilirsiniz.

Arduino'da PORT Manipülasyonu
ATmega Pin Konfigürasyonu

Port Manipülasyonu İle Pin Yapılandırması

Bu noktada gelin mikrodenetleyicimiz pinlerini nasıl giriş ve çıkış şeklinde ayarladığımı görelim. Ardından aslında Arduino’nun hazır kütüphanelerini kullanırsak nasıl olduğunu görelim ve böylece küçük bir kıyaslama yapmış olalım.

Bu noktada Arduino’nun hangi pinin Atmega’da hangi Data ve Data Direction Register’ine denk geldiğini görelim.

Arduino'da PORT Manipülasyonu
Arduino Pinleri

Yukarıdaki görselden rahatça Arduino’nun hangi pinin, Atmega’da hangi Register’da hangi bit’e denk geldiğini anlayabilmekteyiz.

Port Manipülasyonu İle Pin’i Çıkış Olarak Ayarlama

Bu noktada Arduino’da bulunmakta olan 10 numaralı pinimizi gelin çıkış olarak ayarlayalım. Tabi bunu daha iyi anlayabilmek için 4 numaralı pinin aslında hangi Register’da hangi bite denk geldiğini anlayabiliriz.

Arduino'da PORT Manipülasyonu
DDRB

Bu noktada aslında 10 numaralı pinimizin, yukarıdaki görselden PB2 bitine denk geldiğini görmekteyiz.

DDRB = 0b00000100;

Alttaki kod üstteki ile aynı işlemi yapmaktadır. Sadece alttaki kodda bitsel işlem operatörü kullanarak”|” (VEYA) kodumuzu biraz daha anlaşılır bir hale getirdik.

DDRB |= (1 << PB2);

Bu görmüş olduğunuz kod sayesinde, Arduinomuzun 10 numaralı(Mikrodenetleyicide PB2 ye denk gelmektedir) pinini ÇIKIŞ olarak ayarlamaktayız. Bu kod sayesinde projelerinizde o pin üzerinden istediğiniz gibi çıkış alabilirsiniz.

Hazır Kütüphanelerle Pin’i Çıkış Olarak Ayarlama

pinMode(10, OUTPUT);

Gelin yine Arduino’da bulunmakta olan 10 numaralı pinimizi gelin çıkış olarak ayarlayalım. Bu kod bildiğimiz gibi port manipülasyonunu kullanarak programlama ile aynı işlemi gerçekleştirmektedir. Ve aslında dikkatlice incelerseniz bu kod satırının ne kadar okunabilir olduğunu anlayabilirsiniz ancak bildiğimiz üzere bu komutu tercih edersek hafıza ve hız gibi konularda olumsuz etkilerle karşılaşacağız. Yine de önceden belirttiğimiz gibi bu tarz olumsuzluklar hobi projelerinde pek fazla sizin başınızı ağrıtmayabilir. Bu biraz kişisel tercih meselesidir. O an karşılaştığınız problem ne ise ona bağlı en mantıklı çözümü uygulamak en doğru karar olacaktır.

Port Manipülasyonu İle Çıkış Olarak Ayarlanmış Pine Güç Verme

PORTB = 0b00000100;

10 numaralı pinimize gelin güç verelim. Alttaki kod üstteki ile aynı işlemi yapmaktadır. Sadece alttaki kodda bitsel işlem operatörü kullanarak”|” (VEYA) kodumuzu biraz daha anlaşılır bir hale getirdik.

PORTB |= (1 << PB2);

Bu görmüş olduğunuz kod sayesinde, Arduinomuzun 10 numaralı(Mikrodenetleyicide PB2 ye denk gelmektedir) pinini HIGH yapmaktayız. Bu sayede projelerimizde o pin üzerinden istediğimiz işlemi uygulayabiliriz.

Hazır Kütüphanelerle Çıkış Olarak Ayarlanmış Pine Güç Verme

digitalWrite(10, HIGH);

Proje Üzerinde Kaynak Kod Karşılaştırması

Bu projemizde 4 adet LED Kullanacağız. Aslında gerçekten basit bir proje olacak. Buradaki tek amacımız projemizin iki ayrı kaynak kodu arasındaki syntax farkını dikkatlice incelemek olacaktır. Projemizde 2 LED’imiz aynı anda yanacaktır. Diğer iki LED’imiz sönük durumda duracaktır. Sonrasında yana 2 LED’imiz sönük duruma geçip diğerleri yanacaktır.

Hazır Kütüphane ile LED Projesi

#define led1 8
#define led2 9
#define led3 10
#define led4 11

void setup() {
  pinMode(led1, OUTPUT);     // 8. Pin Çıkış Olarak Ayarlandı
  pinMode(led2, OUTPUT);     // 9. Pin Çıkış Olarak Ayarlandı
  pinMode(led3, OUTPUT);     // 10. Pin Çıkış Olarak Ayarlandı
  pinMode(led4, OUTPUT);     // 11. Pin Çıkış Olarak Ayarlandı
} 

void loop() {
  digitalWrite(led1, HIGH);  // 8. Pine Güç Verildi
  digitalWrite(led2, HIGH);  // 9. Pine Güç Verildi
  digitalWrite(led3, LOW);   // 10. Pinden Güç Kesildi
  digitalWrite(led4, LOW);   // 11. Pinden Güç Kesildi
  delay(1000);               // 1 Saniye Beklendi  
  digitalWrite(led1, LOW);   // 8. Pinden Güç Kesildi
  digitalWrite(led2, LOW);   // 9. Pinden Güç Kesildi
  digitalWrite(led3, HIGH);  // 10. Pine Güç Verildi
  digitalWrite(led4, HIGH);  // 11. Pine Güç Verildi
  delay(1000);               // 1 Saniye Beklendi  
}

Port Manipülasyonu ile LED Projesi

void setup() {
  DDRB = 0b000001111;    // 8, 9, 10, 11 Numaralı Pinler Çıkış Olarak Ayarlandı
  PORTB = 0x00;          // Data Register'ını Her Proje Başında Sıfırlamanız Avantajınıza Olacaktır
  
}

void loop() {
  PORTB = 0b000000011;   // 8, 9 Numaralı Pinlere Güç verildi ve Diğer Pinler Sıfırlandı
  _delay_ms(1000);       // 1 Saniye Beklendi
  PORTB = 0b000001100;   // 10, 11 Numaralı Pinlere Güç verildi ve Diğer Pinler Sıfırlandı
  _delay_ms(1000);       // 1 Saniye Beklendi
}

Aslında bu noktada bilmemiz gereken bir kaç şey bulunmaktadır. Bunlardan biri, kod yazmanın ve algoritma kurmanın birden fazla yolunun olduğudur. Bildiğimiz gibi programlama dillerinde for, while gibi döngüler bulunmaktadır. Aslında bu tarz döngüleri de kaynak kodumuzda kullanarak daha da kısaltabiliriz. Veya yapmak istediğimiz şeyi daha farklı yollardan yapabiliriz. Ancak bizim buradaki amacımız sürecin aslında nasıl işlediğini ve aralarındaki net farkı görmek olduğu için böyle yazmayı tercih ettik. Yoksa farklı şekillerde de yazabilirdik.

Bu noktada aslında gördüğünüz gibi mantık tamamen aynıdır fakat yazım şekli birbirinden farklı şekilde olmaktadır. Bunun sebeplerinden biri Port Manipülasyonu ile programlamada digitalWrite, pinMode, gibi komutlar bulunmamaktadır. Onlar yerine bizler DDRx, PORTx gibi komutları kullanmaktayız. Bu bize aslında fazladan bellekte yer ayırmamıza yardımcı olmaktadır. Zaten bunun tam sebeplerinden yazımızın başında bahsetmiştik.

Kısaca farklar bunladır ancak şunu da son kez unutmayalım. Port Manipülasyonu ile programlama da tamamen derya denizdir. Biz sadece gerçekten giriş seviyede size farklarından bahsettik ve Port Manipülasyonu ile programlama giriş yapmak istedik. İleride bununla ilgili güzel bir seri yapmayı düşünmekteyiz. Bu sayede aslında register seviyesinde programlama ile pin yapılandırması, haberleşme sistemleri vb diğer konularda da bilgi sahibi olacaksınız. Bu sayede büyük ve kaliteli projelerinizde daha profesyonel bir kaynak kod geliştirme sürecine adım atabileceksiniz.