C

Proton ile Timer1

Başlatan caylak, 14 Mayıs 2012, 03:56:10

caylak

Selam,

Genelde pic basic konularına değinildiği için sormak istedim, acaba Proton derleyici ile oluşturduğumuz bir programdaki sorunumuzu burda paylaşmak mümkünmü?


ete

Paylaşabilirsin elbette. Bilen çıkarsa cevaplanır.

Ete

caylak

Yeni başladığım bir uygulamada Timer kullanıyorum ve pek tecrübem olmadığı için öneriler bekliyorum..

Uygulamayı özet olarak anlatmam gerekir ise;

LCD ekrandaki menüden bir çıkışın "1" olma süresi "0" olma süresi tanımlanacak. Ayrıca bu "1" olan süre içinde çıkış yine zamanı ayarlanabilen "on-off" şeklinde çalışacak..

Girilen zaman değeri minimum 0,1 sn olacak, "0" kalma süresi ise maksimum 24 saat olacak..

Menü hazırlama konusunda sıkıntım yok fakat Timer çalışması konusunda şüphelerim var..

İkinci mesajımda şu ana kadar Proton basic ile yapmış olduğum çalışmayı ve timer konusunda anladıklarımı paylaşmaya çalışacağım..

ete

Timer1 kullanımı zor bir iş değil. Ancak yapmak istediğinle TMR1 sayacını bağdaştırmak biraz zor olabilir.
0,1sn 100 ms eder ki bu TMR1 için kolay bir işlem. Ancak LOW da kalma süresi 24 saat diyorsunki bu iş biraz sıkıntı doğurabilir. Sıkıntı derken ayrı bir sayaç kullanarak bunu kolaylıkla halledebilirsin.
Mademki 100 ms en küçük değerin sen bunu 50 ms kabul et. TMR1 kemesini aktif et sayacı sıfırla . Diyelimki HIGH süresi sayacaksın.  İlk 50 ms sonunda kesme oluşacak sayac=sayac+1 yapacaksın sayac=1 olacak. İkinci 50 ms de (100ms sonra) sayac=sayac+1 olacak ve sayac değeri 2 olacak bu senin durma zamanını gösterecek ve sayacı sıfırlayacaksın. Başka ne yapacaksan yapacaksın. Diyelimki LOW sayacaksın.  Önce sayac değerininne olması gerektiğini hesaplayalım.
1 sn=1000 ms eder. Bu ise 1000/50=20 sayac demektir.
1 dak=60 sn eder bu ise 60 x 20 =1200 sayac değeri eder.
1 saat=60 dak eder bu ise 1200x60=72.000 sayac eder.
24 saat ise 72.000 x 24= 1.728.000 sayac değeri eder.
Bu durumda DWORD tipi bir değişken kullanarak sayacı sıfırladığın andan itibaren 1.728.000 kesme adedine ulaştığın zaman LOW konumunu sonlandırman gerekir (24 saat süre için)
Gerisini halletmek senin için zor olmamalı herhalde.
TMR1 sayacı direk ulaşılan bir registere sahip değil. 2 adet 8 bitlik register aracılığı ile ön değer virilip değeri sıfırlanabilmektedir. Bunlar TMR1L (Low byte) TMR1H (High Byte) TMR1 i sıfırlamak için bu registerlerin her ikisine sıfır değerini vermen yeterlidir. Bir ön değer vermek için onu High ve Low byte olarak ikiye ayırman gerekecek.
Şayet dword değişken kullanmaz isen iki 16 bitlik değişken kullanarak bu işi halledebilirsin. Birisi sürekli 65535 sayar ve her 65536 olduğunda diğer sayacın bir artırılır. Böylece 1.728.000 saydırmak için , Birinci sayac 26 saydığı anda senin süren 1.728.000 sayac değerini saymış olacaktır. (1.728.000/65535=26)

TMR1 kesmesi oluşturmak için;
T1CON=%00110001          'BİT 5-4 İLE BÖLÜCÜ ,00=1/1 , 01=1/2 , 10=1/4 , 11=1/8 bölme oranına eşdeğerdir.
PIE1.0=1                 'komutu ile her 1000 ms=1sn de bir PULSOUT döngüsü çalıştırılıyor.
PIR1.0=0                 ' kesme bayrağı
TMR1H=$3C:TMR1L=$B7      'Önyükleme değerinin verilmesi
ON INTERRUPT GOTO kesme
şeklinde ayarlama yapmak gerekir. Mesela 50 ms de bir kesme oluşturmak için; (4Mhz kristal frekansı ile)
Bölme oranı 1/1 olacak, TMR1 e ön yükleme değeri olarak 15543 değeri yüklenecektir. 15543 sayısının hex karşılığı $3CB7 dir.
O halde TMR1L=$B7, TMR1H=$3C şeklinde ön değer verilecektir. Kesmeden çıkarken Kesme bayrağı sıfırlanacak ve çıkmadan önce ön yükleme değeri yeniden TMR1 e verilecektir.

Neden ön yükleme değeri veriyoruz onu açıklayayım.
4 Mhz kristal frekansında 4/4=1 us lik bir komut çevrim süreci vardır. Bu şu demektir. TMR1 sayacı her 1us de bir sayar.
50 ms sayılacağına  ve sayma sonunda kesme oluşacağına göre bizim önce 50 x 1000=50.000us saymamız gerekiyor.
Kesme, ancak sayacın değer olarak  65535 den 65536 ya geçerken oluşur. Yani sayaçda taşma olduğu zaman oluşur. Bizim 50.000 saymamız gerek. O halde ben TMR1 e öyle bir değer vermeliyimki o değerden saymaya başlasın ve 50.000 sayac saydıktan sonra değeri 65535 olsun. Bu durumda 65536-50.000=15536 . Bizim verdiğimiz komutların da bir gecikmesi olur. Bunu genellikle 7 sayac değeri olarak alırız. Bunuda sayaca eklersek 15536+7=15543 değerini bulmuş oluruz. O halde sayacımız 15543 den başlayacak ve 65535 den 65536 ya geçerken kesmeyi oluşturacaktır.
Umarım anlaşılmıştır.

Ete


caylak

ETE Hocam aciklama icin tesekkur ederim..Anladiklarim asagi yukari senin anlattiklarin gibi..anlattiklarini baz oiarak isyerime dondugumde uygulamayi hazirlayip paylasacagim..

caylak

Timer test için yazmış olduğum kod aşağıdaki gibi :

'**********TİMER1 AYARLAMA************

INTCON = %11000000 'Tüm kesmelere izin ver

T1CON = %00010001  ' Timer1 Açık,Dahili Osilatör Seçili, ÖnÖlçekleyici 1:2

PIE1.0 = 1         ' TMR1 Aşımı Kesme Oluşturma Aktif Etme Biti

PIR1.0 = 0         ' TMR1 Aşım Kesme Bayrağı


'************* KESME AYARLAMA*****************
INTCON.7 = 1  ' Global kesmeler aktif edildi

On Interrupt GoTo KESME   ' Kesme oluştuğundan KESME bloğuna dallan

TMR1H = $3C     '8Mhz de 50ms kesmeyi 1:2 önçekleyici ile oluşturmak için
TMR1L = $B7     'TMR1 yazmaçlarına yüklenmesi gereken değer


50ms de oluşan kesmeyi sayaç değişkeni ile değerlendiren kısım aşağıda;

Disable
KESME:
INTCON.7 = 0
      SAYAC=SAYAC+1         'Her 50ms bir kesme oluştuğunda sayaç 1 arttırılıcak
      If SAYAC=20 Then     '20 adet kesme oluştuğunda 1000ms = 1sn oluşur
         SAYAC=0            'sayaç sıfırlanıyor
         SN=SN+1            'saniye değeri bir artırılıyor
         If SN=60 Then     
               SN=$00           ' 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
                            EndIf
                        EndIf               
                  EndIf
INTCON.7 = 1        'TMR0 Kesme bayrağı sıfırlanıyor
         Resume
         Enable

End



Ana döngü içinde LCD ekranda Sayaç, saniye ve dakika değerlerini bastığımda çok farklı bir sayma ile karşılaşıyorum..

Ana:

    Print $fe,$80,Dec2 DAK,":",Dec2 SN
    Print $fe,$c0,Dec4 SAYAC
   
    GoTo Ana




ete

#6
Programın tamamını vermediğin için karışıklık oluyor. Verdiğin program parçaları aynı programa mı aittir bilemiyorum.
TMR1 kesmesi oluşturmak için hem Global kesmeler hemde peripheral kesmeler aktif edilmelidir.
Test kısmında bunu yapmışsın ama  Kesme Ayarlama kısmında yalnızca Global kesmeleri açmışsın.
Dolayısıyla INTCON.6 yıda aktif etmelisin. Şayet programlar aynı programa ait ise o zaman Kesme ayarlama kısmında verdiğin INTCON.7=1 komutunun gereksiz olduğunu söylemem gerekir. Zira başta bunu yapmışsın zaten iki defa aynı komutu vermiş oluyorsun.

Ayrıca kodların yanına yazdığın açıklamalarda 8 Mhz gibi bir ifade görüyorum. Benim verdiğim değerler 4 Mhz içindir.
8 Mhde süreleri veya ön yükleme değerlerini yeniden hesaplamak gerekir.
8 Mhz kristal frekansında bir komut çevrimi 0,5us dir. Bu durumda TMR1 sayacı her 0,5us de bir sayar.
kesme süremiz olan 50 ms için sayacın 50000/0,5=100.000 sayması gerekirki bu mümkün değildir. Zİra maksimum sayma değeri 65535 i geçemez.
Bu durumda TMR1 bölme oranını 1/2 olarak almak gerekecektir. Bu durumda 2 komut çevrimi 1us sayacağı için;
(100.000us - 7us (komut gecikmesi))/2=49.996us sayılacak demektir. Bunu 65535 den çıkartırsak 15539 gibi bir değer elde ederiz. Bu bizim TMR1 ön yükleme değerimiz olacaktır.
O halde 8 Mhz de yalnızca T1CON registerinde 4 ve 5 nolu bitlerin değeri değiştirilerek 1/2 bölme oranı verilecek

Sen TMR1 ayarlamada 1/2 bölme oranını vermişsin zaten. Ancak TMR1 ön yükleme değerin biraz farklı. Aslında çok şey fark etmemesi gerekir. Ön yükleme değerleri epeyce yakın bir birine. SOnuç olarak programın doğru çalışması gerekir.

Programın diğer kısımlarında pause komutları kullandı isen o sistemin doğru çalışmasını etkileyebilir.

En önemli hata ise  TMR1 kesme bayrağının kesmeden çıkarken resetlenmemiş olması sanırım.
PIR1.0 = 0 şeklinde resetlenmesi gerekiyor.
Aynı şekilde Kesmeden çıkmadan önce TMR1 ön yükleme değerini yeniden vermemiz gerekiyorki aynı şekilde her 50us de bir kesme oluştursun. O halde kesme bayrağını sıfırlamadan önce;
TMR1H=$3C:TMR1L=$BC   satırınıda eklemek gerekiyor.

Ete

caylak

#7
Hocam kodun tamamını aşağıda verdim.. TMR1 yazmaçlarına verdiğim değer 15536 + 7 (daha önceki mailde belirttiğiniz gibi) 15543 değerin hex karşılığı olan 3CB7'dir.

Global kesmeyi dediğiniz gibi iki kere girmişim.. düzelttim.. Kesme çıkışına TMR1 Aşma bayrağı silmeyide ekledim PIR1.0=0

ISIS'de yapmış olduğum ilk denemede herşey yolunda görülüyor, eve gidince Easypic deneme bordunda tekrar deneyeceğim..

Benim kodumdaki problem Kesme çıkışında bayrağı temizlemem oldu sanırım, global kesmeleri aktif etmemin pek etkisi olduğunu düşünmüyorum..zaten global kesme ektif etme satırını ekledim problem oluşturmadan çalıştı.. Tek yanlış PIR1.0 = 0 satırını kesme çıkışında unutmam..

Son satırınızı tekrar okuduktan sonra TMR1 yazmaçlarına ait değerlerin yüklenmesinide ekledim kodun içine..

Yardımınız için teşekkür ederim şimdi uygulamanın diğer kısımları sırada..


Device 16F877
Config WDT_OFF,PWRTE_ON,BODEN_OFF ,LVP_OFF ,CP_OFF,XT_OSC
Xtal = 8

PortB_Pullups = Off

'**********TİMER1 AYARLAMA************

INTCON = %11000000 'Tüm kesmelere izin ver

T1CON = %00010001  ' Timer1 Açık,Dahili Osilatör Seçili, ÖnÖlçekleyici 1:2

PIE1.0 = 1         ' TMR1 Aşımı Kesme Oluşturma Aktif Etme Biti

PIR1.0 = 0         ' TMR1 Aşım Kesme Bayrağı

'************* KESME AYARLAMA*****************

On Interrupt GoTo KESME   ' Kesme oluştuğundan KESME bloğuna dallan

TMR1H = $3C     '8Mhz de 50ms kesmeyi 1:2 önçekleyici ile oluşturmak için
TMR1L = $B3     'TMR1 yazmaçlarına yüklenmesi gereken değer
               

TRISD = %00000000
TRISC = %00000000
TRISB = %11111111
TRISA = %11111111
PORTD = 0
PORTC = 0

Dim SAYAC   As   Word
Dim SN      As   Byte
Dim DAK     As   Byte
Dim SAAT    As   Byte
Dim GUN     As   Byte

'*********LCD TANIMLAMA*********************
Declare LCD_DTPin PORTD.4     ' The control bits B4,B5,B6,B7
Declare LCD_RSPin PORTD.2     ' RS pin on B2
Declare LCD_ENPin PORTD.3     ' E pin on B3
Declare LCD_Interface 4       ' Interface method is 4 bit
Declare LCD_Lines 4

SN = $00
SAYAC= $00
DAK = $00

Print $fe,1

Ana:

    Print $fe,$80,Dec2 DAK,":",Dec2 SN
    Print $fe,$c0,Dec4 SAYAC
   
    GoTo Ana

Disable
KESME:

      SAYAC=SAYAC+1         'Her 50ms bir kesme oluştuğunda sayaç 1 arttırılıcak
      If SAYAC=20 Then     '20 adet kesme oluştuğunda 1000ms = 1sn oluşur
         SAYAC=0            'sayaç sıfırlanıyor
         SN=SN+1            'saniye değeri bir artırılıyor
         If SN=60 Then     
               SN=$00           ' 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
                            EndIf
                        EndIf               
                  EndIf
TMR1H = $3C     
TMR1L = $B3     
PIR1.0 = 0

         Resume
         Enable

End



ete

TMR1 ön yükleme değerinide kesmeden çıkmadan önce TMR1'e yüklemen lazım. Aksi taktirde TMR1 sayacı sıfırdan başlar ve 65535'e kadar sayar buda saatin geri kalması anlamına gelir. Ön yükleme değeri kristal frekansına göre biraz değişiyor.
Bölme oranını 1/2 yapsan bile komut gecikmeleri 1/1 oranına göre hesaplanır. Sonuç olarak 7/2 yaklaşık 4 dür. O halde önceden komut gecikmesi olarak 7 eklemiş olmamıza rağmen 8 Mhz de 4 eklememiz yeterli olacaktır. Bu 1/2 bölme oranı ile aslında 8 komut gecikmesine eşdeğer olacaktır.
Bu nedenle 1543 yerine 1539 kullanman gerekiyor.
Kesme etiketi altında;
INTCON.7 = 0  komutuda gereksiz. Zira bu işi zaten Disable komutu yapıyor. Buda aynı iş için iki aynı komutu vermek gibi bir şey. Bu nedenle onlarıda oradan çıkart.

Başkada bir şey yok zaten.

Ete

caylak

#9
Düzeltilmiştir.

Edit: Ete
Ama INTCON.7=1 satırı orada duruyor!!!!!!!!!!!!!!!!

caylak : Son düzeltmeyi PC'de yapmışım :)

er-da

#10
merhabalar...

Timer1 ile ilgili  sorum olacak .. Timer1 e Timer1H=0 Timer1L=0  değerleri atarsam Tmer1 sıfırlayıp kesme içerisinde
herhengi bir yerde 65535 e varmadan çıkmak için hangi komutu kullanmalıyım ?

örnek olarak
  toggle led
  delayus  ara '  (burası değişken zaman olabilir)
  Tmr1H=0
  Tmr1L=0
  buraya   kesmeden çık komutu

düzenleme    :
alttaki gibi kesme  çalıştırdım ama ana döngü içindeki proğram çalışmıyor...

                                                                   
Device = 16F628A   ' Selected PICMicro
Xtal = 20   ' Oscillator frequency  20 MHz
                                                                   
'-------------------------- Configuration --------------------
                                                                   
Config  BODEN_OFF, BOREN_OFF, CP_OFF, DATA_CP_OFF, PWRTE_ON, WDT_OFF, LVP_OFF, MCLRE_OFF, HS_OSC
                                                                   
'-------------------------- Ports settings ---------------------------------
                                                                   
Declare PortB_Pullups = 0   ' Disable PORTB pullup resistors
Declare  All_Digital = true   ' Set analogue pins to digital
                                                                   
'-------------------------- Special function registers -------------------
PORTA=%00000000
PORTB=%00000000                                                                 
PORTB=0
PORTA=0
'-------------------------- INTCON --------------------------------------------
                                                                   
Symbol RBIF = INTCON.0 ' RB Port Interrupt Flag
Symbol INTF = INTCON.1 ' RB0 External Interrupt Flag
Symbol T0IF = INTCON.2 ' TMR0 Overflow Interrupt Flag
Symbol RBIE = INTCON.3 ' RB Port Change Interrupt Enable
Symbol INTE = INTCON.4 ' RB0 External Interrupt Enable
Symbol T0IE = INTCON.5 ' TMR0 Overflow Interrupt Enable
Symbol PEIE = INTCON.6 ' Peripheral Interrupt Enable
Symbol GIE = INTCON.7  ' Global Interrupt Enable
                                                                   
'-------------------------- PIE1, PIR1 ----------------------------------------------
                                                                   
Symbol TMR1IE = PIE1.0 ' TMR1 Overflow Interrupt Enable
Symbol TMR1IF = PIR1.0 ' TMR1 Overflow Interrupt Flag
                                                                   
'-------------------------- T1CON ---------------------------------------------------
                                                                   
Symbol TMR1ON = T1CON.0     ' Timer1 Enable
Symbol TMR1CS = T1CON.1     ' Timer1 Clock Source Select
Symbol NOT_T1SYNC = T1CON.2 ' Timer1 External Clock Input Synchronization Control
Symbol T1INSYNC = T1CON.2   ' Timer1 External Clock Input Synchronization Control
Symbol T1SYNC = T1CON.2     ' Timer1 External Clock Input Synchronization Control
Symbol T1OSCEN = T1CON.3    ' Timer1 Oscillator Enable Control
Symbol T1CKPS0 = T1CON.4    ' Timer1 Input Clock Prescale Select bits
Symbol T1CKPS1 = T1CON.5    ' Timer1 Input Clock Prescale Select bits
                                                                   

                                                                   
'-------------------------- TMR1 adjustment --------------------

T1CON = %00000001
TMR1IE = 1    ' Enable interrupt when TMR1 overflow
                                                                   
PEIE = 1    ' Peripheral interrupt enable
GIE = 1    ' Global interrupt enable
Dim ara As Byte
ara=10

                                                                   
On_Interrupt GoTo  Int_Label
GoTo  MainProgram
                                                                   
'-------------------------- Interrupt--------------------------
                                                                   
Int_Label:
Context Save                   '                                 
  If TMR1IF=1 Then
     TMR1IF=0
      TMR1L = 0
      TMR1H = 0
      TMR1ON=1
      Toggle PORTB.1
      DelayUS ara
  EndIf
  TMR1IF=1                                                             
Context Restore                ' Return from the interrupt subroutine
                                                                   
'-------------------------- Main program -------------------------
                                                                   
MainProgram:
PORTA.0=1
DelayUS 500
PORTA.0=0
DelayUS 500                                                           
GoTo  MainProgram   ' Return to main program
                                                                   
                                                                   


ete

Tmr1 sayıp 65535'i aştığı anda TMR1IF=1   olur. Bu TMR1 kesme bayrağıdır. Bu bayrak 1 olduğu müffetçe sistem kesmeye gider hep. Kesmeden çıkarken bu bayrağın sıfırlanması gerekir ki yeniden kesme oluşturmasın.
Peki siz ne yapmışsınız kesemden çıkmadan önce zaten 1 olan bu bayrağı yeniden 1 e eşitlemişsiniz. Onun yerine,
TMR1IF=0
yazın düzelir.

Ete
 

emrej

selam arkadaşlar 12f675 teki sayac degişkenin deki 655 sayısını serial iletişimle 16f676 ya göndermek istiyorum ama 255 in üstü sayıyı gönderemiyorum
lütfen yardımcı olurmusunuz.



GONDER:
    SEROUT2 CIKIS,813,[Rep $AA\5, Rep $00\5, Rep $FF\5]
    SEROUT2 CIKIS,813,["A",SAYAC]
    RETURN
   END


ete

Anladığım kadarı ile sayac değişkenin WORD cinsinden.
Programın değişken tanımlama kısmını koymadığın için anlayamıyoruz hali ile.
SAYAC isimli değişken WORD tipinde ise şöyle yapman gerek,
GONDER:
    SEROUT2 CIKIS,813,[Rep $AA\5, Rep $00\5, Rep $FF\5]
    SEROUT2 CIKIS,813,["A",SAYAC.BYTE1,SAYAC.BYTE0]
    RETURN
   END

Ete

emrej

#14
ETE hocam cevabın için teşekkür ederim
ete hocam alıcı kısmındaki hatamı çözdüm
yardımcı oldugun için çok teşekkür ederim

Powered by EzPortal