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ı

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
6
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Timer0 için interrupt rutini
#int_timer0
timer_kesme_rutini()
{
... //buraya kodlarımızı
...<div class="ad">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-5963711057135309";
/* 336x280, oluşturulma 19.06.2008 */
google_ad_slot = "7915129787";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script><ins style="margin: 0px; padding: 0px; border: currentColor; width: 336px; height: 280px; display: inline-table; visibility: visible; position: relative;"><ins style="margin: 0px; padding: 0px; border: currentColor; width: 336px; height: 280px; display: block; visibility: visible; position: relative;" id="aswift_1_anchor"></ins></ins> 
</div> //yazacağız
}
1
2
3
4
5
6
7
//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

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
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

1
2
3
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.

1
2
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

1
2
3
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.

1
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.

1
2
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.

1
2
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.

1
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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.

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
#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;

1
2
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.

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

 

CCS-C ile PIC Programlama, PIC Interrupts (Kesmeler)” için bir yorum

  • Ağustos 10, 2016 tarihinde, saat 11:09 am
    Permalink

    mehmet bey seni öpebilir miyim? şaka bi yana o kadar mükemmel anlatmışsın ki ne kitaplarda ne de programın kendi manuelinde bile böyle bir anlatım yok şu an tramvay akülerinde ki aşırı ısınmayı ölçen lcd ekranda yazan ve aşırı ısınma esnasında devreyi kesecek bir devre üzerinde çalışıyorum doğal olarak if koşuluyla sicaklik kontrolü yapıp koşul sağlandığında interrupt ile ölçümü kesip devreyi kesmem gerekiyor cidden bu paylaşımın çok faydalı bi de son yazdığın kodun 24. satırında harf hatası olmuş belki copy paste yapan olur diye belirtiyorum saygılar

    Yanıtla
  • Ağustos 10, 2016 tarihinde, saat 11:09 am
    Permalink

    mehmet bey seni öpebilir miyim? şaka bi yana o kadar mükemmel anlatmışsın ki ne kitaplarda ne de programın kendi manuelinde bile böyle bir anlatım yok şu an tramvay akülerinde ki aşırı ısınmayı ölçen lcd ekranda yazan ve aşırı ısınma esnasında devreyi kesecek bir devre üzerinde çalışıyorum doğal olarak if koşuluyla sicaklik kontrolü yapıp koşul sağlandığında interrupt ile ölçümü kesip devreyi kesmem gerekiyor cidden bu paylaşımın çok faydalı bi de son yazdığın kodun 24. satırında harf hatası olmuş belki copy paste yapan olur diye belirtiyorum saygılar

    Yanıtla

eren için bir cevap yazın Cevabı iptal et

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir