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;
#include <p18f4550.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);

}

Kaynak:http://elkitabim.net

Yorumlar