Arduino ile Interrupt Kullanımı ve Uygulamaları

Merhabalar,

Bu konuda mikrodenetleyici ve mikroişlemci ailelerinin olmazsa olmazı interruptlar’ dan bahsedeğiz.  Öncelikle olayın teorisinden bahsedip daha sonra Arduino ile yapımına ve bazı örnek uygulamalar yapacağız.

Bildiğiniz üzere Arduino gibi tek işlemci birimine (veya tek çekirdekli)  sahip donanımlarda herhangi bir işletim sistemi (RTOS) kullanılmadığı durumlarda işlem sırası yukarıdan aşağıya doğru sıra ile gerçekleşir. Normal şartlar altında bu sıra asla terk edilmez ve girişlerdeki değişikler sırası geldiğinde kontrol edilir. Ancak çok uzun yazılım içeren veya içerisinde yüksek bekleme süresine sahip uygulamalarda düşük tepki süresine ihtiyaç duyulduğunda pinin okunma sırasının beklemek çok geç kalınmasına sebep olabilir. Bu gibi durumlarda kesmeler (Interrupt) çokca işe yararlar. Adından da anlaşıldığı gibi kesmeler tetiklendiğinde ana döngünün akışını keserek tetik geldiğinden yapılması gereken işlemlerin yapılması sağlanabilir.

Yukarıda kesme çeşitlerinden dış kesmenin üzerinde durmaya çalıştım. Dış kesmelerin yanı sıra zamanlayıcı(timer) kesmesi, haberleşme kesmesi vb kesme çeşitleri de bulunmaktadır. Zamanlayıcı kesmeleri, ayarlanan zaman gelince program akışın sırasının terk edilip zamanlayıcı vektöründe belirlenen işlemleri yapılamsı sağlar. Haberleşme kesmeleri’ de dış kesmenin haberleşme donanımlarında bulunan modelidir. Örneğin seri porttan veri gelmesi durumunda yapılmasını istediğimiz işlemler varsa bu kesmeler sayesinde gerçekleştirilebilir.

Biz bu konuda daha çok dış kesmelere örnek vereceğiz.  Timer kullanımı ile ilgili konumuza buradan ulaşabilirsiniz.  Ayrıca vektör kavramından bahsettik. Bu vektörler kesme fonksiyonlarının adreslerini tutarlar. Kesme meydana geldiğinde (kesme bayrağı setlendiğinde) bu vektörün tuttuğu adresteki fonksiyona gidilir ve buradaki işlemler yapılır. En son kesme bayrağı sıfırlanır ve fonksiyondan çıkılarak tekrar ana döngüye geri dönülür.

Gelelim dış kesme kullanımına. Arduino da dış kesme kullanımı için özelleşmiş iki adet pin vardır. Bu pinleri kullanarak sadece bir ayar yardımı ile dış kesme rahatlıkla kullanabilirsiniz.

Yukarıdaki resimde Arduino Uno üzerindeki bütün pinler ve sahip oldukları fonksiyonlar görünmektedir.  Resimde dijital 2 ve 3 nolu pinlere bakarsanız sağlarında INT0 ve INT1 yazdığını görebilirsiniz. Bunlar “Interrupt 1” ve “Interrupt 2” olarak ifade edilir ve kesmelerin kolay kullanımı için özelleştirilmiştir. Kesmenin kullanımı için bazı modlar belirlenmiştir. Bunlar şu şekildedir:

  • LOW: Pin gerilimi Lojik-0 olması durumudur.
  • CHANGE: Pin geriliminin değişmesi durumudur.
  • RISING: Pin geriliminin Lojik-0′ dan Lojik-1′ e geçmesi durumudur.
  • FALLING: Pin geriliminin Lojik-1′ den Lojik-0′ a düşmesi durumudur.

Not: Burada bahsedilen Lojik-1, Uno, Mega vb. 5V mikrodenetleyiciler için 2.5V üzerisidir. Due gibi 3.3V mikrodenetleyiciler için 1.65V üzerisidir.

Kesmeyi kullanabilmek için ayarlamaları yapmak üzere bir fonskiyon kullanmamız gerekir. Bu fonksiyon “attachInterrupt” fonksiyonudur. 3 parametre alır. İlk parametre kullanılacak pini gösterir. İkinci parametre kesme fonksiyonunun ismi ve son parametre kesme modunu belirler. 

void setup() {
  //interrupt ayarlama
  attachInterrupt(digitalPinToInterrupt(2), kesme, RISING);
}

//interrupt fonksiyonu
void kesme()
{
  
}

“attachInterrupt” fonksiyonunda kullanılan digitalPinToInterrupt fonskiyonu kullanılacak dijital pin için interrupt numarasını döndüren bir fonksiyondur. Yani D2 pini için 0, D3 pini için 1 değeri döndürür. Siz bu fonksiyonu kullanmadan direk bu şekilde de kullanabilirsiniz.

  attachInterrupt(0, kesme, RISING);

Bu kullanım ile yukarıdaki kullanım aynı işlevi yapar. parametre olarak kullanılan 0, INT0′ dan gelmektedir. Bu fonksiyonu kullanarak dijital 2 pininde Lojik-0′ dan Lojik-1′ e geçiş olması durumunda ana döngüdeki sıradan ayrılarak kesme fonksiyonun içersindeki işlemlere geçilir. Bu fonksiyonun içerisindeki işlemler tamamlanınca tekrar ana döngüde kalınan yerden devam edilir. 

Yazılımın çalışmasında sırasında bu kesmenin modunu değiştirmek veya tamamen devre dışı bırakmak isteyebilirsiniz. Bunun için aşağıdaki fonksiyonu kullanmanız kesmeyi devre dışı bırakmak için yeterli olacaktır. Daha sonra tekrar ayar yaparak farklı modda çalışmasını sağlayabilirsiniz.

detachInterrupt(digitalPinToInterrupt(2));

Bunun ile ilgili bir yazılım örneği yapalım. İki ledimiz ve bir butonumuz olsun. Ledlerden biri 1 saniye aralıklar ile yanıp sönsün. Diğeri ise butona basılıp bırakılınca yanıyorsa sönsün, sönükse yansın. Böylece delaylı işlemlere kesme ile ara verilebildiğini gözlemlemiş oluruz. Devre şemamız aşağıdaki gibi olsun.kesme_bb1

Resimde de görüldüğü üzere buton normalde 5V verecek şekilde bağlanmıştır. Butona basılınca D2 pininde 0V görünmektedir. Kesme RISING şeklinde ayarlandığı için butonun bırakılması durumunda kesme devreye girecektir. Yazılım ise şu şekildedir.

int led1 = 13, led2 = 12;     //led tanımları

void setup() {
  //led ayarları
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);

  //kesme ayarları
  attachInterrupt(digitalPinToInterrupt(2), led_kesme, RISING);
}

//kesme fonksiyonu
void led_kesme()
{
  digitalWrite(led2, !digitalRead(led2));
}


void loop() {
  //ledi 1 sn aralıklar ile yakıp söndürdük
  digitalWrite(led1, !digitalRead(led1));
  delay(1000);
}

Yazılımda da görüldüğü gibi gecikmeli işlemleri kesmeler yardımı ile rahatlıkla bölebilir ve araya istediğimiz işlemleri koyabiliriz. Lakin kesmelerin bazı kısıtlamaları vardır. Örneğin haberleşme kesmelerinde yine aynı haberleşme modülünü kullanmazsınız ayrıca kesme fonksiyonları içerisinde gecikmeli işlemlerin kullanımı tavsiye edilmez.


 

 

Şimdi sıra geldi pin değişim kesmelerine. Pin değişim kesmeleri Arduino Uno ve Nano modelinin üzerindeki neredeyse bütün pinlerde mevcuttur. Önceki kesmeden farklı olarak tanımlama işlemleri biraz daha karışıktır ve pinin lojik durumunun değişiminde aktif olur. Yani önceki kesmelerde olduğu gibi lojik durum değişim ayarı yapılmaz. Kesme meydana geldiğinde pinin durumu kontrol edilerek işlemler gerçekleştirilmesi gerekir. Pin Change Interrupt olarak daha detaylı kaynak bulabileceğiniz konunun Arduino’ nun kendi sitesindeki açıklamasına bağlantıdan ulaşabilirsiniz.  Ben elimden geldiğince burada anlatmaya çalışayım.

vektörler

Yukarıdaki resimde pin değişim kesmelerinin vektörleri görünmekte. Kullanmayı istediğiniz pine göre vektör tanımı yapmalısınız. Örneğin D9 pinini kullanacaksanız “PCINT0_vect” vektörünü kullanmalısınız. Daha sonra da referans sayfada bulunan aşağıdaki fonksiyon ile rahatlıkla pin değişim kesmesini ayarlayabilirsiniz.

void pciSetup(byte pin)
{
    *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
    PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
    PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

Buradaki registerler in ne iş yaptığı ile detaylı bilgim benimde yok. Lakin buradaki kullanımları yorum satırlarında zaten verilmiş ve üzerlerinde fazla durmayacağım. Parametre olarak verilen pin için pin değişim kesmesi ayarını yapan bir fonksiyon olduğunu bilmemiz bu aşamada yeterlidir.

Şimdi bununla ilgili de bir örnek yapalım. Üstteki bağlantı şemamıza bir buton daha ekleyerek aşağıdaki gibi değişelim.

kesme2

 

Yazılımda ise bir ledi 1 saniye aralıklar ile yakıp söndürelim. Diğer ledi ise A0′ a bağlı butona basınca yakalım, D9′ a bağlı butona basınca söndürelim. Yazılımımız da şu şekilde olsun.

int led1 = 13, led2 = 12;
int buton1 = 9, buton2 = A0;

//pin change interrupt ayar fonksiyonu
void pciSetup(byte pin)
{
  *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

void setup() {
  // put your setup code here, to run once:
  //led ve buton tanımları
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(buton1, INPUT);
  pinMode(buton2, INPUT);

  pciSetup(buton1);   //9 nolu pin için pci ayarlandı
  pciSetup(buton2);   //A0 pini için pci ayarlandı
}

ISR (PCINT0_vect) // handle pin change interrupt for D8 to D13 here
{
  //D9 butonu basıldı ise ledi söndür
  if (!digitalRead(buton1))
  {
    digitalWrite(led2, LOW);
  }
}

ISR (PCINT1_vect) // handle pin change interrupt for A0 to A5 here
{
  //A0 butonu basıldı ise ledi yak
  if (!digitalRead(buton2))
  {
    digitalWrite(led2, HIGH);
  }
}

void loop() {
  //ledi 1 sn aralıklar ile yakıp söndürdük
  digitalWrite(led1, !digitalRead(led1));
  delay(1000);
}

Yazılımımız da bu şekilde. Kesme vektörleri içerisindeki ” if ” sorgularında değil ifadesi kullanmamızın nedeni butonların PULL UP şeklinde bağlı olup normalde 5V vermeleridir. Butona basılınca ise 0V verirler. Bu yüzden butona basılma sorgusunda ” !digitalRead(**) ” olarak kullandık. Bu yazının da sonuna geldik. Umarım yardımcı olabilmişimdir. 

İyi çalışmalar dilerim..

 

 

15 Comments

Add a Comment

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir