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

dokładne wyznaczanie czasu atmega8

leo1 23 Gru 2009 11:16 1565 11
  • #1 7426058
    leo1
    Poziom 14  
    Witam.
    Od jakiegoś czasu staram się w celach "samoedukacyjnych" zrobić coś na wzór zegarka. Problem polega na tym, że nie moge odmierzyć dokładnie 1 sekundy. Starałem się rozwiązać problem na kilka sposoów. jaklepszy efekt dała obsługa przerywania, lecz błąd to w dalszym ciągu kilka sekund przez noc ;p Nie chcę stosować żadnych zewnętrzych zegarów. Poniżej mój kod.
    Co można poprawić, aby odmieżyć dokładnie 1 sek?

    #define F_CPU 4000000L
    #include <avr/io.h>
    #include <inttypes.h>
    #include <util/delay.h>
    #include <hd44780.c>
    #include <stdlib.h> // do liczby na wyswietlaczu
    #include <avr/interrupt.h>
    
    signed char sek,min,godz;
    char s[3],m[3],g[3],czas[10]; //maksymalna ilosc cyfr
    
    int main(void)
    {
    
    sek=0;
    min=14;
    godz=23;
    
    TCCR1B |= (1 << WGM12);
    OCR1A = 15625;
    TCCR1B |= (1 << CS12);
    TIMSK |= (1<<OCIE1A);
    sei();
    
    LCD_Initalize();
    LCD_Clear(); 
    LCD_WriteText("Z E G A R");
    
    
    
    while(1)
    { 
    if (sek==60) {sek=0;min++;}
    if (min==60) {min=0;godz++;}
    if (godz==24) {godz=0;}
    	
    	 
    itoa(sek,s,10);
    itoa(min,m,10);
    itoa(godz,g,10); 
    LCD_GoTo(1,1);
    
    LCD_WriteText(g);
    LCD_WriteText(":");
    LCD_GoTo(4,1);
    LCD_WriteText(m);
    LCD_WriteText(":");
    LCD_GoTo(7,1);
    LCD_WriteText(s);
    
    
    }
    
    }
    ISR(TIMER1_COMPA_vect)
    {{
    	sek++;
    	}
    
    }
    
  • #2 7426100
    michalko12
    Specjalista - Mikrokontrolery
    Jak korzystasz z wewnętrznego oscylatora to zapomnij o dokładnym pomiarze.
  • #4 7426124
    leo1
    Poziom 14  
    Wewnętrznego? A nie robie w oparciu o kwarc 4MHz, może jestem w błędzie ale wydawało mi sie ze to on odlicza cykle, które sa dzielone na 256 i zliczane do 15625 co w teorii powinno dać 1 sek.

    Freddie Chopin< co masz na myśli z tą optymalizacją?

    Proszę o poprawienie mnie jeśli coś robię nie tak.
  • Pomocny post
    #5 7426213
    BoskiDialer
    Poziom 34  
    Wewnętrzny zegar jest na tyle mało dokładny, że aby można było nim coś zmierzyć, należało by ustalić jego dokładną częstotliwość porównując z jakimś wzorcem, po czym zapisać poprawkę do osccal albo uwzględnić to w czasie przepełnienia t1 - a i tak dokładność nie będzie duża ze względu na wachania częstotliwości.

    Warto poczytać w dokumentacji jaką dokładnie wartość należy wpisać do OCR1A: wpisuje się wartość o 1 mniejszą, co wynika ze sposobu działania timera. Nie uwzględnienie tego będzie skutkować jakimś 5,5 sekundy błędu na dobę.

    Zwiększanie "sek" w przerwaniu a warunki w pętli głównej trochę ze sobą kolidują - teoretycznie zwiększenie zmiennej "sek" może nastąpić po sprawdzeniu warunków co umożliwi wyświetlenie czasu w postaci "12:00:60".

    No i to co Freddie Chopin zauważył, zmienna "sek" używana w przerwaniu powinna być oznaczona jako volatile, chociaż w tym kodzie nawet bez tego będzie działać, co wynika z wywołania funkcji zewnętrznych - kod musi bezwzględnie zapisać wartości z powrotem do pamięci (kompilator nie wie, czy tamte funkcje używają zmiennej "sek") i uznać kopie w rejestrach za niepoprawne co wymusi odczyt z pamięci.
  • #6 7426358
    leo1
    Poziom 14  
    BoskiDialer < bardzo dziękuje za wytłumaczenie zagadnienia. Naniose poprawki do kodu i zabieram się za sprawdzanie.
  • Pomocny post
    #7 7426544
    Paweł Es.
    VIP Zasłużony dla elektroda
    Dodatkowy wpływ na błąd ma niedokładność częstotliwości generatora kwarcowego. Błąd w sekundach na dobę, wyraża się wzorem:


    $$\Delta=86400*(1-\frac{fwzr}{f})$$

    $$\Delta$$ - błąd w sekundach na dobę (wartość ujemna oznacza spóźnianie się)

    fwzr - częstotliwość wzorcowa (czyli to co ma być np. 4000000 Hz)

    f - częstotliwość rzeczywista kwarcu lub generatora kwarcowego

    Z powyższego wzoru wynika, że już odstrojenie generatora kwarcowego o +/- 46 Hz (dla 4MHz) daje błąd dobowy +/- 1s

    Jeżeli stosujemy zwykły kwarc to dobrze jest dodać do niego trymerek pozwalający na korekcję częstotliwości kwarcu dla zminimalizowania błędu.


    Zwykle całą inkrementację czasu (sekundy, minuty, godziny) wykonuje się w przerwaniu oraz można dodatkowo ustawiać sygnał dla programu głównego, że nastąpiła zmiana wskazania i trzeba uaktualnić wyświetlacz zamiast w kółko wysyłać dane do wyświetlacza mimo, że są takie same.

    Dobrym chwytem jest też sprawdzanie warunków typu większe/mniejsze zamiast równe: tj.

    if (sek>59) sek=0;


    Pozornie obie wersje działają tak samo ale ta druga jest odporna na przekłamania.
    Jeżeli jakieś zakłócenie zmieni stan jakiegoś bitu w komórce sekundy, to może się okazać, że wartość jej jest większa od 60 i test typu

    if (sek==60) sek=0 ;


    nie zadziała poprawnie i zegar pójdzie w maliny

    Poza tym dobrze jest zagnieździć sprawdzania warunków, bo nie wszystkie muszą być sprawdzane co 1 sekundę (

    ISR(TIMER1_COMPA_vect) 
    {
       sek++; 
       if (sek>59) 
        {
           sek=0;
           min++; 
           if (min>59)  // To if jest wykonywane raz na minutę a nie co sekundę 
            {
             min=0;
             godz++;
             if (godz > 23) godz=0; // To if jest wykonywane raz na godzinę a nie co sekundę
            }
         } 
     zmiana=1;  // Poinformowanie programu głównego, że trzeba odświeżyć wyświetlacz
       } 
    
     


    while(1) 
    { 
    
    if (zmiana=1)
    {    
      
    itoa(sek,s,10); 
    itoa(min,m,10); 
    itoa(godz,g,10); 
    LCD_GoTo(1,1); 
    
    LCD_WriteText(g); 
    LCD_WriteText(":"); 
    LCD_GoTo(4,1); 
    LCD_WriteText(m); 
    LCD_WriteText(":"); 
    LCD_GoTo(7,1); 
    LCD_WriteText(s); 
    
    zmiana=0 // Uzbrojenie znacznika
    }
    
    } 


    Pozwala to zwolnić procesor z masy zbędnej pracy oraz uzyskać czas na inne ewentualne działania(a i obniżyc pobór prądu).

    I konieczne jest poinformowanie kompilatora, że pewne zmienne mogą być zmieniane przez przerwania (jeżeli tego nie ma to przy optymalizacji kodu, kopilator może pozmieniać pewne interpretacje fragmentów kodu i program wynikowy będzie działał innaczej niż to by wynikało z kodu w C.

    volatile signed char sek,min,godz,zmiana; 
  • #8 7426633
    gothye
    Poziom 33  
    a jeszcze prościej ,ustawic atmege na kwracu wewnętrzym a pod piny tosc1/2 podłączyć kwarc 32.768khz skonfigurować timerze do pracy 2 w trybie asynchronicznym
  • #9 7426836
    asembler
    Poziom 32  
    No prosciej bo do tego miedzy innymi AVR jest przeznaczony ale i tak stwbilnosc nawet na kwarcu zgarkowym wyniesie kilka minut na rok (po kalibracji nawet)
  • #10 7427108
    leo1
    Poziom 14  
    Paweł Es. < Czy tak długa instrukcja w ISR(TIMER1_COMPA_vect) nie sprawi, że czas jej wykonania wydłuży się? Ciekawy pomysł z tą pętlą co minute.

    Cytat:
    Jeżeli stosujemy zwykły kwarc to dobrze jest dodać do niego trymerek pozwalający na korekcję częstotliwości kwarcu dla zminimalizowania błędu.

    nie słuszałem o takiej metodzie. Jak ją stosować? szeregowo, równolegle, może inaczej?
  • Pomocny post
    #11 7427121
    gothye
    Poziom 33  
    podłączasz trymer pomiędzy Xtal2 (in) procesora a masę
  • #12 7428510
    Paweł Es.
    VIP Zasłużony dla elektroda
    leo1 napisał:
    Paweł Es. < Czy tak długa instrukcja w ISR(TIMER1_COMPA_vect) nie sprawi, że czas jej wykonania wydłuży się? Ciekawy pomysł z tą pętlą co minute.


    Akurat to wydłużenie nic nie czyni złego, bo ten fragment programu jest wywoływany raz na sekundę czyli co czas wielokrotnie dłuższy niż czas wykonania nawet pełnej sekwencji rozkazów (o 23:59:59)
REKLAMA