logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Zliczanie sekund i zegar na LCD?

adamkoc 10 Lis 2009 20:03 1746 12
REKLAMA
  • #1 7242155
    adamkoc
    Poziom 10  
    Witam serdecznie.
    Mikrokontrolerami i w ogóle programowaniem bawię się od niedawna, dlatego też zamieszczony przeze mnie program na pewno nie wygląda zbyt estetycznie i na pewno też nie wykorzystuje pamięci w ekonomiczny sposób, ale nie chodzi o sam program, lecz o sposób naliczania sekund. Dlaczego w programie (poniżej) jest tak, że nie każda sekunda jest sobie równa? Tzn. dlaczego jeśli zegar wskazuje godziny z przedziału mniej więcej 22:00-00:00 zegar idzie mniej więcej idealnie, a w przedziale 00:00 - 06:00 przyspiesza się o około 10 sekund (sekundy w godzinach porannych są najkrótsze, w godzinach południowych trochę dłuższe, w popołudniowych jeszcze dłuższe a w nocnych przed północą najdłuższe)???
    Wydawało mi się, że przerwania z wykorzystaniem timer1 działają w ten sposób, że po przerwaniu wykonywane są procedury "po przerwaniu" u mnie "czas", w tym czasie timer1 wykorzystywany jako licznik już zlicza przeskalowane (1024-krotnie) uderzenia kwarcu , a następnie liczy do 7909 (to miało się równać 1 sekundzie) i że obojętnie ile czasu zajmuje wykonanie procedury "czas" (aby poniżej 1 sekundy), to zawsze przerwania będą występowały co taki sam okres czasu (u mnie 1 sekunda).
    Inaczej ujmując, jeśli procedura "czas" zajmie 1000 licznika, to ten policzy jeszcze 6909 i nastąpi przerwanie, jeśli procedura "czas" zajmie 1500 licznika, to ten policzy jeszcze 6409 i nastąpi przerwanie, tak czy tak przerwanie powinno być chyba co 7909 licznika. Dlaczego więc u mnie nie wygląda to tak w praktyce?

    Program:
    
    $regfile = "m32def.dat"
    $crystal = 8000000
    $baud = 19200
    $hwstack = 32
    $swstack = 10
    $framesize = 40
    
    
    
    'DEFINICJA ZNAKÓW (Ś, Ć, Ę, Ł, "stopień")
    Deflcdchar 4 , 32 , 32 , 14 , 17 , 31 , 16 , 14 , 2
    Deflcdchar 3 , 12 , 4 , 6 , 4 , 12 , 4 , 14 , 32
    Deflcdchar 2 , 2 , 4 , 14 , 16 , 16 , 17 , 14 , 32
    Deflcdchar 1 , 2 , 4 , 14 , 16 , 14 , 1 , 30 , 32
    Deflcdchar 5 , 4 , 10 , 4 , 32 , 32 , 32 , 32 , 32
    
    
    'KONFIGURACJA ADC ORAZ LCD
    Config Adc = Single , Prescaler = Auto , Reference = Internal
    Config Lcdpin = Pin , Db4 = Portb.1 , Db5 = Portb.2 , Db6 = Portb.3 , Db7 = Portb.4 , E = Portb.5 , Rs = Portb.6
    Config Lcd = 20 * 4
    Cls
    Cursor Off Noblink
    'WŁĄCZENIE ZASILANIA PRZETWORNIKA ADC
    Start Adc
    
    
    'KONFIGURACJA PORTÓW JAKO WEJŚCIA I PODCIĄGNIĘCIE DO LOGICZNYCH JEDYNEK
    Ddrc = 0
    Ddrd = 0
    Portc = 255
    Portd = 255
    
    
    'ZMIENNE CZASU
    Dim Sekunda As Byte
    Dim Minuta As Byte
    Dim Godzina As Byte
    Dim Sekunda_1 As String * 2
    Dim Minuta_1 As String * 2
    Dim Godzina_1 As String * 2
    
    Dim Dzien As Byte
    Dim Miesiac As Byte
    Dim Rok As Word
    Dim Dzien_1 As String * 2
    Dim Miesiac_1 As String * 2
    Dim Rok_1 As String * 4
    Dim Przestepnosc As Byte
    Dim Dzien_tyg As Byte
    Dim Dzien_tyg_1 As String * 4
    
    'ZADANIE POCZATKOWYCH WARTOSCI DATY I CZASU
    
    Sekunda = 0
    Minuta = 0
    Godzina = 0
    Dzien = 1
    Miesiac = 1
    Rok = 2009
    Dzien_tyg = 7
    
    'ZMIENNE POMIARU TEMPERATURY I CIŚNIENIA
    Dim U As Integer
    Dim Temp As Single
    Dim Temp_1 As Long
    Dim Temp_2 As String * 5
    Dim C As Integer
    
    Dim W As Integer
    Dim Cisnienie As Long
    Dim V As Integer
    Dim Cisnienie_1 As Single
    Dim Cisnienie_2 As String * 6
    
    'KONFIGURACJA TIMERA
    Config Timer1 = Timer , Prescale = 1024
    On Ovf1 Czas
    Load Timer1 , 7909
    Enable Timer1
    Enable Interrupts
    
    
    'FORMATOWANIE ZMIENNYCH ŻEBY ZAWSZE MIAŁY TĘ SAMĄ DŁUGOŚĆ NA WYŚWIETLACZU
      Sekunda_1 = Str(sekunda)
      Minuta_1 = Str(minuta)
      Godzina_1 = Str(godzina)
      Sekunda_1 = Format(sekunda_1 , "00")
      Minuta_1 = Format(minuta_1 , "00")
      Godzina_1 = Format(godzina_1 , "00")
    
      Dzien_1 = Str(dzien)
      Miesiac_1 = Str(miesiac)
      Rok_1 = Str(rok)
      Dzien_1 = Format(dzien_1 , "00")
      Miesiac_1 = Format(miesiac_1 , "00")
      Rok_1 = Format(rok_1 , "0000")
      If Dzien_tyg = 1 Then
        Dzien_tyg_1 = " pon"
      Elseif Dzien_tyg = 2 Then
        Dzien_tyg_1 = " wto"
      Elseif Dzien_tyg = 3 Then
        Dzien_tyg_1 = " sro"
      Elseif Dzien_tyg = 4 Then
        Dzien_tyg_1 = " czw"
      Elseif Dzien_tyg = 5 Then
        Dzien_tyg_1 = " pią"
      Elseif Dzien_tyg = 6 Then
        Dzien_tyg_1 = " sob"
      Elseif Dzien_tyg = 7 Then
        Dzien_tyg_1 = " nie"
      End If
    
    
    'NIESKOŃCZONA PĘTLA PRZERYWANA TYLKO PO UPŁYWIE 1 SEKUNDY
    
    Do
    Loop
    'INSTRUKCJE, KTÓRE MAJĄ BYĆ WYKONANE PO UPŁYWIE 1 SEKUNDY
    Czas:
      Load Timer1 , 7909
    
      ' PRZELICZENIE CZASU PO UPŁYWIE 1 SEKUNDY
    
      End If
      Incr Sekunda
      If Sekunda >= 60 Then
        Sekunda = 0
        Incr Minuta
      End If
      If Minuta >= 60 Then
        Minuta = 0
        Incr Godzina
      End If
      If Godzina >= 24 Then
        Godzina = 0
        Incr Dzien
        Incr Dzien_tyg
      End If
      If Dzien_tyg > 7 Then
        Dzien_tyg = 1
      End If
    
    
    
      If Miesiac = 1 Or Miesiac = 3 Or Miesiac = 5 Or Miesiac = 7 Or Miesiac = 8 Or Miesiac = 10 Or Miesiac = 12 Then
        If Dzien > 31 Then
          Dzien = 1
          Incr Miesiac
        End If
      Elseif Miesiac = 4 Or Miesiac = 6 Or Miesiac = 9 Or Miesiac = 11 Then
        If Dzien > 30 Then
          Dzien = 1
          Incr Miesiac
        End If
      End If
      If Rok = 2012 Or Rok = 2016 Or Rok = 2020 Or Rok = 2024 Then
        If Miesiac = 2 Then
          If Dzien > 29 Then
            Dzien = 1
            Incr Miesiac
          End If
        End If
      Elseif Miesiac = 2 Then
        If Dzien > 28 Then
          Dzien = 1
          Incr Miesiac
        End If
      End If
      If Miesiac > 12 Then
        Miesiac = 1
        Incr Rok
      End If
    
      Sekunda_1 = Str(sekunda)
      Minuta_1 = Str(minuta)
      Godzina_1 = Str(godzina)
      Sekunda_1 = Format(sekunda_1 , "00")
      Minuta_1 = Format(minuta_1 , "00")
      Godzina_1 = Format(godzina_1 , "00")
    
      Dzien_1 = Str(dzien)
      Miesiac_1 = Str(miesiac)
      Rok_1 = Str(rok)
      Dzien_1 = Format(dzien_1 , "00")
      Miesiac_1 = Format(miesiac_1 , "00")
      Rok_1 = Format(rok_1 , "0000")
    
      If Dzien_tyg = 1 Then
        Dzien_tyg_1 = " pon"
      Elseif Dzien_tyg = 2 Then
        Dzien_tyg_1 = " wto"
      Elseif Dzien_tyg = 3 Then
        Dzien_tyg_1 = " śro"
      Elseif Dzien_tyg = 4 Then
        Dzien_tyg_1 = " czw"
      Elseif Dzien_tyg = 5 Then
        Dzien_tyg_1 = " pią"
      Elseif Dzien_tyg = 6 Then
        Dzien_tyg_1 = " sob"
      Elseif Dzien_tyg = 7 Then
        Dzien_tyg_1 = " nie"
      End If
    
      'DOKONANIE POMIARU CISNIENIA I TEMPERATURY
      Temp_1 = 0
      For C = 1 To 1000
      U = Getadc(3)
      U = Getadc(3)
      Temp_1 = Temp_1 + U
      Next
      Temp = Temp_1 / 2803
      Temp = Temp
      Temp_2 = Fusing(temp , "#.#" )
      Temp_2 = Format(temp_2 , "  000")
    
      Cisnienie = 0
      For V = 1 To 1000
      W = Getadc(1)
      W = Getadc(1)
      Cisnienie = Cisnienie + W
      Next
      Cisnienie_1 = Cisnienie \ 1500
      Cisnienie_1 = Cisnienie_1 + 929
      Cisnienie_2 = Fusing(cisnienie_1 , "#.#")
      Cisnienie_2 = Format(cisnienie_2 , "      ")
    
    
    
    
      'WYŚWIETLENIE NOWEGO CZASU I WARTOŚCI CISNIENIA I TEMPWRATURY NA WYŚWIETLACZU LCD
      Locate 1 , 1
      Lcd "czas: " ; Godzina_1 ; ":" ; Minuta_1 ; ":" ; Sekunda_1
      Locate 2 , 1
      Lcd "data: " ; Rok_1 ; "-" ; Miesiac_1 ; "-" ; Dzien_1 ; Dzien_tyg_1
    
      Locate 3 , 1
      Lcd "Ci" ; Chr(1) ; "nienie:" ; Cisnienie_2 ; " hPa"
      Locate 4 , 1
      Lcd "Temperatura: " ; Temp_2 ; Chr(5) ; "C"
    
    
    
      Return
    
    End
    


    Serdecznie dziękuję i pozdrawiam
  • REKLAMA
  • #2 7248579
    pgnige
    Poziom 14  
    Spróbuj zamiast
    Load Timer1 , 7909
    zastosować
    Timer1 = 7909

    Procedura load nie jest dokładdna i może powodować taki błąd w odliczaniu czasu.
  • #3 7248824
    Konto nie istnieje
    Poziom 1  
  • REKLAMA
  • #4 7249388
    adamkoc
    Poziom 10  
    Dzięki. Dziś sprawdzę jak działa to co podpowiedział pgnige.
    Co do podpowiedzi emarcusa, to nie widzę za bardzo różnicy. U mnie preskaler jest 1024, czyli cztery razy większy niż w tej podpowiedzi, a "załadowanie" licznika w przybliżeniu cztery razy mniejsze, więc i tak powinno wyjść to samo. Ale nie do końca o to mi chodziło, bo przecież obojętnie czy ustawię przerwanie co 1 sekundę, czy w wyniku błędu w moich obliczeniach co np. 0,99 sekundy, to okres co ile występuje przerwanie powinien być stały (byleby czas wykonania procedury przerwania był krótszy niż okres co ile wychodzi przerwania), a u mnie okres ten się zmienia. Dlaczego?
  • REKLAMA
  • #5 7249642
    landy13
    Poziom 31  
    pgnige napisał:
    Spróbuj zamiast
    Load Timer1 , 7909
    zastosować
    Timer1 = 7909
    Kolega pgnige znów nie doczytał. Jeżeli zamiast instrukcji Load stosuje się bezpośredni wpis do licznika trzeba odjąć parametr instrukcji Load od pojemności licznika. Dla licznika 16-bitowego będzie to 65536-7909=57627.
    Czyli zamiast
    Load Timer1 , 7909
    napisz
    Timer1=Timer1+57627
  • #6 7249990
    adamkoc
    Poziom 10  
    Sam prze metodę prób i błędów, a dokładniej po dwóch próbach zorientowałem się, że trzeba tak zrobić jak podał landy 13. Teraz pozostało mi tylko odczekać dobę i kontrolować co jakiś czas, czy opóźnienie/przyspieszenie zegara ma stałą wartość w ciągu doby. Jeśli ma wartość stałą, to wystarczy tylko zmienić parametr w "timer1 = x". A jeśli opóźnienie/przyspieszenie będzie się zmieniało, to nie wiem o co chodzi.
  • #7 7250521
    Konto nie istnieje
    Poziom 1  
  • REKLAMA
  • #8 7250700
    adamkoc
    Poziom 10  
    emarcus...
    Program wyrzuca masę błędów, ponieważ wklejając go do postu część skasowałem. Ale jak już pisałem, nie chodzi mi o sam program, lecz o to, że sekunda nie jest równa sekundzie. Wiem, że teoretycznie wartość 7909 daje więcej niż sekundę, gdyż powinno być dokładnie 7812,5 ale przy tej wartości zegar znacząco się spieszył, na LCD wszystko było wyświetlane dobrze, tylko za szybko o parę-kilkanaście sekund na godzinę.
    Może faktycznie problem jest w tym co opisałeś - ukryte przerwania i waitms, o których ja nie mam zielonego pojęcia. Jak zaznaczyłem w programowanie i w ogóle w mikroprocesory bawię się od niedawna i dlatego też nie czaję co to są stosy itd, dlatego też może być, że tu pies pogrzebany. Czy powinienem więc przenieść procedurę wyświetlania do głównego programu, czyli do pętli do... loop?
    Proszę, emarcus, wyjaśnij mi gdzie są te ukryte przerwania i waitms? Także o co chodzi z tym stosem? Czytam o avr, także helpa w Basic-avr, jest tam mowa o stosach, a le co to jest i jak to się ma w praktyce do pisania programu, ak wziąć to pod uwagę? - nie mam pojęcia. Pomoc mile widziana.
  • #9 7252266
    landy13
    Poziom 31  
    emarcus napisał:
    W tym momencie programu Timer1 = 0, wiec w rownaniu (po prawej stronie) wyrazenie "Timer1 +" jest bez znaczenia.
    W przypadku prostych programów o jednym tylko przerwaniu od Timera1 rzeczywiście jest to bez znaczenia. Natomiast gdy przerwania mogą "czekać w kolejce" wtedy gdy wykonuje się przerwanie Timer1 stan licznika może być większy od zera i wyrażenie "Timer+" niweluje to opóźnienie.
  • #10 7252690
    kacper_80
    Poziom 11  
    Przede wszystkim tak jak już napisał emarcus na początku, wyrzuć wszystko co niepotrzebne z obsługi przerwania zostaw tam samą inkrementacje sekundy a całą reszt wrzuć do głównej pętli, wtedy powinieneś osiągnąć 1s=const. W zasadzie to jest prawidłowy sposób wykorzystania przerwania.
    To obsługa tak "skomplikowanego" przerwania generuje Ci błąd.
  • #11 7254914
    adamkoc
    Poziom 10  
    Ok. Jeszcze dziś postaram się poprawić ten program. Ale mam jeszcze pytanie. Powiedzmy że mamy program główny w pętli do... loop. Gdy nastąpi przerwanie np w 10 linii w pętli, zostanie wykonana procedura przerwania. Czy następnie wykonywana jest pętla od początku, czy od momentu, od linii w której nastąpiło przerwanie? Czy jest możliwość programowania, żeby powrót był do momentu przerwania i czy jest też możliwość programowania, żeby następował powrót do początku pętli?
  • #12 7255381
    kacper_80
    Poziom 11  
    Normalnie po opuszczeniu przerwania program zacznie Ci wykonywać linie nr.11. Jak się postarasz to możesz wrócić do początku pętli, tylko po co? przerwiesz w ten sposób w połowie jakąś procedurę np. obliczania daty.
  • #13 7257348
    Konto nie istnieje
    Poziom 1  
REKLAMA