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.
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.
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.
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..
Merhaba. 2015’te STM32F4 kullanımıyla ilgili uygulamalar paylaşacağınızı yazmıştınız ancak henüz paylaşımda bulunmadınız. Bu geliştirme kartını kullanmaktan vazgeçtiniz mi? Vazgeçtiyseniz sebebi nedir?
Merhaba, öncelikle takibiniz için teşekkür ederim. Sorunuza gelince STM32 serisi geliştirme kartlarını elbet kullanmayı bırakmadım. Aksine daha fazla kullanmaya çalışıyorum. Lakin en basit işlemler için bile ayarlamalar biraz uzun sürdüğü için yazılı olarak anlatımı zor oluyor ki okunma miktarları da Arduino’ ya nazaran çok düşük oluyor. Bu sebeplerden ötürü vakit ayırıp konu yazamıyorum. Lakin STM32 serisi kartlar ile Mbed kullanımı son derece kolay. Eğer STM32 serisi kartlarda yeni iseniz kullanmanızı tavsiye ederim. İyi çalışmalar dilerim..
Bu örnek nano da sorunsuz çalışırken uno da analog pinler çalışmıyor. Sebebi ne olabilir acaba ?
Merhaba, dediğiniz şekilde bir sorun olması için bir durum bulunmamaktadır. Yapmak istediğiniz şey hakkında daha açık olursanız belki yardımcı olabilirim. İyi çalışmalar dilerim…
o kadar güzel anlatmışsınız ki, diğer bir çok web sitesinde konuyu açıklamak yerine daha karışık hale getirmişler. Örnekleriniz de anlatımınız da çok güzel, emeğinize sağlık :)
Yorumunuz için teşekkür ederiz. Yardımcı olabildiysek ne mutlu bize. İyi çalışmalar dileriz…
kesme ayarı yaparken led_kedsme fonksiyonu yazarken led_kesmesi şeklinde kullanmışsınız burada bir yanlışlık mevcut
Uyarınız için teşekkür ederim, hatayı düzelterek konuyu güncelledim. İyi çalışmalar dilerim..
Merhaba, sensörden gelen verinin 1 defaya mahsus gelmesini istiyorum ama arduino sürekli güç aldığı ve dolayısıyla çalıştığı için sürekli olarak sensörden değer geliyor. Bunu interrupt ile nasıl yapabilirim? Teşekkürler
Merhaba, Interrupt ile yapmak istediğiniz sistemi tam olarak anlamadım. Kullanacağınız sensör dijital bir sensör değilse interrupt kullanamazsanız. Dijital bir sensör ise verdiği çıkışa göre düşen yada yükselen kenarda işlem yaptırmanız yeterli olacaktır. İyi çalışmalar dilerim…
Çok güzel anlatmışsiniz emeginize saglik. Mümkünse arduino ile uyku modu ve dış kesme ile sensörlü bir örnek verebilirmisiniz. Pil tasarrufunda ciddi faydalari olacaktır. Halihazirda evde kullandigimiz projelerimizde pil sarfiyati çok fazla teşekkürler
öncelikle bilgiler için çok teşekkür ederim mümkünse sizden yardım istiyorum bu devrede butona iki kez bastığımızda led in yanmasını istiyorum ve 5 saniye sonra sönecek ve görev biticek diğer yanıp sönen led e de ihtiyacım yok çift komutla tetikleme sadece dileğim şimdiden teşekkür ederim
Merhaba, lütfen konu içeriği ile ilgili olmayan özel sorularınızı baser61061@gmail.com adresine detaylı olarak gönderin. İyi çalışmalar dilerim.
Benim çalıştığım mfrc522 den 10 adet veta 7 farketmez. Bunlar için en sağlıklı bağlantı ve kullanabileceğim yol nedir? Nasıl bir yol izlemeliyim. Sadece tanımladığım mfrc522 – 1 veya mfrc522 – 2 den okunduğunu anlayayım bana yeterli.
Merhaba, bahsettiğiniz sorunu iki farklı şekilde çözebilirsiniz. İlki bu cihazlar SPI ile haberleştiği için SS pinleri farklı olmalı ve o anda aktif ettiğinizden okuma yapıyor olmalısınız. Okuma işlemini SS pinleri yardımı ile sıralı bir şekilde gerçekleştirirseniz okumayı hangi cihazdan yaptığınız anlayabilirsiniz. Bir diğer yöntem de modül üzerindeki IRQ pini. Bu modül okuma yaptığında interrupt üretiyor. Bu pinin kullanımına bakarak da sorununuzu çözebilirsiniz. İyi çalışmalar dilerim.