Timer0 Kesme Zaman Ayarı

Başlatan aRci, 06 Mart 2022, 00:50:40

aRci

Merhaba;
Timer0 la birlikte bir geçen süreyi gösteren proje yapmak istiyorum amacım pc başında iken bazen çok fazla kalıyorum örneğin 1 saat yada 2 saat yada ne kadar süredir pc başında kaldığımı hemen masamın üzerinde bir display saatte görmek için proje yapmak istedim.

Aslında projeyi 2 saatte programını yaptım ama ne varki zaman da kayma yapmasını bir türlü engelleyemedim. klasik 1/64 ve 61 kesmede sayaç artırdığımda dahi sapmalar oldu. program alttadır yardımcı olabilirseniz sevinirim.

Pic16f648a
4Mhz harici kristal takılı.

16f648a Bas dosyası
@ __config _BODEN_OFF  & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _XT_OSC & _MCLRE_OFF & _CP_OFF
Define OSC 4
'///////////////////////////////////////////////////////////////////////////////   
'/////////////////////////////////////////////////////////////////////////////// 
OPTION_REG=%00000101 '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 giris çıkıs
'///////////////////////////////////////////////////////////////////////////////   
'///////////////////////////////////////////////////////////////////////////////     

Tm1637 Bas Dosyası
    'Kullanım Şekli
    
    'DIO VAR PORTA.3
    'CLK VAR PORTA.2 
    'include "Tm1637.bas"
    'TmOn    = 1   'diplay aktif
    'TmLight = 1   'display parlaklık   1den 7 ye kadar
    'TmDp    = 0   '2 nokta üstüste 
    
    'TmKmt ekrana yazılacak olan değerleri düzenleme için kullanılacak;
    'TmKmt = 0      'sayısısal değerlerin ust sıfı olanların digitini karartır.  sayac = "0234" ekranda " 234" olarak görülür.
    'TmKmt = 1      'ekranı siler
    'TmKmt = 2      'ekranın orta figitleri yanar
    'TmKmt = 3      'gelen değer olduğu gibi ekrana basar aslında case sorgusunda herhangi bir işlem olmadan geçmesini sağlanıyor.
    'TmDeger = 1234 'ekrana basılacak olan sayısal değerler her biri desimalden karekter e dönüştürülmeli bunun için 
                    '(sayac dig 0) +48 şeklinde sayısal değerin her basamağı alınır ve 48 ile toplanır.
    'Gosub TM1637   've etikete gosub ile giderek ekrana değerler yüklenir.
    
     CHR1 var byte
     TmOn     var bit
     TmDuty  var byte
     TmDeger var byte [4]
     TmDp    var bit
     TmKmt   var byte
     TmSayac var word

     GOTO TmAtla
     
TM1637:                                             'This subrountine outputs all 4 characters to the LED
    GOSUB StartSeq                                  'SHIFTOUT Mode 0 works - LSB first, clock normally low
    SHIFTOUT DIO, CLK, 0, [$40]                    'This shifts out the 8-bit write command character $40
    GOSUB AckSeq
    GOSUB StopSeq
    
    GOSUB StartSeq
    SHIFTOUT DIO, CLK, 0, [$C0]                    'This shifts out the 8-bit command character $C0 that places
    GOSUB AckSeq                                    'the first character CHR1 in the leftmost digit location
                                                    'and then writes all 4 digits.
    Select Case TmKmt
    case 0 
    if TmSayac < 1000 then TmDeger[0] = 32
    if TmSayac < 100 then TmDeger[1] = 32
    if TmSayac < 10 then TmDeger[2] = 32

    case 1
    TmDeger[0] = 32:TmDeger[1] = 32:TmDeger[2] = 32:TmDeger[3] = 32
     
    case 2
    TmDeger[0] = 64:TmDeger[1] = 64:TmDeger[2] = 64:TmDeger[3] = 64
    
    case 3 'Tüm displaylerde değer görülür
    
    Case 4 
    
    
    end select
    
    CHR1 = TmDeger[3]   
    Gosub Disp 
    CHR1.7 = TmDp                                            
    SHIFTOUT DIO, CLK, 0, [CHR1]                    'SHIFTOUT CHR1-CHR4 to fill the display left to right
    GOSUB AckSeq                                    'TM1637 auto-increment mode
    CHR1 = TmDeger [2]  
    Gosub Disp  
    CHR1.7 = TmDp                                             
    SHIFTOUT DIO, CLK, 0, [CHR1]                    'SHIFTOUT CHR1-CHR4 to fill the display left to right
    GOSUB AckSeq                                    'TM1637 auto-increment mode
    CHR1 = TmDeger [1]  
    Gosub Disp  
    CHR1.7 = TmDp                                            
    SHIFTOUT DIO, CLK, 0, [CHR1]                    'SHIFTOUT CHR1-CHR4 to fill the display left to right
    GOSUB AckSeq                                    'TM1637 auto-increment mode
    CHR1 = TmDeger [0]  
    Gosub Disp  
    CHR1.7 = TmDp                                            
    SHIFTOUT DIO, CLK, 0, [CHR1]                    'SHIFTOUT CHR1-CHR4 to fill the display left to right
    GOSUB AckSeq                                    'TM1637 auto-increment mode
    GOSUB StopSeq
    CHR1 = 128 + (TmOn*8) + TmDuty
    GOSUB StartSeq
    SHIFTOUT DIO, CLK, 0, [CHR1]            'The output sequence ends with CMD3 which turns on the display
    GOSUB AckSeq                                    'and set the brightness duty cyle
    GOSUB StopSeq
    TmKmt = 0
Return

'****************************************Subroutine StartSeq******************************************************* 
 StartSeq:                                         
   HIGH CLK                                        
   HIGH DIO                                        'The Start sequence takes DIO low and then CLK low 5 uSec later
   'PAUSEUS 5                                       
   LOW DIO
   Low CLK
 Return     

'**************************************Subroutine AckSeq*********************************************************    
 AckSeq:
    LOW CLK 
    High DIO
    High CLK    
    input DIO                                    'The ACK waits for the TM1637 to assert low on DIO
    'PAUSEUS 100                                       'and then replies with a 5 uSec ACK pulse on CLK
    'while DIO = 1
    'ClearWDt
    'wend
    output DIO
    LOW DIO     
 Return

'****************************************Subroutine StopSeq****************************************************** 
 StopSeq:   
    LOW CLK                                        'The stop sequence takes CLK high and then DIO high 5 uSec later
    LOW DIO
    'PAUSEUS 5
    HIGH CLK
    HIGH DIO
 Return
 '///////////////////////////////////////////////////////////////////////////////
'///////////////////////////////////////////////////////////////////////////////
Disp:
if CHR1 >47 and CHR1 < 59 then 
CHR1 = CHR1 - 48 
lookup CHR1,[$3f,$30,$5b,$f9,$74,$6d,$6f,$38,$7f,$7d,$77,$7c,$39,$5e,$79,$71],CHR1';//0~9,A,b,C,d,E,F],CHR1       Panel ters
'lookup CHR1,[$3f,$06,$5b,$4f,$66,$6d,$7d,$07,$7f,$6f,$77,$7c,$39,$5e,$79,$71],CHR1';//0~9,A,b,C,d,E,F],CHR1      Panel doğru
Else
if CHR1 = 32 or CHR1 = 43 then CHR1 = 0   'Boşluk
if CHR1 = 45 then CHR1 = 64  'Eksi işareti
if CHR1 = "Y" then CHR1 = 110  
if CHR1 = "U" then CHR1 = 62 
if CHR1 = "A" then CHR1 = 119  
if CHR1 = "S" then CHR1 = 109 
 


Endif
'lookup CHR1,[$3f,$06,$5b,$4f,$66,$6d,$7d,$07,$7f,$6f,$77,$7c,$39,$5e,$79,$71],CHR1';//0~9,A,b,C,d,E,F],CHR1
return

'///////////////////////////////////////////////////////////////////////////////     
'///////////////////////////////////////////////////////////////////////////////

TmAtla:

Ana Program
    include "16f648.bas"  
        
    DIO VAR PORTA.3
    CLK VAR PORTA.2 
    include "Tm1637.bas"
    TmOn    = 1
    TmDuty  = 1   'display parlaklık
    TmDp    = 0   '2 nokta üstüste
    

    symbol Ld       = porta.4    'LOW Ld = led yanar
    symbol Bz       = porta.0    'LOW Bz = Buzzer çalar
    Symbol Tr       = Portb.3    'High Tr= Transistör çalışır.
        
    Bt1  var Portb.0
    Bt2  var Portb.6
    Bt3  var Portb.7

ON INTERRUPT GoTo KESME  'kesme oluşursa KESME adlı etikete git.


    Trisa            = 0
    TRISB            = %11110101
    Porta            = 0
    Portb            = 255
    
    Timer      var word
    Cycle      var bit
    CycleYarim var bit
    CycleCyrek var bit
    
    TermSaat var byte
    Gosterge var byte
    Aktar    var word
    
    EskiCycle  var bit
    EskiDp     var bit
    
    f          var byte
    f1         var byte
    Saniye     VAR BYTE
    Dakika     var byte
    Saat       var byte
    ZmnSay     var bit
    
    TmSayac = 0
    high Ld:low Tr:High Bz
'///////////////////////////////////////////////////////////////////////////////
Basla:
    ZmnSay = 0
    Saat = 0:Dakika = 0:Saniye = 0
    TmDp = 0
    TmKmt = 2:gosub Tm1637
    while bt1=1:wend
    pulsout bz,300
    while Bt1 = 0:wend
    
    Timer = 0
    ZmnSay = 1
    while Bt1 = 1 
    Aktar = (Dakika * 100) + Saniye:Gosub AktarDgr:TmKmt = 3:gosub Tm1637
    while Bt1 = 0 :wend      'or Bt2 = 0 or Bt3 = 0 
    while EskiCycle = Cycle and Bt1 = 1 :wend          'and Bt2 = 1 and Bt3 = 1
    EskiCycle = Cycle
    if Saniye > 59 then Saniye = 0: Dakika = Dakika + 1
    if Dakika > 59 then Dakika = 0: Saat = Saat + 1
    if Saat > 23 then Saat = 0
    'if Bt2 = 0 then ZmnSay = ZmnSay + 1:Timer = 0:pulsout bz,300
    'if Bt3 = 0 then Saat = 0:Dakika = 0:Saniye = 0:pulsout bz,300
    
    if ZmnSay = 1 then
    TmDp = TmDp +1
    else
    TmDp = 1
    endif
    wend
    while Bt1 = 0:wend
Goto basla
'///////////////////////////////////////////////////////////////////////////////
AktarDgr:
high tr
    for f = 0 to 3
    TmDeger[f] = (Aktar dig (3-f)) + 48
    next f
low tr
return
'///////////////////////////////////////////////////////////////////////////////
DISABLE
KESME:
    if ZmnSay = 1 then Timer = Timer + 1
    if Timer >49 then
       CycleYarim = 0
    else
       CycleYarim = 1
    endif
    
    if Timer <24 or ( Timer >49 and Timer < 74) then
       CycleCyrek = 0
    else
       CycleCyrek = 1
    endif
    
    if Timer = 61 then Saniye = Saniye + 1:Toggle Cycle:Timer = 0:toggle ld
    tmr0 = 0
    INTCON.2=0 
RESUME
ENABLE

END'///////////////////////////////////////////////////////////////////////////////

ete

Yaptığın ayarlamaya göre her 16,391ms süre içinde kesme oluşturuyorsun. Komut gecikmelerini bir kenara bırakırsak bu 61x16,391= 999,851 ms lık bir saat pulsine denk geliyor. Sana lazım olan 1000 ms ve aradaki fark sürekli zaman kayması olarak karşına çıkıyor.
Şayet bu kayma hep saatin geri kalması şeklinde ortaya çıkıyor ise kesme sayma işlemini şöyle yapabilirsin.
61 kesmede bir saat pulsi al bir sonrakini 62 kesmede bir al bir bit değişkeini toggle çalıştırarak bunu yapabilirsin.  Bu akılda işe yaramaz ise TMR1 sayacından her 100ms de bir kesme al. Hesaba göre 100.001 us de bir kesme yaratabiliyor. Bunda da 1us lik bir sapma var ama daha iyi bir zaman takibi yapabilir denemek lazım.
Unutma hiç bir kristal osilatör mükemmel değil maalesef. Sabit bir oda sıcaklığında doğru çalışan osilatör sıcaklığın 1 derece artması yada eksilmesi sonucu hemen fark oluşturmaya başlıyor. Bazen şans eseri günlük sıcaklık değişimleri nedeni ile bir ileri bir geri giden osilatör gün sonunda stabil gözükebiliyor. Çare, TCO yani sıcaklık kompanizasyonu yapılmış osilatör kullanmak.Onun yerine DS3231 kullanmak daha mantıklı geliyor her zaman. Çünki içindeki osilatör TCO tipi.

Ete


aRci

Merhaba;
Kristal hakkonda bende şüphelendim ve daha stabil olabilir diye artık dahili kristale geçtim. hesaplamada sabit zaman kesmesi varsayarak aradaki farkı ise kesme sayısında belirli bir noktada tmr0 a normalde kullanılan ön yükleme değerinden farklı bir değer vererek oluşan farkı her saniyede azaltmaya yönelik bir test ediyorum.

şu an için bir test de 4:30:00 gibisürede 18sn geridelik oluştu.
1/64 bölme oranına ön değer olarak 185 verdim ve kesme sayacımı 200 olarak sınırladım.
pic multicalc a göre 4,551 ms de bir kesme olmalı. bu şekilde çalışmada kısa testte gerideliği gördüm ve birde kesme sayacı olam timer değişkeni 199 a ulaşınca tmr0 değerini 75 e kurdum daha sonra teste bıraktığım da 4.30 saate 18sn geridelik yapmış şimdi değeri değiştirip gene test edeceğim yavaşca ama test ederek değeri tutturmaya çalışacağım.
suan testi 77 olarak değiştirerek devam ediyorum.

DISABLE
KESME:
    if ZmnSay = 1 then Timer = Timer + 1
    if Timer >99 then
       CycleYarim = 0
    else
       CycleYarim = 1
    endif
    
    if Timer <49 or ( Timer >99 and Timer < 149) then
       CycleCyrek = 0
    else
       CycleCyrek = 1
    endif
    tmr0 = 184
    if Timer = 199 then tmr0 = 77'75
    if Timer = 200 then Saniye = Saniye + 1:Toggle Cycle:Timer = 0:toggle ld
    
    if Saniye > 59 then Saniye = 0: Dakika = Dakika + 1
    if Dakika > 59 then Dakika = 0: Saat = Saat + 1
    if Saat > 23 then Saat = 0
    INTCON.2=0 
RESUME
ENABLE

Hattuşa

8 bitlik sayıcı yerine 16 bitlik sayıcı yani timer1 kullansanız hata oranınız ortadan kalkar, neden timer1 kullanmıyorsunuz?

ete

Nasıl bir konfigürasyonla hata oranı ortadan kalkacak onuda açıklasaydınız söylediklerinizi dikkate almak daha kolaylaşacak. Bekliyoruz.

Ete

aRci

Alıntı yapılan: Hattuşa - 06 Mart 2022, 10:06:338 bitlik sayıcı yerine 16 bitlik sayıcı yani timer1 kullansanız hata oranınız ortadan kalkar, neden timer1 kullanmıyorsunuz?

timer1 ide denedim aslında timer 1in en buyuk avantajı 1/1 oranınan sahip ben ise ana programa zaman ayırmak istiyorum çok fazla kesmeden sakınmak istiyorum yanlış yorumluyorda olabilirim. daha nasıl kullanılacağına hakim olmadığım için şimdilik böyle test edeceğim.

mehmet

TMR0 = 6
oran = 1/4
4MHz için 1ms lik kesme oluşur.
Daha kolay olmaz mı?

http://eng-serve.com/pic/pic_timer.html
Olan olmuştur,
olacak olan da olmuştur.
Olacak bir şey yoktur.
http://www.mehmetbilgi.net.tr
CC BY

aRci

Alıntı yapılan: mehmet - 06 Mart 2022, 13:47:15TMR0 = 6
oran = 1/4
4MHz için 1ms lik kesme oluşur.
Daha kolay olmaz mı?

http://eng-serve.com/pic/pic_timer.html
merhaba;
Öncelikle site için teşekkürler.
Galiba kristalde bir problem var artık bunu düşünüyorum. hem dahili hem harici kristaldede farklı sonuç aldım.
1/4 denedim şimdi burada 1ms de kesme geldiği için Timer değişkeni 1000 e ayarladım neredeyse 1.2 sn civarı sayma yapıyor. denemelere devam ediyor hesapla çözebileceğimi sanmıyorum

Hattuşa

Alıntı yapılan: ete - 06 Mart 2022, 11:12:09Nasıl bir konfigürasyonla hata oranı ortadan kalkacak onuda açıklasaydınız söylediklerinizi dikkate almak daha kolaylaşacak. Bekliyoruz.

Ete

hocam saat için sn saydırmıyoruz mu?
16 bit timer ile 1000mS 16 bitte 1/16 prascelerde 0,001% hata oranı Tmr1 =3036
aynı şekilde 50 mS ise 1/1 prascelerde 0.0% hata oranı Tmr1 =15543

demek istediğim hata oranları 16 bitte daha düşük çıkıyor bu sayede de zamanda sapma miktarı daha da azalıyor.
yanlışmıyım?

ete

Azalma başka ortadan kalkma daha başka şeyler buna dikkatini çekmek için öyle söyledim.
Sözler yanlış anlaşılmaya sebep olabiliyor. Özellikle bilmeyen kişi 16 bit Timer ile hatasız saat pulsleri elde edilebilir şeklinde bir anlam çıkartabilir o sözlerden ama gelen yeni açıklama olaya açıklık getirmiş oldu.

Yinede bir komut gecikmesi olasılığını dikkate almadığını düşünüyorum. Gerçekte o kadar az hata ile bu işler dönmüyor maalesef. Ayrıca Timer1 sayacıda işlemci osilatöründen puls alacak ve işlemciyi çalıştıran ana osilatör sıcak soğuk farklarında farklı frekanslarda çalışacaktır. Bunlar zaman içinde epeyce bir fark oluşturabiliyor.
Bir simulasyon sisteminde bu işi denerseniz hesapladığınız az hata oranlarını yakalayabilirsiniz. Ama gerçekte işler biraz farklı oluyor. Bunu yüzlerce kez denemiş birisi olarak konuşuyorum.

Ete

aRci

Merhaba;
1/4 ve 11 ön değer ile 756 kesme sonrası daha stabil bir sonuc aldım yaklaşık 2 saattir kesintisiz sayıyor. ve ilk başladığı andaki fark ile devam ediyor. bu kısmı açıklamak gerekirse telefonun kronometresini açarak test ediyorum.

sayacın bir baslat dümesi var bu dümeye basık tutuyorum sonra telefon kronometre sini başlat dumesine basıyorum ve aynı anda elimi çektiğimde az miktar hata ile start alıyorlar bu şekilde 1.5 günde sonuc anca aldım :(

aRci

Merhaba;
dün denemeler sonucunda 1/4 tmr0= 18 ve 741 kesme sayacı ile 12 saatlik test de saniyenin 1/3 kadar geri kalma yaptı. alttaki fotoda her kesmeye gittiğinde tr den pals gönderiyorum her geldiğinde plas sinyali değişiyor. sonraki kesmeye kadarda o sekilde kalıyor. osiloskop basit bir osiloskop kendim montajlamıştım ama kullanımını bilmiyorum ekransa yazan değerlere göre kesmeler arası farkı hesaplayabilirmiyiz.



ete

Skop ekranı çok net gözükmüyor. Yanlış görmüyor ise süre 0,2ms olarak görünüyor. Bu durumda ekrandaki her kare genişliği 0,2 ms ye eşdeğerdir. Kareleri sayarak süreleri hesaplayabilirsin. Daha iyi bir hesap için ekranda tam bir puls görülmeli daha doğrusu iki yükselen kenarıda görmek lazım. İki yükselen kenar arasındaki mesafe kesme süresini vermesi lazım.

Ete

aRci

Merhaba;
ekranda0,2ms yazmaktadır ekranda görülen aslında 2 kesme arasını göstermekte ben kesme oluştuğunda toggle Tr diyerek transistör çıkışının konumunu her kesmede değiştiriyorum bu sayede kesme programında duruş yaşanmadan bu anın görüntüsünü aldım buna göre 2 kesme arası süre 1.4ms oluyor ama resimde eğer dikkat ederseniz son karede neredeyse 4/5 olan son kısımda sinyal düşüyor.

Buna göre dikkate akarak geçen süreyi 1,36 ms ve bu süreden 742 adet daha olduğunda 742x1,36 = 1009,12ms zaman geçiyor. Bu değer donucunda ben yaklaşık 12 saatlik testimde saatimde geridelik olduğunu gözlemledim bu pozitif olan fazla değer zamanla eksi yöne dönmüş.

Bu çalışmada birde şunu gördüm. timer kesmesinde kesme rutinin hazricinde programa yapılan her kod zamanlamayı etkiliyor. belki de ben hatalı olabilirim. ancak test sırasında gerek görmediğim buton kontrollerini test sona erdiğinde eklediğimde bir anda geridelik oluştu ve tekrar değerlerle oynayarak kalibre etmeye çalıştım.

programa dahil ettiğim butun kontrolleri alttadır. komutlar daki pulsout tı dikkate almayın buton kontrolleri zaman sayıcını sıfırlama yada durdurma için bu sebeple timer durumu önemli değil ama butona basmadığım halde zaman kayması oluşmaması gerektiğini düşünüyorum.

    
'///////////////////////////////////////////////////////////////////////////////
Basla:
    ZmnSay = 0
    Saat = 0:Dakika = 0:Saniye = 0
    TmDp = 0
    TmKmt = 2:gosub Tm1637
    while Bt1 = 0:wend
    
    while bt1=1:wend
    pulsout bz,300
    while Bt1 = 0:wend
    
    Timer = 0
    ZmnSay = 1
    while Bt1 = 1 
    Aktar = (Dakika * 100) + Saniye:Gosub AktarDgr:TmKmt = 3:gosub Tm1637
    while Bt1 = 0 or Bt2 = 0 or Bt3 = 0 :wend      '
    while EskiCycle = CycleYarim and Bt1 = 1 and Bt2 = 1 and Bt3 = 1:wend          '
    EskiCycle = CycleYarim
    if Bt2 = 0 then ZmnSay = ZmnSay + 1:Timer = 0:pulsout bz,300
    if Bt3 = 0 then Saat = 0:Dakika = 0:Saniye = 0:pulsout bz,300
    
    if ZmnSay = 1 then
    TmDp = TmDp +1
    else
    TmDp = 1
    endif
    wend
Goto basla
'///////////////////////////////////////////////////////////////////////////////
AktarDgr:
    for f = 0 to 3
    TmDeger[f] = (Aktar dig (3-f)) + 48
    next f
return
'///////////////////////////////////////////////////////////////////////////////
DISABLE
KESME:
    if ZmnSay = 1 then Timer = Timer + 1
    if Timer >400 then
       CycleYarim = 0
    else
       CycleYarim = 1
    endif
    if Timer <200 or ( Timer >400 and Timer < 600) then
       CycleCyrek = 0
    else
       CycleCyrek = 1
    endif
                 '741      
    if Timer >= 741 then   '741 12 saat az geri kalmaya başladı
    Saniye = Saniye + 1:Toggle Cycle:Timer = 0:toggle Tr
    if Saniye > 59 then Saniye = 0: Dakika = Dakika + 1
    if Dakika > 59 then Dakika = 0: Saat = Saat + 1
    if Saat > 23 then Saat = 0
    endif
    Tmr0 = 18     '18 12 saat sonra 741 kesme sayısı ile az geri kalmaya başladı.
    INTCON.2=0 
RESUME
ENABLE

ete

Özellikle dikkatimi çeken şey kesme alt programında bir sürü kod olduğu.
Kesme oluşuyor ve bu kodları işledikten sonra TMR0 yeniden yükleme değerini alıp kesmeden çıkılıyor. Bu kodlarıda kesme süresine eklemek için Tmr0 önyükleme değerini kesme içine girergirmez verin bakalım ne değişecek.
Elbette çok kısa kesme süresi de ana program kısmını etkiler. Bu işi daha az kesme ve uzun TMR0 süresi ile halletseniz daha iyi olurdu bence.

Ete

Powered by EzPortal