C18 Diliyle Robot Programlama – Dış Kesme (External Interrupt) Kullanımı

Bu bölümde amacımız B portundan gelen sinyalin değişimini algılayan bir kesme oluşturmak. Bu kesme pinlerden gelen sinyaller tarafından tetiklenecek ve istediğimiz işlemi yapacak. Özellikle encoder okuma modülünü kodlarken bu sinyal değişimleri tarafından tetiklenen kesmelerden çokça faydalanacağız.

PIC18F4585’te 3 adet dış kesme girişi bulunur. Bunlar RB0(INT0), RB1(INT1) ve RB2(INT2) pinleridir.

Aynı zamanda bu RB0, RB1 ve RB2 pinlerine bizim butonlarımızın da bağlı olduğunu biliyoruz. Biz bu butonlar sayesinde bu kesme girişlerine +5V sinyali ve 0 (IDLE) sinyali verebiliyoruz. (Butona basmazsak 5V, butona basarsak 0V olduğunu kabul edelim). Amacımız butonlarla oluşturacağımız bu sinyalin düşen veya yükselen kenarlarında kesmelerin oluşmasını sağlamak. Düşen kenar dediğimiz şey, sinyalin 1 (+5V) durumundan 0 (IDLE) durumuna geçmesi, yükselen kenar dediğimiz şey ise sinyalin 0 (IDLE) durumundan 1 (+5V) durumuna geçmesidir.

Şekilde;

  • Mavi dikey çizginin olduğu bölümler; B sinyalinin yükselen kenarlarını,
  • Kırmızı dikey çizginin olduğu bölümler A sinyalinin yükselen kenarlarını,
  • Mor dikey çizginin olduğu bölümler B sinyalinin düşen kenarlarını,
  • Yeşil dikey çizginin olduğu bölümler A sinyalinin düşen kenarlarını göstermektedir.

Bazı butonlarda basılı konum +5 volt(1)’u gösterirken bazı butonlarda durum tam tersidir. Bu yüzden deneme yapmadan önce butonlarımızın hangi durumda +5 volt, hangi durumda 0 volt verdiğini ölçmeliyiz(Genellikle basılı durumda 0 volt vermesi gerekir). Ben RB0, RB1 ve RB2 pinindeki butonların voltajlarını her iki durum içinde multimetreyle ölçtüm. Hepsi de basılı konumda 0, diğer konumda 1 sinyalini vermekteler. Yani şekildeki A sinyalini baz alacak olursak. Ben her butona bastığımda sinyal 1 durumundan (+5V) sıfır durumuna (0V) geçecek. 1’den 0’a geçişin olduğu yerler A sinyali için yeşil çizgili alanlar. Benim elimi butondan her çekişimde kırmızı çizgili alanlara denk gelecek (0’dan 1’e geçme durumu).

Burdan şu sonuç çıkar. Eğer ben dış kesmelerimi yükselen kenarda tetiklemek istiyorsam butondan elimi çekmeliyim (interrupt kodlarım ancak o zaman çalışacak), eğer düşen kenarda tetiklemek istiyorsam butona basmalıyım. 0-1 veya 1-0 değişiminde interrupt kodlarım çalışmaya başlar. Eğer kesmemi yükselen kenarda tetiklenecek şekilde ayarlamışsam ve buton da basılı durumda 0V veriyorsa. Ben butona ilk bastığımda interrupt kodlarım çalışmaz. Çünkü butona basarken 1-0 geçişi oluşuyor, butona basılı haldeyken elimi çektiğim zaman interrupt oluşur (0-1 geçişi) ve interrupt kodlarım ancak o zaman alışır. Dış kesmenin oluşma durumunu bu şekilde inceleyeceğiz.

External interrupt (Dış kesme) oluşturmak için öncelikle RB0, RB1 ve RB2 pinlerini ortak kullanan diğer modüller varsa kapatmalıyız. RB0 ve RB1 pini ek olarak ADC modülü için AN8 ve AN10 kanalı olarak kullanılmakta. Bu kanallar üzerinden herhangi bir sinyal gelme ihtimaline karşın ADC modülünü CloseADC() fonksiyonu ile kapatacağız. Ayrıca ADC modülü için kullanılan bu pinler analog sinyal ucu olarak açılmış olabilir (default olarak   analog pin olarak açılırlar). Bizim buton üzerinden göndereceğimiz sinyal dijital sinyal olacağı için eğer bu pinler analog olarak açılmışsa düzgün değerler okuyamayız. Bu nedenle RB0, RB1 ve RB2 pinlerini dijital sinyal kullanımı için ayarlamamız gerekir.(ADCON1 register’ının son 4 biti bunu ayarlamak için kullanılır). RB2 pini dış kesmenin haricinde CAN haberleşme modülü için de kullanılır fakat şu anda herhangi bir CAN haberleşme ağına bağlı olmadığımızdan dolayı -ve benim kullandığım kartta CAN haberleşmesini desteklemediğinden- bir problem olmayacak. (Kart CAN haberleşmesini desteklemese de PIC18F4585 CAN’i destekler, onun örneklerini de ilerleyen konularda göreceğiz).

Dış kesmeleri açmak için hazır fonksiyon kullanacağız fakat ondan önce kesmeleri oluşturmak için ayarlanması gereken register bitleri hakkında bilgi vereceğim (Fonksiyon kullanırken bu bitleri dğeiştirmemize gerek kalmayacak, fonksiyon kendi ayarlıyor ama istersek fonksiyon kullanmadan da kesmeleri ayarlayabiliriz).

Kesme İşlemleri bölümünde ‘de bahsedildiği gibi dış kesme oluşturmak için INTCON ve INTCON2 register’ları kullanılır.

  • INT0IE: INT0 dış kesmesini enable/disable yapar. (Ör: INTCONbits.INT0IE = 1)
  • INT0IF: INT0 dış kesmesi oluştuğunda bu flag set(1) olur.
  • INT1IE: INT1 dış kesmesini enable/disable yapar. (Ör: INTCON3bits.INT1IE = 1)
  • INT1IF: INT1 dış kesmesi oluştuğunda bu flag set(1) olur.
  • INT2IE: INT2 dış kesmesini enable/disable yapar. (Ör: INTCON3bits.INT2IE = 1)
  • INT2IF: INT2 dış kesmesi oluştuğunda bu flag set(1) olur.

Kesmelerin tetiklenme durumunu ayarlamak için ise INTCON2 register’ı kullanılır.

  • INTEDG0: INT0 dış kesmesinin tetiklenme durumunu belirler.

              INTCON2bits.INTEDG0 = 0 ise düşen kenarda,
              INTCON2bits.INTEDG0 = 1 ise yükeselen kenarda tetiklenir. 

  • INTEDG1: INT1 dış kesmesinin tetiklenme durumunu belirler.

              INTCON2bits.INTEDG1 = 0 ise düşen kenarda,
              INTCON2bits.INTEDG1 = 1 ise yükeselen kenarda tetiklenir. 

  • INTEDG2: INT2 dış kesmesinin tetiklenme durumunu belirler.

              INTCON2bits.INTEDG2 = 0 ise düşen kenarda,
              INTCON2bits.INTEDG2 = 1 ise yükeselen kenarda tetiklenir.              

Dış kesmelerin tetiklenme durumlarını ayarladıktan sonra kesmeleri aktifleştirirsek ve gerekli modülleri kullanıma kapatmışsak, interrupt mekanizmamız düzgün çalışır. Fakat bu biraz meşakatli bir iş. Bazen tüm register bitleri doğru ayarlanmış olsa bile bazı ayarları gözden kaçırabiliyoruz. C18 dilinde her şeyi registerlar ile yapmak yerine önce o işlemi yapmak için herhangi bir fonksiyon tanımlı mı diye araştırmak gerekir. Eğer fonksiyonu varsa programları mümkün olduğunda fonksiyonel yazmak programın verimi açısından faydalı olacaktır.

Bu bölümdeki kodlarda kullanacağımız fonksiyonlar şu şekildedir;

  • OpenRB0INT(): RB0 pinine bağlı kesmeyi açar.
  • OpenRB1INT(): RB1 pinine bağlı kesmeyi açar.
  • OpenRB2INT(): RB2 pinine bağlı kesmeyi açar.
  • CloseRBxINT(): İlgili pine bağlı kesmeyi kapatır. (x = 1,2,3)

Not: I/O portlarıyla ilgili fonksiyonlar hakkında geniş bilgi C18 Kütüphaneleri ve Özellikleri bölümünde verilmektedir.

Bu bölümde yazacağımız programda amaç RB0 pinine bağlı butona basıldığında RB3 pinine   bağlı ledi 5 defa yakıp söndürmek, RB1 pinine bağlı butona basıldığında RB4 pinine bağlı ledi yakıp söndürmek. RB2 pinine baplı butona basılıp çekildiğinde RB5 pinine bağlı ledi 5 defa yakıp söndürmek.

Program kodları aşağıdaki şekilde olacaktır;

<pre>/*
 * File:   ext_interrupt_main.c
 * Author: cagatay
 *
 * Created on 26 Ocak 2012 Per?embe, 10:23
 */

#include <p18f4585.h>
#include <stdio.h>
#include <timers.h>
#include <delays.h>
#include <adc.h>
#include <portb.h>

#pragma config OSC = HS, WDT = OFF, LVP = OFF

int i;

void int_handler(void);

#pragma code high_vector=0x08
void high_interrupts (void) {
    _asm GOTO int_handler _endasm
}

#pragma code low_vector=0x18
void low_interrupts (void) {
    _asm GOTO int_handler _endasm
}

#pragma code
#pragma interrupt int_handler
void int_handler(void) {

    if(INTCONbits.INT0IF == 1) {

       INTCONbits.GIE = 0;

       for(i=0;i<5;i++) {
            PORTBbits.RB3 = 1;
            Delay10KTCYx(10);
            PORTBbits.RB3 = 0;
            Delay10KTCYx(10);
       }

       INTCONbits.INT0IF = 0;
       INTCONbits.GIE = 1;
    }

    if(INTCON3bits.INT1IF == 1) {
       INTCONbits.GIE = 0;

       for(i=0;i<5;i++) {
            PORTBbits.RB4 = 1;
            Delay10KTCYx(10);
            PORTBbits.RB4 = 0;
            Delay10KTCYx(10);
       }

       INTCON3bits.INT1IF = 0;
       INTCONbits.GIE = 1;
    }

    if(INTCON3bits.INT2IF == 1) {
        INTCONbits.GIE = 0;

        for(i=0;i<5;i++) {
            PORTBbits.RB5 = 1;
            Delay10KTCYx(10);
            PORTBbits.RB5 = 0;
            Delay10KTCYx(10);
        }

        INTCON3bits.INT2IF = 0;
        INTCONbits.GIE = 1;
    }
}

void main(void) {

    TRISB = 0x07;

    CloseTimer1();
    CloseTimer2();

    ADCON1 = 0x0F; //B portunun tüm girisleri dijital

    PORTB = 0;

    OpenRB0INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT);
    OpenRB1INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT);
    OpenRB2INT(PORTB_CHANGE_INT_ON & RISING_EDGE_INT);

    INTCONbits.GIE = 1;

    while(1);

}

Satır 17’de ilgili pine baplı ledin 5 defa yanıp sönmesi için i değişkeni tanımlanmıştır. Satır 33’te interrupt kodlarımızı yazacağımız int_handler() fonksiyonuna giriliyor. Bu sefer aynı   programda 3 adte interrupt kullanacağımız için int_handler içerisinde 3 ayrı flag’i kontrol   ediyoruz. Herhangi bir kesme oluştuğunda bu 3 flag’den hangisinin set(1) olduğuna karar verip interrupt kodlarını ona göre çalıştıracağız.

Satır 35’te INT0 interrupt flag’i, satır 50’de INT1 interrupt flag’i, satır 64’te INT2 interrupt flag’i kontrol edilmektedir. İlgili kesmeye göre yapılacak işlemler bu satırlarda yapılan kontroller sonucunda belirlenmektedir.

Satır 37, 51 ve 65’te global kesmeler kapatılmıştır. Bu kesmelerin kapatılma nedeni; 3 ayrı kesme aynı programda çalıştırılacağından dolayı bu 3 kesmenin birbirini etkilemesini engellemektir. Biz RB0 pinine bağlı butona bastıktan sonra daha onun işlevi bitmeden RB1 butonuna basarsak, RB1 butonu RB0 butonunun tetiklediği interrupt’ın işini etkilemeyecek. Satır 47, 61 ve 75’te global interruptlar tekrar kullanıma açılmıştır.

Satır  81’de B portu RB0, RB1 ve RB2 pinleri giriş, diğer pinler çıkış olacak şekilde ayarlanıyor. Satır 83 ve 84’de Timer1 ve Timer2 modülleri kapatılıyor (Bu modülleri program çalışırken kullanmayacaksanız kapatmanız faydalı olur).

Satır 86’da B portundaki RB0, RB1, RB2, RB3 pinleri dijital giriş çıkış için ayarlanıyor.

Not: Hatırlanırsa ADC modülü anlatılırken ADCON bitlerinin dijital/analog giriş çıkışlarını ayarladığına değinilmişti. OpenADC() fonksiyonuyla verdiğimiz parametrelerden    birinin 1-15 arası tamsayı olduğuna, bu tamsayının analog/dijital giriş çıkışları ayarladığına değinmiştik. O sayı buradaki ADCON register’ının son 4 bitini temsil etmekte. Bizim girdiğimiz 0x07 değeri OpenADC() fonksiyonunda 7 değeri olarak da verilebilirdi. O zamanda aynı işlevi görmüş olurdu.

Satır 90,91 ve 92’de RB0, RB1 ve RB2 pinleri kesme oluşturacak şekilde ayarlanıyor.

  • PORTB_CHANGE_INT_ON: PORTB pinlerinde değişim meydana geldiğinde kesme oluştur.
  • FALLING_EDGE_INT: Oluşacak kesmeler düşen kenarlarda tetiklensin.
  • RISING_ENGE_INT: Oluşacak kesmeler yükselen kenarlarda tetiklensin.

Dikkat edilirse RB0 ve RB1 pini açılırken interrupt düşen kenarda tetiklenecek şekilde ayarlanmış (Satır 90, 91), RB2 pini açılırken interrupt yükselen kenarda tetiklenecek şekilde ayarlanmıştır (Satır 92). Mikrodenetleyici bu program ile çalışırken RB0, RB1, RB2 pinleri; basılı durumda 0, diğer durumda 1 sinyalini verecek şekilde ayarlanmış butonlara bağlanırsa RB0 ve RB1 pinlerine bağlı butonlara basıldığında ilgili ledler 5 defa yanıp sönecek, RB2 pinine bağlı butona basıldığında ise hiçbir değişim olmayacak fakat bastıktan sonra butondan el çekildiğinde ilgili led 5 defa yanıp sönecektir.

Not: Bu örneği interrupt kullanmadan da yapabilirdik fakat o zaman programın tek yapacağı iş sürekli olarak bu pinleri sinyal geldi mi gelmedi mi diye kontrol etmek olacaktı. Bizim istediğimiz bu değil. Butonlardan herhangi birinde bir değişim meydana geldiğinde eğer istediğimiz tetiklenme durumu oluşmuşsa program akışı kesilmekte ve ilgili kodlar çalıştırılmakta. Böylece program daha verimli çalışmakta. 

Program kodunda interrupt tetiklenmesiyle ilgili değişiklikler yapılarak butonların çalışma mekanizması gözlemlenebilir. Bu bölümde butonları sinyal oluşturmak için kullandık. İlerleyen bölümlerde okuduğumuz başka sinyaller için de external interrupt’ları kullanacağız.

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s