DERS-6
KESME (INTERRUPT) KULLANIMI:
Tüm bilgisayarlar ve mikroişlemci sistemlerinde kullanılan bir özelliktir. Basitçe açıklamak gerekirse, bir mikroişlemciye kesme anında neler yapması gerektiği bir program bölümü şeklinde verilir. Açıkçası mikro işlemci kesme oluştuğu anda yaptığı işi tamamen bırakarak kesme bölümüne gider ve orada gösterilen işleri yapar. Kesme bölümünün sonunda tekrar geriye dön komutunu alır ve önceden yaptığı işe kaldığı yerden devam etmek üzere döner.
Bu olayı günlük hayatımızda yaşadığımız bir örnekle açıklayalım isterseniz.
Diyelim ki bir evde yaşıyorsunuz ve bir gün tüm aileyi topladınız. Amacınız aileye bir yangın olması durumunda ne yapılması gerektiğini öğreteceksiniz. Burada yangının meydana gelmesi bir kesme olayı olacaktır. Normal yaşama düzeninde hiç yangın olmaz ise kesme oluşmayacak demektir. Ama oluşma ihtimali her zaman olacaktır. Şimdi bir akşam yemeği esnasında yangın çıktığını varsayalım. Kesme oluşacak ve herkes önceden öğretildiği gibi görevinin başına koşacak. Yangın söndürülecek ve her kes yemeğe geri dönecektir. İşte tipik bir kesme olayına örnek.
Kesme olayını kısaca açıkladıktan sonra Pic işlemcilerinde hangi olayların kesme yaratabileceğine bir göz atalım.
RB0/INT KESMESİ:
En çok kullanılanlardan birisi PortB.0 pininde meydana gelen lojik seviye değişikliğinin oluşturacağı kesme dir. Option Yazmaçının 6. biti önceden ayarlanarak kesmenin sıfırdan – bir konumuna geçişte mi yoksa bir konumundan – sıfır konumuna geçişte mi oluşturulacağı belirlenir.
OPTION_REG.6=0 olur ise RB0 da düşen kenarda kesme oluşur.
OPTION_REG.6=1 olur ise RB0 da yükselen kenarda kesme oluşur.
Düşen veya yükselen kenar terimi bir clock palsının kenar şekli olarak anlaşılmalıdır.
Kesme işleminin aktif edilebilmesi için INTCON (interrupt control registeri) yazmaçı kullanılır.
Örneğin RB0 kesmesinin aktif hale getirilebilmesi için;
INTCON.4=1 yapılmalı ve daha sonra ;
INTCON.7=1 değeri 7 nolu bite verilerek tüm kesmeler açılmalıdır.
Bu iki değer tek bir komutlada verilebilir. Şöyle,
INTCON=%10010000
Bu kesme aktif hale getirildikten sonra şayet bir kesme oluşur ise INTCON yazmaçı nın 1 nolu biti 0 konumundan 1 konumuna geçer. Bu bit şayet tekrar 0 konumuna program içinde getirilmez ise yeniden bir kesme oluşmaz. Bu nedenle programın KESME bölümünde bu bit sıfırlanmalıdır.
RB0/INT Kesme kullanımını toparlamak gerekir ise;
RB0/INT kesmesini kullanmak için yapılması gerekenler sırası ile;
- Programın baş kısmına ON INTERRUPT GOTO KESME komutu verilerek kesme oluştuğunda programın gideceği yer (KESME) belirlenir.
- RB0 pini giriş olarak ayarlanacak
- Gerekir ise Option Yazmaçının 7. biti 1 veya 0 yapılarak pullup dirençleri isteğe göre ayarlanacak.
- Option Yazmaçının 6 biti 1 veya 0 olarak ayarlanarak kesmenin düşen kenar veya yükselen kenardamı olacağına karar verilecek.
- Intcon yazmaçına INTCON=%10010000 değeri verilerek kesme aktif hale getirilecek.
- Kesme bölümüne başlarken DISABLE komutu verilerek kesme anında yeniden kesme oluşumuna imkan verilmeyecek.
- Kesme bölümünde INTCON.1=0 komutu verilerek kesmeden dolayı 1 olan bayrak tekrar sıfırlanır ve sonradan yeni kesme oluşmasına imkan tanınır.
- Kesme bölümünün sonunda RESUME komutu verilerek programın kesme oluşmasından önceki yerine dönmesi sağlanır.
- En sona ENABLE komutu yazılarak kesmeden dönüldükten sonra tüm kesmeler aktif hale getirilir.
Şimdi de öğrendiklerimizi bir örnek vererek açıklayalım.
Örnek Programımız RB0 bacağına bağlı bir tuş yardımı ile RA.0 bacağına bağlı olan bir led’i yakıp söndürsün. Program içinde tuşa basıldı mı? şeklinde bir komut vermeden direkt kesme özelliğinden yararlanacağız. Yani RB0 bacağını sürekli 1 konumunda tutacağız (pullu up ile). Dolayısıyla bu bacağı tuşa basarak sıfıra çekersek kesme oluşacak ve kesme bölümünde de led’i toggle komutu ile yanıksa söndüreceğiz, sönük ise yakacağız.
Tabiiki seçim olarak RB0/INT bacağı düşen kenar da kesme oluşacak şekilde ayarlanacaktır.
Önce şemamızı verelim;
İşte Programımız;
'****************************************************************
PORTA=0
TrisA=%00000000
TrisB=%00000000
'-----------------------------------------------------------------
@ DEVICE pic16F628 'işlemci 16F628
@ DEVICE pic16F628, WDT_on 'Watch Dog timer açık
@ DEVICE pic16F628, PWRT_ON 'Power on timer açık
@ DEVICE pic16F628, PROTECT_OFF 'Kod Koruma kapalı
@ DEVICE pic16F628, MCLR_OFF 'MCLR pini kullanılmıyor.
@ DEVICE pic16F628, INTRC_OSC_NOCLKOUT 'Dahili osilatör kullanılacak
'-----------------------------------------------------------------
ON INTERRUPT GoTo KESME 'kesme oluşursa KESME adlı etikete git.
OPTION_REG=%0000000 'dahili Pull up dirençleri aktif edildi ayrıca pullup direncine gerek yok.
INTCON=%10010000 'Tüm Kesmeler aktif ve RB0/INT kesmesi aktif
TRISB=%00000001 'PortB.0 giriş diğerleri çıkış yapıldı.
TRISA=%00000000 'A portu tamamı çıkış yapıldı.
CMCON=7 '16F628 de komparatör pinleri iptal hepsi giriş çıkış
'-----------------------------------------------------------------
SYMBOL TUS=PORTB.0
SYMBOL LED=PORTA.0
'-------------------------------------------------------------------
BASLA: 'Ana program bölümünde program bir şey yapmayacak
pauseus 100 ‘kesme oluşmadığı müddetçe program
goto basla ‘ bu satırlar arasında dolaşır
DISABLE 'yeniden kesme oluşması önleniyor
KESME: 'program buraya geldiğinde kesme oluşmuş demektir.
TOGGLE LED 'LED konum değiştirdi
PAUSE 1
INTCON.1=0 'RB0/INT Bayrağı (flag) silindi.
Resume 'geldiğin yere dön
Enable 'kesmeler yeniden aktif.
End
‘-------------------------------------------------------------------------------------------------
B portunda dahili pullup dirençleri vardır ve OPTION Yazmaçının 7. biti bunların aktif olup olmamasını kontrol eder. Şayet bu bit 0 ise dirençler aktif, 1 ise dirençler devrede değildir.
Sanırım örnek her şeyi açıkça anlatıyor. Burada özellikle dikkat edilecek yer ana program bölümünde pauseus 100 komutundan başka bir komut olmadığıdır. Normal olarak program kesme oluşuncaya kadar sürekli Başla etiketi ile Goto başla komutu arasında dönüp duracaktır.
Buraya kadar açıklananlar yalnızca RB0/INT kesmesinin kullanımına örnektir.
PORTB (RB4-RB7) DEĞİŞİKLİK KESMESİ :
Bu kesme tipinde RB4-RB7 bacaklarının mevcut konumlarında oluşacak bir değişiklik sonucunda da kesme oluşturulmaktadır. Bu kesme PORTB nin RB4-RB7 arası bacaklarının tamamının giriş yapılması halinde geçerlidir. Bacaklardan birisi çıkış yapılır ise kesme iptal olur.
Kesme bir kere aktif hale getirilir ise bu 4 adet bacak değeri sürekli pic tarafından okunur. Okunan değer bir önceki ile karşılaştırılır. Şayet fark var ise kesme oluşur.
Kesme de oluşan RBIF bayrağının silinmesi için PortB nin bir kere programcı tarafından mutlaka okutulması gerekir. Aksi taktirde bayrak silinemez ve sürekli kesme oluşur. Tabiiki yalnızca PortB nin okunması bayrağı silmeye yetmeyecektir. Ayrıca RBIF bayrağının kesme bölümünde silinmesi de gerekir.
RB PORT (Rb4-Rb7) Değişiklik Kesmesinin Oluşması İçin Gerekenler :
Programın baş kısmına ON INTERRUPT GOTO KESME komutu verilerek kesme oluştuğunda programın gideceği yer (KESME) belirlenir.
- RB4-RB7 pinleri mutlaka giriş olarak ayarlanacak
- Intcon yazmaçının 3 biti bu kesme için ayrılmıştır. Bu bit 1 yapılarak kesme aktif edilir. INTCON=%10001000 değeri verilerek kesme aktif hale getirilecek.
- Kesme bölümüne başlarken DISABLE komutu verilerek kesme anında yeniden kesme oluşumuna imkan verilmeyecek.
- Kesme Bölümünde DURUM=PORTB şeklinde port değeri okunacak
- Kesme bölümünde INTCON.0=0 komutu verilerek kesmeden dolayı 1 olan bayrak tekrar sıfırlanır ve sonradan yeni kesme oluşmasına imkan tanınır.
- Kesme bölümünün sonunda RESUME komutu verilerek programın kesme oluşmasından önceki yerine dönmesi sağlanır.
En sona ENABLE komutu yazılarak kesmeden dönüldükten sonra tüm kesmeler aktif hale getirilir
Bu kesmeyi bir örnekle açıklayalım.
Bu defa yine PortA.0 pinine bir LED bağlı olsun. PortB.7 pinine bir Tuş bağlayalım ve bu sefer tuş, pulldown yani GND ye çekili olsun. Tuşa basılınca PortB.7 pinine High uygulayalım. İşte bağlantı şemamız;
Program RB (4-7) Değişiklik Kesmesi:
'****************************************************************
PORTA=0:portb=0
TRISB=%11110000 'PortB tamamı giriş yapıldı.
TRISA=%00000000 'A portu tamamı çıkış yapıldı.
'-----------------------------------------------------------------
@ DEVICE pic16F628 'işlemci 16F628
@ DEVICE pic16F628, WDT_on 'Watch Dog timer açık
@ DEVICE pic16F628, PWRT_ON 'Power on timer açık
@ DEVICE pic16F628, PROTECT_OFF 'Kod Koruma kapalı
@ DEVICE pic16F628, MCLR_off 'MCLR pini kullanılmıyor.
@ DEVICE pic16F628, INTRC_OSC_NOCLKOUT 'Dahili osilatör kullanılacak
'-----------------------------------------------------------------
ON INTERRUPT GoTo KESME 'kesme oluşursa KESME adlı etikete git.
OPTION_REG=%10000000 'dahili Pull up dirençleri iptal edildi
INTCON=%10001000 'Kesmeler aktif ve RB CHANGE kesmesi aktif
CMCON=7 '16F628 de komparatör pinleri iptal hepsi giriş çıkış
'-----------------------------------------------------------------
DURUM VAR BYTE
SYMBOL TUS=PORTB.0
SYMBOL LED=PORTA.0
SYMBOL RBIF=INTCON.0
'-------------------------------------------------------------------
BASLA: 'Ana program bölümü
DURUM=PORTB
PAUSEUS 100
GOTO BASLA
DISABLE 'yeniden kesme oluşması önleniyor
KESME: ' burada kesme oluştu demektir.
TOGGLE LED 'LED konum değiştirdi
PAUSE 300
DURUM=PORTB ‘PortB değeri okundu
RBIF=0 'INTCON.0=0 yapıldı yani RB CHANGE Bayrağı (flag) silindi.
RESUME 'geldiğin yere dön.
ENABLE 'kesmeler yeniden aktif.
End
TMR0 KESMESİ :
Ram belleğin 01 adresinde bulunan özel bir yazmaçdır. Genellikle adı üstünde zamanlayıcı olarak kullanılır. 8 bitlik bir sayıcıdır. Yazılabilir okunabilir. Programlanabilen bir özel bölücüsü (prescaler) vardır. Harici veya dahili clock palsları ile sayım yapabilir. Sayma yönü daima artan yöndedir.
Bu sayıcı veya zamanlayıcı 255 değerini aştığı zaman değeri tekrar sıfır olur ve bu anda bir kesme oluşturulabilir. Bu sayıcının diğer önemli bir özelliği ise arka planda yani ana programdan bağımsız çalışmasıdır. Ana program çalışırken veya kesme oluştuğu andan itibaren saymasına devam eder.
OPTION Yazmacının ilk üç biti frekans bölme (prescaler) ayarlaması için kullanılır. Bu üç bitin aldığı değerlere göre bölücünün aldığı değerler aşağıda gösterilmiştir. Bu bölücü aynı zamanda WDT içinde kullanıldığından tabloda WDT için bölücü değerleride gösterilmiştir.
Bitler
|
TMR0
|
WDT
|
000
|
1/2
|
1/1
|
001
|
1/4
|
1/2
|
010
|
1/8
|
1/4
|
011
|
1/16
|
1/8
|
100
|
1/32
|
1/16
|
101
|
1/64
|
1/32
|
110
|
1/128
|
1/64
|
111
|
1/256
|
1/128
|
Tablodan da anlaşılacağı üzere sinyal kaynağından gelen palsların TM0 sayıcısını 1/1 yani direkt saydıracağı bir konum mevcut değildir. Özellikle dışarıdan bir sinyal kaynağından gelen palsların sayılmasında lazım olacak olan bu husus için OPTION yazmacının 3. biti 1 (bir) yapılarak frekans bölme işlemi WDT için yapılır. Bu bir nevi aldatmadır. Bölme WDT için yapılınca Frekans bölücü TM0 için Bay-Pass edilmiş yani atlanmış olur. Dolayısıyla gelen sinyaller 1/1 olarak Timer0 tarafından sayılır. Bunu yapacağımız örnekte göreceğiz.
TM0 sayısının kullanılması için bazı parametrelerin önceden ayarlanması gerekiyor. Bunlar sırası ile;
1. Sayıcının sayabilmesi için gerekli olan clock sinyalinin kaynağı ne olacaktır?. Bu kaynak dahili osilatör olabileceği gibi standart olarak PortA.4 /TOCKI bacağı kullanılarak dışarıdan bir sinyal kaynağı ile beslenebilir. Bu seçim OPTION yazmacının 5. biti olan TOCS biti ile yapılır.
TOCS biti = 0 ise sinyal kaynağı dahili osilatör dür.
TOCS biti = 1 ise sinyal kaynağı PortA.4 pinin den giren harici sinyal kaynağıdır.
2. Harici sinyal kaynağı seçilir ise, sayacın düşen kenarda mı yoksa yükselen kenarda mı sayma işini yapacağı OPTION yazmacının 4. biti olan TOSE biti ile ayarlanır.
TOSE Biti = 0 ise düşen kenarda sayma
TOSE Biti = 1 ise Yükselen kenarda sayma yapılır.
3. Frekans bölme işlemi TMR0 için mi yoksa WDT için mi geçerli olacaktır? Bunu seçmek için OPTION yazmacının 3. biti olan PSA biti kullanılır.
PSA Biti = 0 ise Frekans Bölme TMR0 için geçerli,
PSA Biti = 1 ise Frekans Bölme WDT için geçerli olur.
4. Son olarak da Frekans bölme kullanılacak ise değeri ayarlanır. Bunun içinde OPTION yazmacının ilk 3 bitinin kullanıldığını söylemiştik. Tabloda verdiğimiz değerlerden biri seçilerek OPTION yazmacına yazılır.
5. Programın baş kısmına ON INTERRUPT GOTO KESME komutu verilerek kesme oluştuğunda programın gideceği yer (KESME) belirlenir.
6. INTCON yazmacının 5. biti bu kesme için ayrılmıştır. Bu bit 1 yapılarak kesme aktif edilir.
7. Kesme bölümüne başlarken DISABLE komutu verilerek kesme anında yeniden kesme oluşumuna imkan verilmeyecek.
8. Kesme bölümünde INTCON.2=0 komutu verilerek kesmeden dolayı 1 olan bayrak tekrar sıfırlanır ve sonradan yeni kesme oluşmasına imkan tanınır.
9. Kesme bölümünün sonunda RESUME komutu verilerek programın kesme oluşmasından önceki yerine dönmesi sağlanır.
10.En sona ENABLE komutu yazılarak kesmeden dönüldükten sonra tüm kesmeler aktif hale getirilir.
Burada bir konuyu açıklamakta fayda görüyorum. Dahili veya harici osilatör kullanılması durumunda, frekans değerinin Pic’e bağlı kristal değerinin dörtte biri olacağını bilmeniz gerekiyor. Diyelimki Pic 4 MHz lik bir kristal ile çalışıyor. O halde TMR0 için kullanılacak sinyal kaynağı 1 MHz frekansa sahip olacaktır.
Dahili osilatör ve Frekans bölücü pek çok uygulamalarda kullanılır. Özellikle hassas zamanlama işlerinde önemli bir kullanım alanı vardır. Bunların başında Pic’in bir saat olarak kullanılması gelir. Dahili veya harici ösilatör ve frekans bölücü kullanılarak pic’in her bir saniyede bir kesme oluşturmasını sağlayabiliriz. Şayet bu zamanı hassas bir şekilde ayarlayabilir isek doğru çalışan bir saat yapabiliriz.
Şimdi bunu bir örnek ile açıklayalım. Yapacağımız örnek de 4 MHz de çalışan bir 16F628 kullanacağız. Dahili sinyal kaynağını kullanarak gelen sinyali 64’e böleceğiz. Bu durumda pic, TMR0’ ile 0 dan başlayıp 255’e kadar sayıp kesme oluşturabilmesi için ;
1 us x 64 x 256 = 16384 us süre kullanacaktır. 1 sn = 1000 ms ve oda 1000.000 us ye eşit olduğundan şayet 1.000.000 us değerini 16384’e bölersek 61 değerini buluruz.
O halde her kesme oluştuğunda bir başka değişkeni saydırır ve bunun değeri 61 den 62 ye geçtiği anda değerini sıfırlayıp saniye değerini bir artırırsak bir saniyelik saat palslarını yakalamış oluruz. Bunu bir programda kullanarak da saat yapabiliriz.
Bu örnek için bir şemamız aşağıdadır.
Programa geçmeden önce gerekli parametrelerimizi tespit edelim;
Tespitlerimizin etki alanı OPTION Yazmacı olacağından tüm tespitleri Binary olarak bu yazmaca işleyeceğiz.
1. Sinyal kaynağımız dahili osilatör olacaktır. O halde TOCS (5.bit) 0 olacaktır.
OPTION_REG=%00000000
2. Frekans Bölme işlemi TMR0 için olacak olup 3. bit 0 olacaktır.
OPTION_REG=%00000000
3. Frekans bölme (prescaler) değeri 64 olacak olup bunun TMR0 için bit karşılığı 101 dir.
OPTION_REG=%00000101
4. TMR0 kesmesini kullanacağımızdan INCON yazmacının 5. biti high olacaktır.
Tüm Kesmeleri açmak için INTCON 7. biti high olacağından bu iki durumu tek komutta toparlarsak, INTCON=%10100000 şeklinde bir komut yazmamız gerekir.
Bu aşamadan sonra programımızı verelim.
'****************************************************************
PORTA=0:portb=0
TRISB=%00000000 'PortB tamamı çıkış yapıldı.
TRISA=%00000000 'A portu tamamı çıkış yapıldı.
'-----------------------------------------------------------------
@ DEVICE pic16F628 'işlemci 16F628
@ DEVICE pic16F628, WDT_OFF 'Watch Dog timer kapalı
@ DEVICE pic16F628, PWRT_ON 'Power on timer açık
@ DEVICE pic16F628, PROTECT_OFF 'Kod Koruma kapalı
@ DEVICE pic16F628, MCLR_off 'MCLR pini kullanılıyor.
@ DEVICE pic16F628, INTRC_OSC_NOCLKOUT 'Dahili osilatör kullanılacak
'-----------------------------------------------------------------
DEFINE LCD_DREG PORTB 'LCD data bacakları hangi porta bağlı?
DEFINE LCD_DBIT 4 'LCD data bacakları hangi bitten başlıyor?
DEFINE LCD_EREG PORTB 'LCD Enable Bacağı Hangi Porta bağlı?
DEFINE LCD_EBIT 3 'LCD Enable Bacağı Hangi bite bağlı ?
define LCD RWREG PORTB 'LCD R/W Bacağı Hangi Porta bağlı?
define LCD_RWBIT 2 'LCD R/W Bacağı Hangi bite bağlı ?
DEFINE LCD_RSREG PORTB 'LCD RS Bacağı Hangi Porta bağlı ?
DEFINE LCD_RSBIT 1 'LCD RS bacağı Hangi Bite bağlı ?
DEFINE LCD_BITS 4 'LCD 4 bit mi yoksa 8 bit olarak bağlı?
DEFINE LCD_LINES 2 'LCD Kaç sıra yazabiliyor
'-------------------------------------------------------------------------
ON INTERRUPT GoTo KESME 'kesme oluşursa KESME adlı etikete git.
OPTION_REG=%10000101 'Pull up dirençleri İPTAL- Bölme oranı 1/64.
INTCON=%10100000 'Kesmeler aktif ve TMR0 kesmesi aktif
TMR0=0
CMCON=7 '16F628 de komparatör pinleri iptal hepsi giriş çıkış
'----------------------------------------------------------------------------
SAYAC VAR BYTE
SN VAR BYTE
DAK VAR BYTE
SAAT VAR BYTE
GUN VAR BYTE
'-----------------------------------------------------------------------------
CLEAR 'tüm değişkenler sıfırlandı
PAUSE 200
LCDOUT $FE,1
LOW PORTB.2 ‘LCD -R/W bacağı LOW’a çekildi.
'-----------------------------------------------------------------------------
BASLA:
LCDOUT $FE,$84,DEC2 SAAT,":",DEC2 DAK,":",DEC2 SN
GOTO BASLA
DISABLE
KESME:
SAYAC=SAYAC+1 'kesme sayacı 1 sn= 61(sayac) x 256 (Tmr0) x 64 (bölme)
IF SAYAC=61 then '61 adet kesme olunca 1 sn. süre geçiyor.(999424 us)
SAYAC=0 'sayaç sıfırlanıyor
SN=SN+1 'saniye değeri bir artırılıyor
IF SN=60 THEN 'saniye 60 olmuş ise 1 dakika süre geçti o halde
SN=0 ' saniye sıfırlanıyor
DAK=DAK+1 ' dakika değeri bir artırılıyor
IF DAK=60 THEN 'dakika 60 olmuş ise 1 saat süre geçti
DAK=0 ' dakika sıfırlanıyor
SAAT=SAAT+1 ' saat değeri bir artırılıyor
IF SAAT=24 THEN 'saat 24 olmuş ise 1 gün geçti
SAAT=0 'saat sıfırlanıyor
GUN=GUN+1 'gün değeri bir artırılıyor
IF GUN=365 THEN GUN=0 'gün 365 olmuş ise
ENDIF 'gün sıfırlanıyor 1 yıl geçti
ENDIF
ENDIF
ENDIF
INTCON.2=0 'TMR0 Kesme bayrağı sıfırlanıyor
RESUME
ENABLE
END
‘-----------------------------------------------------------------------------------
Her ne kadar hesap sonucunda kesmenin oluşturacağı gecikme yaklaşık 1 sn hesaplanmış isede pratikte program komutlarınında bir gecikmeye sebep olacağı unutulmamalıdır.
Yukarıdaki programda KESME bölümünde bir çok komut bulunmaktadır. Makine dilinde her bir komut yaklaşık (4MHz de) 1 us süre almaktadır. Bu nedenle hesaplama doğru olsa da saat ileri gidebilir veya geri kalabilir. Bunun için SAYAC değeri her 61 kesme yerine 60 – 59 -58 kesmede bir saniye artırımı yaptırılabilir. En iyisi programın yazılması tamamlanınca bir doğru çalışan bir saat yardımı ile süre karşılaştırması yapılmalı ve gerekirse SAYAC değeri ile oynanarak hassas bir ayarlama yapılmalıdır.
Burada ikinci bir örnek daha vereceğiz ve saat’in başka ilerde nasıl kullanılacağına bakacağız.
Diyelim ki 4 adet tuş ile kontrol edilen 4 adet rölemiz var ve bunlar bir takım sistemleri çalıştırıyorlar. Her bir röle ayrı bir sistemi çalıştırıyor ve biz hangi sistemin ne kadar çalıştığını bilmek istiyoruz. İşte yazacağımız program 4 adet tuş yardımı ile 4 adet röleyi çalıştıracak ve arka planda ise TMR0 kesmesi yardımı ile saatimiz çalışacak.
Bu programda ayrıca yeni bir değişken tipi ile tanışacağız. Dizi değişkeni. Dizi değişkenleri aynı isimle açılırlar ancak her bir değişkenin bir indeks denilen parametresi vardır.
Indeks ayrı bir değişken altında saklanabilir ve değeri değiştirilerek istenilen dizi değişkenine kolaylıkla ulaşılabilir.
Kullanımı değişken tanımlarında şöyle yapılmaktadır.
DEGER var Byte [4] burada değer adlı değişkenin 4 ayrı indeksli çeşidi vardır. Bunlar ;
DEGER[0] – DEGER [1] – DEGER [2] ve DEGER [3] dür.
Önce programla ilgili şemamızı verelim;
Dikkat edilirse şemada röle yerine logic prop bağlanmıştır. Bunların yerine asıl devrede birer role bağlanacaktır. Ancak asıl amacımız programın işlevini göstermek olduğundan detaya girilmemiştir.
İşte programımız;
'****************************************************************
PORTA=0:portb=0
TRISB=%00001111 'PortB (RB4-RB7) çıkış diğerleri giriş yapıldı.
TRISA=%00000000 'A portu tamamı çıkış yapıldı.
'-----------------------------------------------------------------
@ DEVICE pic16F628 'işlemci 16F628
@ DEVICE pic16F628, WDT_OFF 'Watch Dog timer kapalı
@ DEVICE pic16F628, PWRT_ON 'Power on timer açık
@ DEVICE pic16F628, PROTECT_OFF 'Kod koruma kapalı
@ DEVICE pic16F628, MCLR_off 'MCLR pini kullanılıyor.
@ DEVICE pic16F628, INTRC_OSC_NOCLKOUT 'Dahili osilatör kullanılacak
'-----------------------------------------------------------------
DEFINE LCD_DREG PORTA 'LCD data bacakları hangi porta bağlı?
DEFINE LCD_DBIT 0 'LCD data bacakları hangi bitten başlıyor?
DEFINE LCD_EREG PORTA 'LCD Enable Bacağı Hangi Porta bağlı?
DEFINE LCD_EBIT 7 'LCD Enable Bacağı Hangi bite bağlı ?
DEFINE LCD_RSREG PORTA 'LCD RS Bacağı Hangi Porta bağlı ?
DEFINE LCD_RSBIT 6 'LCD RS bacağı Hangi Bite bağlı ?
DEFINE LCD_BITS 4 'LCD 4 bit mi yoksa 8 bit olarak bağlı?
DEFINE LCD_LINES 2 'LCD Kaç sıra yazabiliyor
'-------------------------------------------------------------------------
ON INTERRUPT GoTo KESME 'kesme oluşursa KESME adlı etikete git.
OPTION_REG=%10000101 'Pull up dirençleri İPTAL- Bölme oranı 1/64.
INTCON=%10100000 'Kesmeler aktif ve TMR0 kesmesi aktif
TMR0=0
CMCON=7 '16F628 de komparatör pinleri iptal hepsi giriş çıkış
'----------------------------------------------------------------------------
SAYAC VAR BYTE
SN VAR BYTE[5]
DAK VAR BYTE[5]
SAAT VAR BYTE[5]
GUN VAR BYTE
DURUM var byte
I VAR WORD
ESKI VAR BYTE
ROLE VAR BYTE
'-----------------------------------------------------------------------------
CLEAR 'tüm değişkenler sıfırlandı
PAUSE 200
LCDOUT $FE,1
'-----------------------------------------------------------------------------
BASLA:
LCDOUT $FE,$84,DEC2 SAAT[0],":",DEC2 DAK[0],":",DEC2 SN[0]
DURUM=PORTB & %1111
IF DURUM>0 THEN PORTB=DURUM
ESKI=PORTB | DURUM*16
ROLE=NCD ESKI 'NCD KOMUTU SAYIDAKİ EN YÜKSEK HİGH OLAN BİTİ VERİR
ROLE=((ROLE>0)&%1)*(ROLE-4) 'BİR ÇOK İŞİ GÖREN TEK BİR KOMUT
LCDOUT $FE,$C0,"ROLE=",#ROLE," ",DEC2 SAAT[ROLE],":",DEC2 DAK[ROLE],":",DEC2 SN[ROLE]
PORTB=ESKI
FOR I=0 TO 2000
PAUSEUS 10
NEXT I
GOTO BASLA
DISABLE
KESME:
SAYAC=SAYAC+1 'kesme sayacı 1 sn= 61(sayac) x 256 (Tmr0) x 64 (bölme)
IF SAYAC=61 then '61 adet kesme olunca 1 sn. süre geçiyor.(999424 us)
SAYAC=0 'sayaç sıfırlanıyor
SN[0]=SN[0]+1
IF ROLE=0 then ATLA
SN[ROLE]=SN[ROLE]+1 'saniye değeri bir artırılıyor
ATLA: IF SN[0]=60 THEN 'saniye 60 olmuş ise 1 dakika süre geçti ohalde
SN[0]=0
SN[ROLE]=0 ' saniye sıfırlanıyor
DAK[0]=DAK[0]+1
DAK[ROLE]=DAK[ROLE]+1 ' dakika değeri bir artırılıyor
IF DAK[0]=60 then 'dakika 60 olmuş ise 1 saat süre geçti
DAK[0]=0
DAK[ROLE]=0 ' dakika sıfırlanıyor
SAAT[0]=SAAT[0]+1
SAAT[ROLE]=SAAT[ROLE]+1 ' saat değeri bir artırılıyor
IF SAAT[0]=24 THEN 'saat 24 olmuş ise 1 gün geçti
SAAT[0]=0
SAAT[ROLE]=0 'saat sıfırlanıyor
GUN=GUN+1 'gün değeri bir artırılıyor
IF GUN=365 THEN GUN=0 'gün 365 olmuş ise
endif 'gün sıfırlanıyor 1 yıl geçti
ENDIF
ENDIF
ENDIF
INTCON.2=0 'TMR0 Kesme bayrağı sıfırlanıyor
RESUME
ENABLE
END
'-----------------------------------------------------------------------------------------
Sayac=61 satırında 61 sayısı 1 sn lik süreyi ayarlamaktadır. Program komutlarındaki gecikmeler nedeni daha hassas bir ayarlama gerekebilir. Bir saatle birlikte kontrol edilerek pic saati geri kalıyorsa değeri azaltılmalıdır. İleri gidiyor ise artırılmalıdır.
Programla iligli genel açıklamalar;
Bu programda ileri programlama teknikleri kullanılmıştır. Program satırlarını esas alarak açıklamaya çalışalım.
‘--------------------------------------------------------------------------------------------------------
LCDOUT $FE,$84,DEC2 SAAT[0],":",DEC2 DAK[0],":",DEC2 SN[0]
‘--------------------------------------------------------------------------------------------------------
Programda saat için 5 adet indeks li değişken kullanılmış idi. Bunun amacı her bir röle için ayrı bir zaman tutmak ve sıfır ile çalışan indeks değerini de ana saat için kullanmaktır. Yani saat(0) sistemin ana saati olacak Saat(1-4) arası ise her bir röleye ait olacaktır.
Yukarıdaki komutta görüldüğü gibi [0] indeksi kullanılmış ve dolayısıyla sistem ana saati LCD üst satırında ekrana verilmiştir.
‘--------------------------------------------------------------------------------------------------------
DURUM=PORTB & %1111
‘--------------------------------------------------------------------------------------------------------
Bu komutta Tuşların hangisine basıldığını kontrol etmek için tek bir komut kullanılmıştır.
PORTB değeri okunarak 15 (%1111) sayısı ile AND işlemi uygulanmıştır. Dolayısıyla PortB nin ilk 4 biti filtre edilerek ayrılmış olacak ve bu bitlerden hangisi 1 ise (tuşa basılmış ise) belirlenecektir.
‘--------------------------------------------------------------------------------------------------------
IF DURUM>0 THEN PORTB=DURUM
‘--------------------------------------------------------------------------------------------------------
Burada herhangi bir tuşa basılmış ise eski basılmış olanı iptal etmek ve yenisini devreye sokmak için bu komut kullanılmıştır.
‘--------------------------------------------------------------------------------------------------------
AKTIF=PORTB | DURUM*16
‘--------------------------------------------------------------------------------------------------------
Burada basılan tuşa paralel olarak hangi rolelin aktif hale getirileceği hesaplanmaktadır.
Bir yerde basılan tuşun bit değeri 4 bit sola kaydırılarak PortaB ye verilmektedir.
Hangi rölenin aktif olduğunu ise aşağıdaki komut hesaplamaktadır.
‘--------------------------------------------------------------------------------------------------------
ROLE=NCD AKTIF 'NCD KOMUTU SAYIDAKİ EN YÜKSEK HİGH OLAN BİTİ VERİR
‘--------------------------------------------------------------------------------------------------------
NCD komutu bir sayı içindeki en yüksek değerlikli high olan bitin konumunu verir.
Örnek vermek gerekir ise SAYI=%00010000 ise NCD SAYI komutu bize 5 sayısını verecektir. En yüksek high 5. sıradaki 1 dir. Bu komut yardımı ile basılan tuşa göre hangi rolenin aktif olduğunu belirliyoruz. Ancak bu belirleme bize PORTB deki sırayı verecektir. Yani 4-8 arası bir değer. Halbuki biz 1. Role – 2. Role … 4.Role demek isteriz. Dolayısıyla bu değeri 1-4 arası değere indirgememiz gerekir. Burada işlem aslında basit Hesaplanan değerden 4 çıkartır isek bize 1-4 arası değeri verir. Ancak ya değer 0 (sıfır) ise. Bu durumda 0-4 bize 65531 gibi bir değer verirki buda sistemin hata yapmasına sebep olur. Bu durumda şayet Role değeri sıfır ise bu hesabı yapmadan atlamamız gerekir. Akıllıca bir programlama mantığı ile bu işi tek komutla halledebiliriz.
‘--------------------------------------------------------------------------------------------------------
ROLE=((ROLE>0) & %1)*(ROLE - 4) 'BİR ÇOK İŞİ GÖREN TEK BİR KOMUT
‘--------------------------------------------------------------------------------------------------------
Bu komut bizi birkaç defa if kullanmaktan kurtarır. Bu komut aşağıdaki program satırlarının görevini yapmaktadır;
‘--------------------------------------------------------------------------------------------------------
IF ROLE=0 THEN Hesap yapma ve Ekrana bir şey yazma
IF ROLE>0 then ROLE= Role-4 hesabını yap
‘--------------------------------------------------------------------------------------------------------
Şimdi komut nasıl çalışıyor bir bakalım;
(ROLE>0) & %1 komutunda (ROLE>0) ifadesi bir logik operatör kullanan bir aritmetik ifadesidir. Role>0 ise değeri 255, Role=0 ise değeri 0 dır. Dolayısıyla bunu %1 ile AND işlemine tabi tutar isek Role 0 dan büyük olunca değeri 1, sıfıra eşit olunca ise değeri 0 olarak gelir. Bu ifadeyi (ROLE-4) ile çarpar isek istediğimizi elde etmiş oluruz. Yani
1 x (Role-4) burada Role 0 dan büyük olduğundan role değeri 1-4 arası çıkacaktır.
0 x (Role-4) ki burada Role 0 olduğundan sonuç 0 olacaktır.
‘--------------------------------------------------------------------------------------------------------
LCDOUT $FE,$C0,"ROLE=",#ROLE," ",DEC2 SAAT[ROLE],":",DEC2 D
DAK[ROLE],":",DEC2 SN[ROLE]
‘--------------------------------------------------------------------------------------------------------
Komutu ile aktif olan role ile o röleye ait saat değeri ekrana getirilmektedir.
‘--------------------------------------------------------------------------------------------------------
PORTB=AKTIF
‘--------------------------------------------------------------------------------------------------------
Aktif olan role ekrana verilmektedir.
Kesme oluşmasında kolaylık sağlamak üzere 20 ms lik gecikme us cinsinden döngü kullanılarak verilmektedir.
‘--------------------------------------------------------------------------------------------------------
FOR I=0 TO 2000
PAUSEUS 10
NEXT I
‘--------------------------------------------------------------------------------------------------------
GOTO BASLA
‘--------------------------------------------------------------------------------------------------------
Goto başla ile program tekrar başa yönlendirilmektedir.
Programın Kesme kısmında ise 1 sn lik süre Sayac=61 sayısı ile kontrol edilmekte ve bu süre sonunda Sn=Sn+1 komutu ile değeri bir artırılmaktadır. Sn değerinin 60’ı geçmesi halinde dakika ve paralel olarak saat ve gün değerleri artırılmaktadır.
Buradaki diğer önemli nokta ise o andaki ROLE değeri ki burada indeks olarak kullanılmakta esas alınarak o roleye ait saat değeri ise ayrıca saydırılmaktadır.
Tabiiki hiçbir role aktif değil ise role değeri 0 olacağından sistem ana saati saydırılmaktadır.