Önceki yazımızda PIC16F84A’nın pinlerini kontrol etmeyi işlemiştik. Elbette ki buraya kadarki bilgilerle çok sayıda uygulama yapmak mümkün ancak bu yazı dizisi altyapı oluşturma amacı taşıdığından şimdilik farklı uygulamaları geleceğe bırakarak altyapı çalışmalarına devam diyoruz. Mikrokontrolör’ün giriş çıkış arayüzlerini detaylıca inceledikten sonra sıra geldi kesmelere. Önce genel manada kesmelerin ne olduğuna değinip, ardından da port ve pin kesmeleri ile devam edeceğiz. Mikrokontrolör’ün çevresellerinden bazıları farklı tipte kesmeler destekler. Örneğin zamanlayıcı kesmelerine, zamanlayıcı modülünde değineceğiz. Hazır port/pin kontrolünü görmüşken bu yazıda port/pin kesmelerine değineceğiz.
Kesme nedir demeden önce, mikrokontrolör ve CPU arasındaki farkları hatırlayalım. CPU (işlemci, merkezi işlem birimi) mikrokontrolör içerisindeki bir modüldür. Mikrokontrolör içinde, CPU’ya ek olarak bellek ve çevreseller (perihperal) bulunur. CPU içerisinde genelde aritmetik/lojik işlemleri yapan bir modül (ALU) bulunur. Hemen PIC16F84A’nın yapısını inceleyelim. Bu blog diyagram PIC16F84A’nın veri kağıdından alındı.
Diyagramda ALU’ya dikkat ediniz. Onu CPU olarak düşünebilirsiniz. ALU’dan W reg’e giden yolda sağa doğru bir kanal olduğunu göreceksiniz. O kanal I/O portlarına, TMR (timer, zamanlayıcı) modüllerine ve daha nice yerlere gidiyor. Bu kaçak kanal, olası bir kesme sinyalinin ALU’ya müdahale edebildiğinin bir kanıtı. Bu yazının sonunda da resmin sağ alt kısmında yer alan RB0/INT’in ne olduğu anlatılacak.
Kesme Nedir?
Kesmeleri tanımlamak uygulamada göstermekten zor bir iştir ama şöyle bir tanım yapmak mümkün: Kesme (interrupt), CPU’nun dışındaki bir dış etken tarafından oluşturulan ve oluştuğunda CPU’nun yaptığı işi durdurup, ayrı bir özel kod parçacığının işletilmesini (yürütülmesini) sağlayan elektriksel bir işarettir. Kesme oluştuğunda işletilen bu koda genelde kesme servis rutini (interrupt service routine, ISR) denir. Kesme oluştuğu anda, ISR’ye gitmeden önce, CPU program işletiminde kaldığı yeri kaydeder. ISR’nin işletimi bittiği anda da CPU bu kayıtlı noktadan yani kaldığı yerden programı işletmeye devam eder.
Kesme asenkron gerçekleşen bir olaydır, yani ne zaman gerçekleşeceğini CPU önceden bilmez. Gündelik hayattan buna benzer bir süreç örneği verecek olursak; kapı zilinin ya da telefonun çalması durumlarını örnek verebiliriz. Diyelim yemek yapıyorsunuz, zil çaldı; yemek yapmayı bırakıp kapıyı açıp yemek yapmaya devam ederiz. Zil çalınca, yahu dur sırası mıydı demeyiz. Ya da sürekli kapıyı kontrol etmeden zilin ne zaman çalacağını bilemeyiz. Bununla birlikte, kapının çalışını duymazdan gelmeniz bir seçenektir tabi ki. Aynı şeyler kesmeler için de geçerli 🙂
Kesmeler, gömülü sistemlerde çok çok önemli bir yere sahiptir. Kesmeler’in düzgün kullanımı ile tasarlanmış bir sistem daha verimli, daha duyarlı (responsive) ve daha anlaşılır şekilde tasarlanabilir. Kesmeler düzgün kullanılmazsa da “yaa böyle olmaması lazımdı ama, çok saçma, böyle çalışmaması lazımdı ama” sözleri havada uçuşur, işin içinden çıkılmaz. Halbuki dostlar, her şey deterministik 🙂 Çok güçlü ama biraz da tehlikeli olduklarından bazı yazılımcılar kesmelerden korkar ama gömülü sistem programcısı kesmeden korkmamalı, kesmelerle samimi olmalı, onları son derece işeyarar ve güçlü bir arkadaş olarak görmelidir. Zira, etrafınızda gördüğünüz gömülü sistemlerden en az bir kesme kullanmayanı bulmak sıradışı bir olaydır. Sözün özü, kesme candır.
Farklı mikrokontrolör mimarilerinde kesmeler farklı şekillerde ele alınır. Şimdi PIC’ten başlayalım. Yine ibretlerle dolu bir uygulama yapacağız.
Switch ve Led Uygulaması
Bir önceki yazımıza yaptığımız uygulamada, while döngüsünün içerisinde switch’in durumunu sürekli kontrol etmiştik. Buna polling deniyor ve ekstrem derecede verimsiz bir yöntemdir bu. Bunun yerine kesme kullansaydık, sadece anahtarın durumu değiştiğinde ledlerin durumuna müdahale edebileceğimiz bir senaryo oluşacaktı. Bu da ileride özellikle güç tüketimi anlamında da bize avantajlar sağlayacaktı. Haydi sağlasın madem. Uygulamayı bu defa kesme kullanarak yapacağız. Yine switch bir konumda iken kırmızı yanar sarı söner durumda olacak, switch diğer konumdayken sarı yanar kırmızı söner durumda olacak. Yine bu uygulamada da kod yazımımızı bir kaç adım öteye taşıyacağız ancak önce kaldığımız yerden devam edelim 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
/* * File: main.c * Author: ozenozkaya * * Created on 02 Haziran 2015 Sali, 12:47 */ #define F_OSC (1000000UL) #define REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile unsigned char*)REG_TRISB_ADDR) #define TRIS_PIN_OUTPUT (0) #define TRIS_PIN_INPUT (1) #define TRIS_PORT_OUTPUT (0) #define TRIS_PORT_INPUT (0xFF) #define REG_PORTB_ADDR (0x06) #define REG_PORTB (*( volatile unsigned char*)REG_PORTB_ADDR) #define PORT_PIN_LOW (0) #define PORT_PIN_HIGH (1) #define PORT_ALL_LOW (0) #define PORT_ALL_HIGH (0xFF) #define SWITCH_PIN pins.pin0 #define RED_LED_PIN pins.pin1 #define YELLOW_LED_PIN pins.pin2 #define LED_ON (PORT_PIN_HIGH) #define LED_OFF (PORT_PIN_LOW) #define REG_INTCON_ADDR (0x0B) #define REG_INTCON (*( volatile unsigned char*)REG_INTCON_ADDR) #define INT_ENABLE (1) #define INT_DISABLE (0) #define INT_FLAG_CLEAR (0) #define REG_OPTION_ADDR (0x81) #define REG_OPTION (*( volatile unsigned char*)REG_OPTION_ADDR) #define INT_EDGE_FALLING (0) #define INT_EDGE_RISING (1) #define BIT_TOGGLE(x) (x^=1) typedef struct { unsigned char pin0:1; unsigned char pin1:1; unsigned char pin2:1; unsigned char pin3:1; unsigned char pin4:1; unsigned char pin5:1; unsigned char pin6:1; unsigned char pin7:1; }tris_port_pin_t; typedef struct { unsigned char RBIF:1; unsigned char INTF:1; unsigned char T0IF:1; unsigned char RBIE:1; unsigned char INTE:1; unsigned char T0IE:1; unsigned char EEIE:1; unsigned char GIE:1; }intcon_reg_t,*intcon_reg_ptr_t; typedef struct { unsigned char PS:3; unsigned char PSA:1; unsigned char T0SE:1; unsigned char T0CS:1; unsigned char INTEDG:1; unsigned char RBPU:1; }option_reg_t,*option_reg_ptr_t; typedef union { tris_port_pin_t pins; volatile unsigned char port; }tris_port_t,*tris_port_ptr_t; tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; tris_port_t *trisb_ptr=(tris_port_ptr_t)REG_TRISB_ADDR; intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; static void interrupt global_isr_handler() { if(intcon_reg_ptr->INTF) { switch(portb_ptr->SWITCH_PIN) { case PORT_PIN_LOW: portb_ptr->RED_LED_PIN = LED_ON; portb_ptr->YELLOW_LED_PIN = LED_OFF; break; case PORT_PIN_HIGH: portb_ptr->RED_LED_PIN = LED_OFF; portb_ptr->YELLOW_LED_PIN = LED_ON; break; default: break; } BIT_TOGGLE(option_reg_ptr->INTEDG); intcon_reg_ptr->INTF=INT_FLAG_CLEAR; } } void main() { trisb_ptr->port = TRIS_PORT_INPUT; trisb_ptr->SWITCH_PIN = TRIS_PIN_INPUT; trisb_ptr->RED_LED_PIN = TRIS_PIN_OUTPUT; trisb_ptr->YELLOW_LED_PIN = TRIS_PIN_OUTPUT; portb_ptr->port = PORT_ALL_LOW; intcon_reg_ptr->INTF = INT_FLAG_CLEAR; intcon_reg_ptr->INTE = INT_ENABLE; intcon_reg_ptr->GIE = INT_ENABLE; option_reg_ptr->INTEDG = INT_EDGE_RISING; while(1); } |
Kodun çalıştırılacağı devre bir önceki yazıdakilerle aynı. Yani çıktı aşağıdaki gibi olacak.
Şimdi gelelim fasülyenin faydalarına. Kesmeyi nasıl kullanacağımızı nereden bildik? PIC16F84A’nın veri kağıdının 6.8 Interrupts başlıklı kısmında hangi registerları nasıl konfigüre etmemiz gerektiği açıkça yazıyor. Bildiğiniz gibi bu yazı dizisinde kendi yazdıklarımızdan başka bir kütüphane kullanmıyoruz. Bunun amacı her şeyi temelinden anlamaktı. Doğal olarak kullanacağımız yeni registerlar için de tanımlamaları yapıyoruz. Aşağıda OPTION ve INTCON kütüklerinin (registerlarının) adres tanımlarını ve yardımcı etiketleri görebilirsiniz. Buna ek olarak bir de BIT_TOGGLE makrosu tanımladık. Bu makronun amacı bir biti birse sıfır, sıfırsa bir yapmak. Bunu ileride kullanacağız.
1 2 3 4 5 6 7 8 9 10 11 12 |
#define REG_INTCON_ADDR (0x0B) #define REG_INTCON (*( volatile unsigned char*)REG_INTCON_ADDR) #define INT_ENABLE (1) #define INT_DISABLE (0) #define INT_FLAG_CLEAR (0) #define REG_OPTION_ADDR (0x81) #define REG_OPTION (*( volatile unsigned char*)REG_OPTION_ADDR) #define INT_EDGE_FALLING (0) #define INT_EDGE_RISING (1) #define BIT_TOGGLE(x) (x^=1) |
Registerların modellenmesini ise aşağıdaki gibi yaptık.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
typedef struct { unsigned char RBIF:1; unsigned char INTF:1; unsigned char T0IF:1; unsigned char RBIE:1; unsigned char INTE:1; unsigned char T0IE:1; unsigned char EEIE:1; unsigned char GIE:1; }intcon_reg_t,*intcon_reg_ptr_t; typedef struct { unsigned char PS:3; unsigned char PSA:1; unsigned char T0SE:1; unsigned char T0CS:1; unsigned char INTEDG:1; unsigned char RBPU:1; }option_reg_t,*option_reg_ptr_t; |
Bu tanımlamaları datasheetten bakıp yaptıktan sonra INTCON ve OPTION kütüklerini gösterecek işaretçileri aşağıdaki gibi tanımladık.
1 2 |
intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; |
Ardından gelelim ana fonksiyon(main) içinde yaptığımız ek konfigürasyonlara.
1 2 3 4 5 |
option_reg_ptr->INTEDG = INT_EDGE_RISING; intcon_reg_ptr->INTF = INT_FLAG_CLEAR; intcon_reg_ptr->INTE = INT_ENABLE; intcon_reg_ptr->GIE = INT_ENABLE; |
Öncelikle kesme bayrağının temiz olduğundan emin olmak için onu temizledik. Ardından INTE ile RB0/INT kesmesine izin verdik. Ardından da global kesme aktivasyonu için GIE bitini etkinleştirdik. GIE ve INTE bitlerinin ikisi de 1 değerini almadan kesme gerçekleşemez. Bunu da verikağıdındaki aşağıdaki şemadan anlayabiliyoruz.
Konfigürasyonda kesmeyi de yükselen kenarda olacak şekilde ayarladık. Buna göre anahtar sıfırdan bire çekildiğinde yükselen kenar kesmesi gelecek. Ama burada bir sorun var, bize hem yükselen kenar hem düşen kenarda kesme gerekiyor. Çünkü anahtarın konumu sıfırdan bire değiştiğinde de birden sıfıra değiştiğinde de kesme almak ve LED’lerin durumunu değiştirmek istiyoruz. PIC’te böyle doğrudan böyle bir seçenek yok; ya yükselen kenar kesmesi seçilebiliyor ya da düşen kenar. Burada beyin bedava diyoruz ve kafayı çalıştırarak soruna çözüm sunuyoruz. Önce yükselen kenar kesmesini aktive ettiğimizi düşünelim, bu durumda ilk gelen yükselen kenar kesmesinin hemen ardından düşen kenar kesmesini aktive edersek, switchin durumu tekrar değiştiğinde kesme alabiliriz. Yani yükselen/düşen kenar konfigürasyonunu, kesme oluştuğunda toggle etmemiz gerekiyor. Kesme tamamlandığında kesme bayrağını yani INTF bitini de sıfıra çekip temizlemek gerekiyor. Kodda da bunları yaptık. PIC’te kesme fonksiyonu fonksiyon adının başına gelen “interrupt” kelimesi ile derleyiciye anlatılır. Buna göre kesme fonksiyonumuz aşağıdaki gibi oluyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
static void interrupt global_isr_handler() { if(intcon_reg_ptr->INTF) { switch(portb_ptr->SWITCH_PIN) { case PORT_PIN_LOW: portb_ptr->RED_LED_PIN = LED_ON; portb_ptr->YELLOW_LED_PIN = LED_OFF; break; case PORT_PIN_HIGH: portb_ptr->RED_LED_PIN = LED_OFF; portb_ptr->YELLOW_LED_PIN = LED_ON; break; default: break; } BIT_TOGGLE(option_reg_ptr->INTEDG); intcon_reg_ptr->INTF=INT_FLAG_CLEAR; } } |
PIC’te tüm kesmeler tek bir fonksiyon içine düşer, bu da interrupt ön ekli fonksiyondur. Bu sebeple bu fonksiyonun adını global_isr_handler koyduk. Daha sonra gelen kesmenin RB0/INT olup olmadığını kontrol ettik ve eğer o ise switch değerlendirmesi işlemini yapıp yükselen/düşen kenar seçimini güncelleştirip bayrağı temizledik. INTEDG’nin toggle edilmesi ile hem düşen hem yükselen kenardaki değişimleri güzel bir oyun ile algılayabilir duruma geldik. Çiçek oldu.
Şimdi gelelim bu kodun sorunlarına. Birincisi register tanımları giderek kabarıyor ve onların ayrı bir *.h dosyası içinde yer alması gerekiyor artık. İkincisi, ilk durumda switch’in durumuna bakmaksızın kesme konfigurasyonu yaptığımız için ilk açılışta ledlerin ikisi de yanmıyor. Üçüncüsü de şu, madem her kesme aynı fonksiyona düşecek, ileride kod daha da büyüdüğünde kesme fonksiyonu sürekli değiştirilecek ve eğer birisi orada yanlış bayrakları temizlerse ya da kurarsa, önceden yazılmış kısımlar da bundan etkilenecek ve kodun her yeri bozulacak. Bu çok büyük bir risk. Burada SDK tasarımı devreye giriyor. Akıllıca bir seçenek, kesme fonksiyonunu kullanıcı programından ayırmak olacaktır. Burada kesme bayraklarının yönetiminin de otomatik yapılması da isabetli olacaktır. Bu olay gerçekten bir ileri tasarım öğesidir ancak adım adım oraya gideceğiz. Kodun bir diğer önemli sorunu da kütüphane kullanmayacağız inadımızla kullanmadığımız stdint kütüphanesidir. “unsigned char” gibi boyutu “hayırlısı” olan bir veri tipi kullanmak yerine “uint8_t” ile modelleme yapmamız daha temiz olurdu. Nitekim amacımız PIC kütüphanelerini kullanmadan, daha iyi şekilde temelden olayı yazarak olayın dinamiklerini anlamaktı. Bu da stdint’e geçişimizi engellemiyor. Bir diğer eksik nokta da register tanımlarıyla ilgili. Önceki yazıda, sebeplerini sıraladığımız üzere hem bit hem de byte erişimi sağlayabilmek için kütük modellemelerinde union kullanmıştık. Yeni modellediğimiz kütüklerde de bunu uygulamakta fayda var. Bunlara göre projemizi yine düzenleyelim 🙂
Switch ve LED Uygulaması v2
Bu projede 3 adet dosyamız olacak. Birincisi pic16f84a kütüphanemizin header dosyası. Bu dosya (pic16f84a_lib.h) aşağıdaki gibidir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
/* * File: pic16f84a_lib.h * Author: ozenozkaya * * Created on 20 Temmuz 2015 Pazartesi, 00:05 */ #ifndef PIC16F84A_LIB_H #define PIC16F84A_LIB_H #ifdef __cplusplus extern "C" { #endif #include <stdint.h> #define F_OSC (1000000UL) #define REG_TRISB_ADDR (0x86) #define REG_TRISB (*( volatile unsigned char*)REG_TRISB_ADDR) #define TRIS_PIN_OUTPUT (0) #define TRIS_PIN_INPUT (1) #define TRIS_PORT_OUTPUT (0) #define TRIS_PORT_INPUT (0xFF) #define REG_PORTB_ADDR (0x06) #define REG_PORTB (*( volatile unsigned char*)REG_PORTB_ADDR) #define PORT_PIN_LOW (0) #define PORT_PIN_HIGH (1) #define PORT_ALL_LOW (0) #define PORT_ALL_HIGH (0xFF) #define REG_INTCON_ADDR (0x0B) #define REG_INTCON (*( volatile unsigned char*)REG_INTCON_ADDR) #define INT_ENABLE (1) #define INT_DISABLE (0) #define INT_FLAG_CLEAR (0) #define REG_OPTION_ADDR (0x81) #define REG_OPTION (*( volatile unsigned char*)REG_OPTION_ADDR) #define INT_EDGE_FALLING (0) #define INT_EDGE_RISING (1) #define MAX_INT_CALLBACK_NUM (5) typedef struct { uint8_t pin0:1; uint8_t pin1:1; uint8_t pin2:1; uint8_t pin3:1; uint8_t pin4:1; uint8_t pin5:1; uint8_t pin6:1; uint8_t pin7:1; }_tris_port_pin_t, *_tris_port_pin_ptr_t; typedef struct { uint8_t RBIF:1; uint8_t INTF:1; uint8_t T0IF:1; uint8_t RBIE:1; uint8_t INTE:1; uint8_t T0IE:1; uint8_t EEIE:1; uint8_t GIE:1; }_intcon_reg_t,*_intcon_reg_ptr_t; typedef struct { uint8_t PS:3; uint8_t PSA:1; uint8_t T0SE:1; uint8_t T0CS:1; uint8_t INTEDG:1; uint8_t RBPU:1; }_option_reg_t,*_option_reg_ptr_t; typedef union { _tris_port_pin_t pins; uint8_t port; }tris_port_t,*tris_port_ptr_t; typedef union { _option_reg_t bits; uint8_t value; }option_reg_t, *option_reg_ptr_t; typedef union { _intcon_reg_t bits; uint8_t value; }intcon_reg_t, *intcon_reg_ptr_t; typedef void (*rb0_int_callback_t)(); int8_t register_rb0_int_callback(rb0_int_callback_t new_cb); int8_t unregister_rb0_int_callback(rb0_int_callback_t cb_to_unreg); #ifdef __cplusplus } #endif #endif /* PIC16F84A_LIB_H */ |
Yine pic16f84a kütüphanemizin kaynak dosyası (pic16f84a_lib.c) aşağıdaki gibidir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
#include "pic16f84a_lib.h" #define BIT_TOGGLE(x) (x^=1) #ifndef NULL #define NULL ((void*)0) #endif rb0_int_callback_t rb0_int_callback_list[MAX_INT_CALLBACK_NUM]; static void interrupt global_isr_handler() { intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; uint32_t cb_index=0; if(intcon_reg_ptr->bits.INTF) { for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL != rb0_int_callback_list[cb_index]) { rb0_int_callback_list[cb_index](); } } BIT_TOGGLE(option_reg_ptr->bits.INTEDG); intcon_reg_ptr->bits.INTF=INT_FLAG_CLEAR; } } int8_t register_rb0_int_callback(rb0_int_callback_t new_cb) { uint32_t cb_index=0; if(NULL != new_cb) { for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(NULL == rb0_int_callback_list[cb_index]) { rb0_int_callback_list[cb_index]=new_cb; } } if(MAX_INT_CALLBACK_NUM == cb_index) { return -2; //All callbacks are already registered } } else { return -1; // Callback is NULL } return 0; } int8_t unregister_rb0_int_callback(rb0_int_callback_t cb_to_unreg) { uint32_t cb_index=0; if(NULL != cb_to_unreg) { for(cb_index=0;cb_index<MAX_INT_CALLBACK_NUM; cb_index++) { if(cb_to_unreg == rb0_int_callback_list[cb_index]) { rb0_int_callback_list[cb_index]=NULL; } } } else { return -1; // Callback is NULL } return 0; } |
Ve nihayetinde ana programımız (main.c) aşağıdaki gibidir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
/* * File: main.c * Author: ozenozkaya * * Created on 20 Temmuz 2015 Sali, 23:47 */ #include "pic16f84a_lib.h" #define SWITCH_PIN pins.pin0 #define RED_LED_PIN pins.pin1 #define YELLOW_LED_PIN pins.pin2 #define LED_ON (PORT_PIN_HIGH) #define LED_OFF (PORT_PIN_LOW) static tris_port_t *portb_ptr=(tris_port_ptr_t)REG_PORTB_ADDR; static tris_port_t *trisb_ptr=(tris_port_ptr_t)REG_TRISB_ADDR; static void switch_event_manager() { switch(portb_ptr->SWITCH_PIN) { case PORT_PIN_LOW: portb_ptr->RED_LED_PIN = LED_ON; portb_ptr->YELLOW_LED_PIN = LED_OFF; break; case PORT_PIN_HIGH: portb_ptr->RED_LED_PIN = LED_OFF; portb_ptr->YELLOW_LED_PIN = LED_ON; break; default: break; } } static void initialize_switch_and_leds() { trisb_ptr->port = TRIS_PORT_INPUT; trisb_ptr->SWITCH_PIN = TRIS_PIN_INPUT; trisb_ptr->RED_LED_PIN = TRIS_PIN_OUTPUT; trisb_ptr->YELLOW_LED_PIN = TRIS_PIN_OUTPUT; portb_ptr->port = PORT_ALL_LOW; switch_event_manager(); } static void initialize_switch_interrupt() { intcon_reg_t *intcon_reg_ptr= (intcon_reg_ptr_t)REG_INTCON_ADDR; option_reg_t *option_reg_ptr = (option_reg_ptr_t)REG_OPTION_ADDR; register_rb0_int_callback(switch_event_manager); option_reg_ptr->bits.INTEDG = INT_EDGE_RISING; intcon_reg_ptr->bits.INTF = INT_FLAG_CLEAR; intcon_reg_ptr->bits.INTE = INT_ENABLE; intcon_reg_ptr->bits.GIE = INT_ENABLE; } void main() { initialize_switch_and_leds(); initialize_switch_interrupt(); while(1); } |
Kodda, vadettiğimiz tüm değişiklikleri yapmış olduk. En en en en en önemli yer, kullanıcının yani main dosyasının kesme fonksiyonunu hiç bir şekilde görmeden kesme işlemlerini haber alması. Bunun için SDK geliştiricisi olarak çıkardığımız fonksiyonu kullancı aşağıdaki şekilde çağırmış oldu:
1 |
register_rb0_int_callback(switch_event_manager); |
Bu fonksiyonun implementasyonu da tam anlamıyla ibretlik. Maksimum 5 kaynak RB0/INT kesmesine abone olabiliyor. Bu genişletilebilirlik açısından çok çok önemli. Biz switch kodunu yarın öbürgün “switch geldiğinde bir de düdük çalsın” isteriyle genişletmek istediğimizde düdüğü çalacak fonksiyonu da register edecersek o düdük çalınacaktır ve bizim önceki işlerimiz asla bundan etkilenmeyecektir. Bu olaya lütfen çok dikkat ediniz. Listeye kaydolmuş fonksiyonların yani callback’lerin çağırıldığı satır ise aşağıdaki satır.
1 |
rb0_int_callback_list[cb_index](); |
Şimdilik bu kadar 🙂 Bu gün epey fantastik mevzulara değindik. PIC deyip geçmemek gerek, bu basit alet için bile ileri düzey tasarım yapmak mümkün. Nitekim daha da ilerleyeceğiz. Yazılar devam edecek.
Yazıları beğendiyseniz, faydalanabilecek tanıdıklarınızla paylaşmayı unutmayınız.
Önceki Sayfa Sonraki Sayfa