CCS-C ile PIC Programlama, PIC Interrupts (Kesmeler)

Giriş

Bildiğiniz gibi bundan önceki yazımızda Timer’lar konusuna giriş yapmıştık. Bu konuya biraz ara verip kesmeler konusuna geçiş yapacağız. Timer’lar da dahil olmak üzere ilerde anlatacağımız donanımlarda kesmeler konusuna mecburen girmemiz gerekiyordu, bu yüzden hepsinden önce anlatıp diğer konularda da yeri geldikçe kesmelerle ilgili örenekler vermeye deva edeceğiz.

Kesmeler (Interrupts) konusu yeni başlayanlar için zor veya karmaşık gibi görünse de (benim için de öyleydi ilk başladığım zamanlar), CCS-C’de kullanımı oldukça basittir gözünüz korkmasın, CCS-C’nin pratikliğini sonuna kadar kullanacağız :)

Kesme Nedir?

Aslında bu yazımız tam bir kesme örneği oldu, Timer’lar konusunda üç adet yazı yazyınlayacağımızı belirmiştik ama bu konu tabiri caizse bodoslama bir şekilde araya girdi. :) Timer’lar yazı dizisi, kesmeler konusu ile kesilmiş oldu anlayacağınız. Elbetteki bu konuyu bitirdikten sonra Timer’lar konusuna kaldığımız yerden devam edeceğiz.

İşte mikro denetleyicilerdeki kesme’ler de aynı yukarıdaki örnekte olduğu gibidir. Herhangi bir kesme oluştuğunda denetleyici program akışını olduğu yerde bırakır ve kesme vektörüne giderek oradaki kodu işletir ve tekrar geriye dönerek kaldığı yerden devam eder. PIC içerisinde bulunan donanımların çoğu (Timer, Adc, ccp,usart vs) kesme oluşturabilme kapasitesine sahiptir. Bu kesmeler istenildiği gibi programlanarak devreye alınabilir veya devre dışı bırakılabilir. Şimdi kesme oluştuğunda nasıl işleme alındığını daha iyi anlayabilmek için PIC denetleyicilerinin genel porgram akışını inceleyelim

PIC Kesme Mekanizması

ORG    0000h
GOTO  ANA_KOD

ORG    0004h
:
:
:
RETFIE

ANA_KOD
:
:
:
PROGRAM_SONU

PIC ilk besleme geldiğinde yada resetlendiğinde program işletimi için ilk olarak #0000h adresine gelir ve buradan itibaren kodu işletmeye başlar. #0000h adresine PIC Reset Vector (reset vektörü) denir. bu adresten hemen sonra ana kodun başlangıç adresine dallanılır (GOTO ANA_KOD). PIC Denetleyicilerinin Kesme vektörü ise #0004h adresidir. Bu adrese kesme oluştuğunda işletilecek kod yerleştirilir. Normalde  ANA_KOD ile PROGRAM_SONU arasındaki kodlar işletilir, herhangi bir kesme oluştuğunda ise PIC #0004h adresine gider ve oradaki kodu işleterek RETFIE (Return from interrupt) komutunu gördüğü yerden geri döner ve program kaldığı yerden işetilmeye devam edilir.

Assembly dilinde programlama yapsaydık aynı yukarıdaki gibi adresleri belirterek kod yazmamız gerekecekti, biz  C ile kodladığımız için bu vektörleri belirtmemize gerek kalmıyor. Herhangi bir kesme rutini yazdığımızda CCS-C bu rutin için #0004h adresine yerleşen kesme kodlarını kendisi otomatik olarak yerleştiriliyor.

Şimdi akla şöyle bir soru gelmiş olası lazım; Program kesme rutinine gidip geri dönerken nasıl kaldığı yeri buluyor? Hemen cevaplayalım;

PIC Denetleyicisi işleyeceği komutun adresini PC (Program Counter) denilen register (yazmaç) ile tututar. Yani PC değeri o an işletilecek olan komutun adresini gösterir. Her komut işletildiğinde PC artırılarak bir sonraki komutu göstermesi sağlanır. Kesme rutinine gidilmeden önce sıradaki komut işletilmez ve PC değeri “Stack” (yığın) alanına Push (itilir) edilir. Kesme rutininden dönülürken de bu değer Stack alanından pull (çekilir) edilerek kaldığı yerden devam etmesi sağlanır.

PIC registerlerinin bazıları normal kod içerisinde kullanıldığı gibi kesme kodu içerisinde de kullanılır bu yüzden bazı registerleri kesme rutinine girmeden önce yedeklemek ve geri dönülmeden önce de eski değerlerini geri yüklemek gerekir. Örneğin kesmeye gidilmeden önce bank register hangi bank’ı gösteriyordu W registerinin içeriği neydi vs. İşte bu değerlerin yedeklenmesi olayına Context Saving denilmektedir. CCS-C kullananlar için iyi haber; derleyici bu işi de sizin yerinize yapar :). Biz yine de bu işin nasıl yapıldığını görelim. Aşağıdaki kod 16f628A teknik dökümanından alınmıştır.

MOVWF W_TEMP		; W registerini geçici W_TEMP  değişkenine aktar
SWAPF STATUS,W   	; STATUS registerini SWAP yap sonuç W’ye aktarılıyor
BCF STATUS,RP0 		; Bank 0’a geç
MOVWF STATUS_TEMP	; W’ değerini geçici STATUS_TEMP değişkenine aktar
:
:				;(Kesme kodları)
:
SWAPF STATUS_TEMP,W	; STATUS_TEMP swap yap sonuç W’ye aktarılır
MOVWF STATUS 		        ; Status geri yükleniyor
SWAPF W_TEMP,F 		; W_TEMP swap yapılıyor
SWAPF W_TEMP,W 		; W_TEMP tekrar swap yapılıp W’ye yükleniyor. W eski değerini alıyor
RETFIE				; Kesme rutininden geri dön
.

Kodda görüldüğü gibi interrupt rutinine girildiğinde STATUS ve W yedekleniyor, çıkarken de geri yükleniyor. Atamalar için neden SWAPF kullanıldığı sorusu gelebilir akla, nedeni SWAPF komutunun STATUS bayraklarını etkilememesi.

PIC denetleyicilerinde her interrupt için bir bitlik FLAG (Bayrak)’ler bulunur. Bu bayraklar kesmenin oluşup oluşmadığını gösterir. Örneğin Timer0 255 değerinden 0′a geçtiği anda TOIF (Timer0 Interrupt Flag) 1 olur ve Timer0 kesmesi enable (devreye alınmış) ise PIC kesme vektörüne gidecektir. Kesme rutininden çıkılmadan önce TOIF bayrağı sıfırlanmalıdır. “0″ (Sıfır)’lanmazsa PIC kesmeden çıktıktan sonra tekrar kesmeye gidecektir, bu yüzden mutlaka sıfırlanması gerekir. CCS-C derleyicisi bu işi de kendisi hallediyor.

Buraya kadar anlattıklarımız anlaşıldıysa şu bilgilerin hafızanızda yer etmiş olması gerekiyor.

  • Her hangi bir kesme oluştuğunda, PIC çalışmasını durdurarak kesme kodunu işletir ve geri dönüp kaldığı yerden devam eder.
  • PIC’in kesme vektörü 0x004h adresidir, kesmeyi servis edecek kod buradan başlar
  • Kesmeye girmeden önce bazı registerlar yedeklenmeli ve çıkarken eski değerleri geri yüklenmelidir.
  • Kesmeden çıkılmadan önce servis edilen kesmelerin bayrakları sıfıranmalıdır.

PIC Kesme Kaynakları

Şimdi sıra geldi PIC detleyicilerinde bulunan kesmelerin açıklamalarına, çalışacağınız PIC’in hangi kesmeleri desteklediğini teknik dökümanına bakarak bulabilirsiniz. Ek olarak CCS-C programında “View->Valid Interrupts” yolunu izleyerek listeden istediğiniz denetleyiciyi seçerek hangi interruptları kullanabileceğinizi öğrenebilirsiniz.

PIC Kesme kaynaklarından bazıları.

  • RTCC veya TIMER0: Timer0 taştığında ( overflow ) oluşan kemse
  • Timer1: Timer1 taştığında oluşan kesme
  • Timer2: Timer2 taştığında oluşan kesme
  • Timer3: Timer3 taştığında oluşan kesme
  • EXT: External Interrupt ( harici kesme) PIC’ın PORTB0 pini değiştiğinde oluşur.
  • EXT1,EXT2: Bazı pic modellerinde 3 adet harici kesme vardır
  • RB: PORTB Pin 4-7 pinlerinin değişmesi halinde oluşan kesme (Portb Cahnge Interrupt)
  • AD: Analogtan Dijitala dönüştürme işi tamamlandığında oluşan kesme.
  • PSP: Paralel Slave Port, veri okumaya hazır olduğunda oluşan kesme
  • RDA: R2232 Receive Data Available Interrupt, RS232 den veri alındığında oluşan kesme
  • TBE: RS232 Transmit Buffer Empty, RS232 Gönderme işlemi tamamlandığında oluşan kesme
  • SSP: SPI veya I2C aktivitesinde oluşan kesme
  • CCP1: Capture Compare on Unit 1, CCP1 kesmesi
  • CCP2: Capture Compare on Unit 2, CCP2 Kesmesi
  • EEPROM: Write Complete, dakili eeprom yazma işlemi bittiğinde oluşan kesme

CCS-C’de kesmelerin kullanımı

Öncelikle şurada daha önce anlatmış olduğumuz önişlemci direktfilerindeki #int_xxx kelimesi ile ilgili kısmı okuyun. Yazıyı okuduğunuza göre artık şunları biliyor olmalısınız

Bir kesmeye fonksiyon yazmak için

#int_xxx
xxx_isr()
{
... yapılacak
... islemler
}

xxx kısmına hangi kesme için kod yazıyorsanız onu yazacaksınız, #int_timer0, #int_ad, #int_rda vs. gibi. #int ifadesinden hemen sonra fonksiyon gelmelidir ve bu fonklsiyona istediğiniz ismi verebilirsiniz.
Örnekler

//Timer0 için interrupt rutini
#int_timer0
timer_kesme_rutini()
{
... //buraya kodlarımızı
... //yazacağız
}
//ADC için interrupt rutini
#int_ad
donusturme_isr()
{
... //buraya kodlarımızı
... //yazacağız
}

Projenizde hangi interruptlar kullanılıyorsa onların hepsine bu şekilde ayrı ayrı rutinler yazabilirsiniz. Her nekadar ayrı ayrı yazılmış gibi görünse de derleyici Kesme vektörüne bir kod yerleştirerek, interrupt hangi kesme kaynağından gelmişse onun kodunu çalıştıracak şekilde ayarlar.

Eğer siz ben bu işi derleyiciye bırakmak istemiyorum benim neyim eksik bende yaparım diyorsanız ozaman #int_global kelimesini kullanmanız gerekecek. Pek tavsiye etmediğim bu yöntem oldukça risklidir ve tecrübe ister. Derleyici yazdığınız kodu kesme vektörüne yerleştirmekten başka birşey yapmaz register yedeklemelerini sizin yapmanız gerekir. aşağıdaki örnek kullanıma bakalım

int save_w;
#locate save_w=0x7f
int save_status;
#locate save_status=0x20

#byte status = 3
#bit zero_flag = status.2
#bit t0if = 0xb.2

#INT_GLOBAL
void kesme_servisi()  {
#asm
//W ve STATUS yedekleniyor
MOVWF save_w
SWAPF status,W
BCF   status,5
BCF   status,6
MOVWF save_status
// Kodumuzda yedeklememiz gerektiğini düşündüğümüz
// başka değerleri de yine burada yedeklememiz gerekir
// eğer kodumuzda goto kullanıyorsak PCLATH yedeklemesi de yapmamız gerekir

if(T0IF) {   //gelen timer0 kesmesimi
if(++sayac == 100) {
sayac = 0;
}
TOIF = 0;   // timer0 kesme bayrağı temizleniyor
}
// bu sekilde kullanılan tüm kesmeler için if kontrolü ile gereki kodlama yazılabilir

// Yedeklenen değerler geri yükleniyor
SWAPF save_status,W
MOVWF status
SWAPF save_w,F
SWAPF save_w,W
#endasm
}

Gördüğünüz gibi oldukça zahmetli ve bilgi gerektiren bir iş. Yeni başlayanlar için hiç tavsiye etmiyorum.

Şimdi geldik esas mevzuya :) CCS-C de kesmeler için kullanılan hazır fonsksiyonları öğrenelim.

Kesmelerle ilgili fonksiyonları

  • enable_interrupts()
  • disable_interrupts()
  • clear_interrupt()
  • ext_int_edge()
  • interrupt_active()

enable_interrupts()

Bu fonksiyon herhangi bir kesmeyi devreye almaya yarar. Parametre olarak enable (devreye alma) edilecek kesme adı verilir. PIC’lerde bulunan GLOBAL kesmeyi aktif etmek için GLOBAL kelimesi kullanılır

enable_interrupts(INT_TIMER0);  // timer0 kesmesi aktif
enable_interrupts(INT_RDA);  // RS232 Receive kesmesi aktif
enable_interrupts(GLOBAL); // global interrup açık.

Yeri gelmişken belirtelim, PIC’lerde global interrupt denilen genel bir bayrak vardır, herhangi bir kesmenin çalışabilmesi için bu bayrağın set edilmesi gerekir. Örneğin kodumuzda sadece RDA kesmesini kullanacaksak aşağıdaki satırları yazmamız yeterli olacaktır.

enable_interrupts(INT_RDA);  // RS232 Receive kesmesi aktif
enable_interrupts(GLOBAL); // global interrup açık. açılmazsa rda da çalışmaz

GLOBAL interrupt açıksa aktif ettiğiniz kesmeler çalışır, kapalıysa aktif etmiş olduğunuz kesmeler çalışmaz.

disable_errupts()

Bu fonksiyon herhangi bir kesmeyi pasif yapmaya yarar. Parametre olarak disable (pasif) edilecek kesme adı verilir. GLOBAL kesmeyi pasif etmek için yine GLOBAL kelimesi kullanılır

disable_interrupts(INT_TIMER0);  // timer0 kesmesi pasif
disable_interrupts(INT_RDA);  // RS232 Receive kesmesi pasif
disable_interrupts(GLOBAL); // hiç bir interrupt çalıştırılmayacak.

clear_interrupt()

Daha önce kesmele ait bayraklar olduğunu ve kesme oluşunca bu bayrakların set edildiğini söylemiştik. Bu fonksiyon parametre olarak verilen kesmeye ait bayrağı temizlemek (sıfır yapmak) için kullanılır.

clear_interrupt(INT_TIMER0); // TOIF (timer0 interrupt flag) sıfırlanır

ext_int_edge()

PIC’lerde harici kesme oduğundan bahsetmiştik, bu kesme aktifleştirildiğinde dışarıdan gelen sinyalin durumuna göre kesme tetiklenir. Bu aonksiyon ile sinyalin hangi kenarında kesme oluşturulacağı ayarlanabilir.

ext_int_edge(L_TO_H); // sinyalin yükselen kenarında kesme oluştur
ext_int_edge(H_TO_L); // sinyalin düşen kenarında keme oluştur

Peki birden fazla harici kesmesi olan PIC’lerde ne yapacağız? bu durumda ikinci bir parametre olarak hangisine işlem yaptığımızı belirtebiliyoruz.

ext_int_edge(0,L_TO_H); // 1. harici kesme, yükselen kenar
ext_int_edge(2,H_TO_L); // 3. harici kesme, düşen kenar

interrupt_active()

Bu fonksiyon ise herhangi parametre olarak verilen kesmenin aktif olup olmadığını kontrol etmek için kullanılır.

if(interrupt_active(INT_TIMER0)) // timer0 kesmesi aktif ise

Örnek Uygulama

PIC16f628A’nın B0,B1 bacaklarına bağlı iki adet LED ve A0 bacağına bağlı bir buton olsun. Uygulamamız şu işi yapsın; butona basılıp bırakıldığında B0′daki led durum değiştirsin, B1 deki led ise her 500 ms sürede bir durum değiştirsin.

Normalde bunu şu şekilde halledebiliriz.

#include <16F628A.h>
#device *=16
#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT, MCLR
#use delay(clock=4000000)

void main()
{
while(true)
{
if(!intput(PIN_A0))  // buton basıldıysa
{
while(!input(PIN_A0)); // bırakılana kadar bekle
output_toggle(PIN_B0);  // B0 ledinin durumunu değiştir
}
delay_ms(500);             //500 ms gecikme
output_toggle(PIN_B1);  // B1 ledinin durumunu değiştir
}
}

Butona basılmadığı zaman B1 deki led 500ms de bir yanıp sönecektir, butona basıldığında ise işler bir hayli karışacaktır. Birinci sıkıntı butona uzun basılmasıdır, örneğin 5 saniye boyunca buton bırakılmazsa B1 deki led son durumunda kalacaktır 5 sn boyunca. İkinci sıkıntı ise 500ms’lik gecikme esnasında butona basılıp bırakılırsa yazılım bunu anlamayacaktır.
Bu durumu düzeltmek için yapılacak tek şey 500ms gecikme süresini timerlardan biri ile oluşturmak ve B1 deki ledin durmunu kesme içerisinde değiştirmek.

Bu iş için timer0 ve kesmesini kullanacağız, Saat frekansımız 4Mhz olduğu için timer0′ı 1:4 prescaler ile çalıştırırsak timer taşma süresi 1ms civarında olur. Yani timer0 0 dan 255 değerine gelinceye kadar 1ms süre geçer. Bu değeri PIC wizard kullanarak bulabilirsiniz.

#include <16F628A.h>
#device *=16
#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT, MCLR
#use delay(clock=4000000)

long sure_sayac;

#int_timer0            // timer0 kesme rutini
timer0_kesmesi()
{
if(++sure_sayac == 500)  // timer0 500 kez taştıysa
{                                 // 500 x1 ms = 500 ms süre geçmiştir
sure_sayac = 0;         // sayacı sıfırla
output_toggle(PIN_B1); // B1'deki ledin durumunu değiştir
}
}

void main()
{
sure_sayac = 0;
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_4) // dahili clock 1:4 prescalar
enable_interrupts(INT_TIMER0);  //Timer0 kesmesi aktif
enable_interrupts(GLOBAL);   // global kesme aktif
while(true)
{
if(!intput(PIN_A0))  // buton basıldıysa
{
while(!input(PIN_A0)); // bırakılana kadar bekle
output_toggle(PIN_B0);  // B0 ledinin durumunu değiştir
}
}
}

Bu örnek kesmelerin neden kullanılması gerektiği konusunda da biraz fikir sahibi olmanızı sağlamıştır :)

Örnek uygulamamızı da yaptığımıza göre yazımını bitirmemiz gerekiyordu teorik olarak:) ama bitirmeyeceğiz. Interruptlar’la alakalı biraz daha teknik bilgi aktarıp CCS-C nin hazır fonksiyonlarını kullanmadan yukarıdaki örneğimizi tekrar yapacağız ve yazımızı öyle noktalayacağız.

Kesmelerle alakalı register’lar

PIC’ler de kesmeler ile ilgili register’ların sayısı modeline göre değişiklik göstermektedir. Örneğin PIC16F628A da bir bir adet INTCON varken PIC18F442 INTCON,INTCON2 ve INTCON3 olmak üzere üç adet registera sahiptir. Biz 16f628A için INTCON registerini inceleyeceğiz, kesmelerle alakalı diğer registerlar için ayrıntı isteyenler denetleyicinin teknik dökümanına bakabilirler

INTCON

Görüldüğü gibi adresi 4 bank için 0x0b,0x8b,0x10b,0x18b’dir ve 8 Bit uzunluğundadır. Şimdi bu bitleri sırayla açıklayalım.

  • GIE: Global Interrupt Enable bit, bu bit “1″ ise tüm interreptlar açık “0″ ise tüm interruptlar kapalı olur daha önce anlatmıştık zaten
  • PEIE: Peripheral Interrupt Enable Bit, PIC’teki TIMER modülleri gibi çevrebirimlerin kesmelerini enable/disable etmek için kullanılır. Örneğin Timer0 kesmesini kullanacaksanız hem GIE hemde PEIE set edilmesi gerekiyor. enable_interrupts(INT_TIMER0) denildiğinde derleyici gerekli tüm bitleri set ediyor.
  • T0IE: Timer0 Interrupt Enable Bit, Bu bit “1″ ise Timer0 kesmesi aktif “0″ ise pasif olur.
  • INTE: PIN B0 External Interrupt Enable Bit, “1″ harici kesme aktif “0″ pasif
  • RBIE: RB Port Change Interrupt, “1″ aktif “0″ pasif
  • T0IF: Timer0 Interrupt Flag, Timer0 kesmesi oluştuğunda “1″ olur
  • INTF: External Interrupt Flag, Harici kesme oluştuğunda “1″ olur
  • RBIF: Port B Change Interrupt Flag,  Kesme oluştuğunda “1″ olur

PIC16F628A modeli için kesmelerle ilgili diğer registerlar olan PIE1 ve PIR1 Register’ları da INTCON gibidir, onlarda da diğer kesmlere ait enable bitleri ve flag bitleri bulunur.

Yukarıda yaptığımız timer0 kesmesi örneğinde iki ade hazır fonksiyon kullanmıştık;

enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);

Bu hazır fonksiyonları kullanmadan aynı işi yapmak istersek, yapmamız gereken şey INTCON registerini tanımlamak ve ilgili bitleri set etmek olacaktır.

#byte INTCON = 0x8B    // INTCON Tanımlanıyor
INTCON = 0xD0;  //Timer0 kesmesi aktif ediliyor (dolayısıyla gie ve peie de set ediliyor)

Kesmeler konusunu dilim döndüğünce anlatmaya çalıştım, ufak tefek hatalarımız olduysa mazur görün. Sağdan soldan copy / paste etmeden yazı yazmak inanın çok zahmetli ve hayli vakit alan bir iş. Timerlarla ilgili yazı serimizin ikincisinde görüşmek üzere…

CCS-C ile PIC Programlama, PIC Interrupts (Kesmeler) yazısı için 36 yorum yapıldı

  1. [...] yapılması gereken bağlantılar 14- PORTLAR, Led, Röle ve Buton kullanımı 15- Interrups (Kesmeler) 16- Timerlar – 1 Yazıyı [...]

  2. fatih says:

    usta,,, CCS de en hızlı kesme en fazla ne kadar olur? 40 Mhz osilatör kullanıyorum. 18f serisi… 10 mikrosaniyelik timer2 kesmesi çalışıyor ama 8 mikrosaniye çalışmıyor. benim çok daha hızlı kesmelere ihtiyacım var. komut saykılım 100ns ve ben 1 mikrosaniyade bir kesme istiyorum ama 8us bile çalışmıyor. çok şey mi istiyorum? kesmeleri assembly ile yapsak hızlanır mı. ccs de kesmeye girmezden evvel yapılan hazırlıklar (register yedekleme işlemleri) çok zaman götürür mü. teşekkürler…

  3. Emrah AYDIN says:

    @fatih
    Attığın maile cevap yazdım..

  4. fatih says:

    tamam hocam sağol. tavisyelerini yapayım, döneceğim sana. bu arada yukarıdaki yazıyı “nasıl olsa ben biliyorum diye” okumamıştım. okudum. gerçekten faydalı oldu ve büyük zahmet vermişsin. sağol.

  5. Hüseyin DEMİRBİLEK says:

    Hocam merhaba yine ben iyiki varsınız sıkışınca size koşuyorum şimdiki sorum PIC12C508A için tmr0 kesmesi yapabilirmiyim
    Teşekkurler

  6. Emrah AYDIN says:

    @Hüseyin
    Yapamazsın, 12c508a denetleyicisi herhangi bir kesme kaynağına sahip değildir. Önceki yazılarımızı okumamissin demekki. hatta bu yazıyı da :) bu yazıda da belirttiğim gibi derleyicide view menüsünden valid interrupts kısmını seçersen bir pencere açılır orada istediğin PIC’i seçip hangi kesmeleri oluşturabildiğini görebilirsin. 12C508A için hiçbir kesme listelenmeyecektir.

  7. rustu says:

    Harici kesme kullandığımızda kesme sonunda programın kaldığı yerden çalışmaya devam ettiğinden bahsetmişsiniz. Acaba kesme sonunda PIC resetlenmiş gibi ya da ana programın baştan başlatılabilmesi mümkün müdür? Kesme sonunda programın (Ana program) kaldığı yerden değil de en baştan başlatılmasının bir yolu var mıdır?

  8. Emrah AYDIN says:

    reset_cpu();
    Bu komut PIC’i resetleyerek programı en baştan başlatır. Kod içerisinde istediğin yere koyabilirsin, ister kesme ister ana döngü veya herhangi bir fonksiyon.

  9. alimerdan says:

    hocam sizi gökte ararken yerde buldum bir konu hakkinda yardiminiza ihtiyacim var acaba size msn adresimi versem zamaniniz oldugunda girebilirmisiniz yardim alamak istedigim konu hakindaki resimleri öncelikle size göstermek isterim simdiden tessekkürler hocam

  10. alimerdan says:

    alimerdan@live.de benim adresim

  11. Emrah AYDIN says:

    @alimerdan
    Msn için vakit ayıramam, soracaklarını mail yoluyla sorabilirsin. aydin_emrah@yahoo.com veya admin@teknobakis.com

  12. ayhan says:

    yazılarınızı tesadüfen gördüm ve tamamını okudum elinize saglık simdiye kadar okudugum en güzel TÜRKÇE kaynak
    yazılarınızın devamını merakla bekliyorum

  13. Emrah AYDIN says:

    devamını getirmeyi çok istiyorum ama şu sıralar pek ilgilenemiyorum.

  14. Kazım says:

    Hocam bilgileriniz için çok teşekkür ediyorum. CCS’ye yeni başladım eskiden direkt C’de yazıyordum register ayarlarını kendim seçerek. Geliştirmem istenen program CCS’de yazılmış. Bu sebeple bende yeni başladım olaya. Hocam size sormak istediğim bir soru var. output_float komutunu tam olarak anlamadım. Bu komutu pin’e uyguladığımızda pin çıkışını 1 mi yapıyor. Yani hem çıkışı bir yapıyor hemde sıfıra düşmesi durumunda input mu algılıyor. Yani bu komutu veri iletiminde output_high komutu yerine kullanabilir miyim. Bu komut hakkında ayrıntı verirseniz çok memnun olacağım. Ayrıca emeğiniz için size büyük teşekkürlerimi sunuyorum.

  15. Kazım says:

    Hocam bir önceki mesajımda mail adresimi yanlış yazmışım doğru adres bu mesajdaki adresimdir.

  16. fatihb says:

    Merhabalar;
    Ben projemde 18F452 kullanıyorum.bildiğiniz üzere bu pic 3 tane harici kesmeye sahip.ben bunlardan bir tanesini kullanıp diğerlerini giriş olarak kullanmak istiyorum.Bir kaç deneme yaptım fakat kesmeye giriyor ama giriş olarak istediğim özellikleri sağlamıyor.bu giriş olarak belirlediğim uçların kesme özelliğini kapatmammı gerekiyor?Teşekkürler…

  17. ali veli says:

    Yazılarının devamını bekliyoruz..

  18. mehmet says:

    hocam merhaba ben 16f628a da ext ve rda kesmlerini birlikte kullanmak istiyorum programda rda kesmesi çalışıyor ama ext kesmesi çalışmıyor. Bu konuda yardımcı olursanız sevinirim.

  19. Emrah AYDIN says:

    @mehmet
    Yardımcı olabilmem için gerekli bilgileri vermen gerekiyor, kodunun tamamını veya ilgili kısmını yollarsan incelerim.

  20. mehmet says:

    hocam sorunu çözdüm teşekürler

  21. ali says:

    @Emrah Aydın öncelikle ellerinize sağlık bizler için güzel bir kaynak oluşturmuşsunuz. Verdiğiniz timer0 örneğinde anlayamadığım bir şey var. timer0 kesmesi her 500ms ‘de bir ledin durumunu değiştirecek. Eğer biz timer0 diyelim sure_sayac=300 ms’de iken butona basarsak led durum değiştirecek ve geçtiği durumda 200ms ‘ye kalmış olacak. Bu durumda timer0 kullanılmadan yazılan kodun işlevi de değişmiş olacak. Yanlış mı düşünüyorum acaba?

  22. Emrah AYDIN says:

    @ali
    Verdiğim örnekte iki adet led var ve bu led’lerden biri timer ile diğeri ise buton ile kontrol ediliyor. Sen tek led olduğunu düşündün galiba

  23. ali says:

    Evet ben tek led olarak düşünmüşüm. Kusura bakmayın.

  24. Rüstem Kınacı says:

    Hocam emeğine sağlık. Benim gibi CCS C’yi yeni öğrenenler için bulunmaz bir kaynak. Yazılarınızı ilgiyle takip ediyorum. Umarım devamı gelir her konuda.

  25. gdfjhdj says:

    wetewtwet

  26. fahrettin yıldız says:

    #include
    #device *=16
    #fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT, MCLR
    #use delay(clock=4000000)

    long sure_sayac;

    #int_timer0 // timer0 kesme rutini
    timer0_kesmesi()
    {
    if(++sure_sayac == 500) // timer0 500 kez taştıysa
    { // 500 x1 ms = 500 ms süre geçmiştir
    sure_sayac = 0; // sayacı sıfırla
    output_toggle(PIN_B1); // B1′deki ledin durumunu değiştir
    }
    }

    void main()
    {
    sure_sayac = 0;
    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_4) // dahili clock 1:4 prescalar
    enable_interrupts(INT_TIMER0); //Timer0 kesmesi aktif
    enable_interrupts(GLOBAL); // global kesme aktif
    while(true)
    {
    if(!intput(PIN_A0)) // buton basıldıysa
    {
    while(!input(PIN_A0)); // bırakılana kadar bekle
    output_toggle(PIN_B0); // B0 ledinin durumunu değiştir
    }
    }
    }
    // hocam buörnek çalışmayı bitürlü deneyemedim hata veriyor birde siz deneyın saygılarımla
    hata: 22. satırda

  27. [...] Yukarıdaki fonksiyonları kesme kullanımıyla ilgili yayımladığım yazıda açıklamıştım. Yazı için buraya tıklayın [...]

  28. cihan says:

    c de şu kod kesme ile nasıl bitirilir

    int i;
    while(1)
    {
    i++;
    }
    END:printf(“%d”,i);

  29. newbie says:

    Merhaba hocam,
    Ben 16f877 kullanıyorum.Bir switch baglayıp o switche basıldıgında bazı işlemler yapmak istiyorum.Yalnız sanırım baglantıları yanlış yaptım pic yandı.[o denemeden beri çalışmıyor:)]Switch üzerinde 3 baglantı kablosu var, bunları nasıl baglamalıyım?Pice giden ucu hangi pine takmalı ve hangi kesmeyi kullanmalıyım örnek kısa bi kod gösterebilir misiniz?teşekkürler şimdiden

  30. sinan says:

    Hocam yazılarınızı okudum, çok güzel emekler vermişsiniz. Teşekkürler.

  31. Gürkan says:

    Merhabalar hocam,

    Öncelikle böylesine faydalı bir kaynağı biz yeni başlayanlara sağladığınız için çok teşekkür ederim.

    size bir sorum olacakti. Daha önceki projelerimde at89c52 ile calişiyordum. Bugün Pic16f877 öğrenmeye başladım. Yazı serinizi bulana kadar kara kara düşünüyordum cidden. Bana çok faydası dokundu.

    Şimdi ilk olarak şunu denedim. Quadrature bir sinyali analiz etmeye çalışıyorum. B0 pininden ana sinyal geliyor. Bu sinyalin her yükselen kenarında kesme yaratıp, eğer 2. sinyal ilk sinyalin 90derece önündeyse A4ü, gerisindeyse A5i toggle etmesini istedim. Proteusda istedigimi aldım ama her saniye yüzlerce warning veriyor. verdiği warning ise ” [PIC16 CORE]PC=0×0017. Stack overflow pushing return address of interrupt.” Bunun nedeni sizce nedir?

    Kodum ise şu şekilde;

    #include
    #device *=16
    #fuses XT, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
    #use delay(clock=4000000)
    #define aa PIN_A0
    #int_ext

    signed int aci=0;
    ext_isr()
    {
    output_toggle(PIN_A3);
    if(input(aa)==0)
    {
    output_toggle(PIN_A4);
    aci=aci+0.36;
    }
    else
    {
    output_toggle(PIN_A5);
    aci=aci-0.36;
    }
    }
    void main ()
    {
    set_tris_A(0×01); //

    enable_interrupts(INT_EXT);
    enable_interrupts(GLOBAL);
    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);
    setup_psp(PSP_DISABLED);
    setup_spi(FALSE);
    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DISABLED,0,1);
    while(TRUE)
    {

    }
    }

    P.S: Hocam bunu yazdıktan sonra aklıma geldi. signed int aci=0; Bu komutu kesme rutininin üstünde yazdığımdan dolayı veriyormuş o uyarıları. Ama maininin içine koyunca da compile ederken aci değişkeni bulunamadı diye hata veriyor. Basit bişeydir heralde ama yeni başladığımdan daha her şey havada olduğu için gözümden kaçıyor heralde. Bir de sabahin 5inde bunları yazdığımdan anlatımda bozukluk olmuş olabilir affola :)

  32. Gürkan says:

    Hocam bir soru daha sorayım bu arada, Mesela bir değişkenimdeki değeri seri port ile bilgisayara nasil gönderebilirim?

    Yukardaki kodumdaki overflow hatasını hallettim. En son şekilde kodum ama bu seferde “aci” değişkenimdeki değeri gönderemedim.

    #include
    #fuses XT, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
    #use delay(clock=4000000)
    #define aa PIN_A0
    #int_ext
    #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

    ext_isr()
    {
    signed int aci;
    //output_toggle(PIN_A2);
    if(input(aa)==1)
    {
    output_toggle(PIN_A3);
    aci=aci+0.36;
    printf(“/b”);
    printf(“%c”,aci);
    }
    else
    {
    output_toggle(PIN_A5);
    aci=aci-0.36;
    printf(“/b”);
    printf(“%c”,aci);
    }
    }
    void main (void)
    {
    signed int aci=0;
    set_tris_A(0×01);
    set_tris_B(0×01);
    enable_interrupts(INT_EXT);
    enable_interrupts(GLOBAL);
    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);
    setup_psp(PSP_DISABLED);
    setup_spi(FALSE);
    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DISABLED,0,1);

    printf(“/b”);
    printf(“%c”,aci);
    while(TRUE)
    {

    }
    }

  33. samet says:

    RB kesmesiyle ilgili örnek yayımlarsaınız seviniriz

  34. mehmet says:

    slm
    iyi günler programlayıcı arkadaşlar
    bende bu ccs c programını öğrenmek istiyrum aynızamanda tezim var ccs ile pıc16f84a yha yazılacak bi kodlarım var fakat ben yeni olduğum için pek bişi anlamadım derlediğimde 36 hata veriyo hataları anlamadım bana yardımcı olabilirmisiniz bu koddda negibi hatalar çıkar veya bana derleyip atabilirmisiniz lütfen yardımlarınızı bekliyorum …

    #include
    #include

    #define StD RB4
    #define HAT_ROLE RA1
    #define LED RA2
    #define PALS RA0

    // 4MHz osilator
    __CONFIG(XT&WDTDIS&PWRTEN);

    unsigned char kontrol=0;
    unsigned int CX;

    //———————————————————-
    // Timer alt programi
    // 15 saniye suresince tusa basilmadiysa hatti kapatmak icin
    //———————————————————-
    void interrupt kesme(void){

    CX++;
    if(CX>15000)kontrol=1;

    T0IF=0; // bayragi temizle
    TMR0=131;
    }
    //———————————————————-

    //———————————————————-
    // ANA PROGRAM
    //———————————————————-
    main(void){

    unsigned char i,komut,sifre[5],sayac;

    //———————————————————-
    // Port konfigurasyonu
    //———————————————————-
    TRISB=0x1F; // PORTB’nin RB5,6,7 pinleri cikis,digerleri giris
    TRISA=0×01; // RA0 giris digerleri cikis
    //———————————————————-

    PORTB=0; // cihazlar enerjisiz
    PORTA=0; // hat kapali, LED sonuk

    //————————————————————-
    //Timer islemleri (kesme suresi=1ms)
    //————————————————————-
    T0CS=0; // Dahili clock
    PSA=0; // Prescaler TMR0 icin ayarli
    PS0=0; PS1=1; PS2=0; // Oran 1:8
    ei(); // butun kesmeler etkin
    T0IE=0; //Baslangicta timer pasif
    T0IF=0; // bayrak temizle
    //————————————————————-

    // resetten sonra 1 saniye bekle
    for(i=0;i50){ // Zil sayisi 2
    HAT_ROLE=1; // Hatti ac

    sifreoku:

    for(i=0;i15s ise hatti kapat
    HAT_ROLE=0; goto basla; // basa don
    }
    else {};
    }

    T0IE=0; CX=0; // Tusa basildi ise Timer’i durdur, CX’i sifirla
    // Boylece yeni bir 15s’lik ek sure kazanilmis olur
    sifre[i]=PORTB&0x0F; // sifreyi oku

    while(StD==1); // tus basili ise bekle
    }
    //————————————–
    // sifrenin son hanesi * olmalidir

    //——–Sifre konrol——————
    if(sifre[0]==1 && sifre[1]==2 && sifre[2]==3 && sifre[3]==4 && sifre[4]==11){
    // 11 degeri tus takimindaki * tusuna karsilik gelir.

    LED=1; // sifre dogru ise LED’i yak

    for(;;){
    while(StD==0); // cihazlar icin komut bekle

    komut=PORTB&0x0F;

    if(komut==1)RB5=1; // cihaz 1 ON
    if(komut==2)RB6=1;
    if(komut==3)RB7=1;
    if(komut==4)RB5=0; // cihaz 1 OFF
    if(komut==5)RB6=0;
    if(komut==6)RB7=0;

    if(komut==12){ // # tusuna basildi ise hatti kapat
    HAT_ROLE=0; LED=0; goto basla;
    // Cihazlarin ON/OFF durumlari degismeden kalir.
    }
    }
    //————————————–
    }

    else{
    goto sifreoku; //sifre yanlis ise tekrar sifre okumaya git
    }
    }

    else{ // sayac<50 ise
    while(PALS==0);
    }

    } // for dongusu

    }

  35. erhan says:

    usart olmayan pinler ile nasıl veri iletimi yapılıyor acaba?

    bu kbhit kodu nedense düzgün çalışmıyor?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>