Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[atmega8][C] Timer2 i opóźnienia w zliczaniu czasu

17 Sie 2008 21:22 3944 9
  • Poziom 18  
    Witam
    Kod jak poniżej służy do zliczania czasu trwania jednego impulsu (ta funkcja działa prawidłowo) oraz do zliczania czasu (w celu zliczania ilości impulsów na jednostkę czasu).

    Code:
    volatile unsigned char pomiar = 0;
    
    volatile uint32_t cw_impuls,impuls = 0;
    unsigned char sreg;
    float tc1;
    char czas[8];

    void Inicjalizacja(void)
    {
       LCD_init();                           // Inicjalizacja wyświetlacza
       sei();                               // Włączenie obsługi przewań   
       MCUCR = 0;                           // Zerowanie rejestru MCUCR
       MCUCR |= (1<<ISC11)|(0<<ISC10);         // Wyzwolenie przerwania zboczem opadającym
       GICR |= (1<<INT1);                     // Załączenie przerwania na INT1
       TCCR1A = 0;                         // T/C1 w trybie czasomierza
       TCNT2 = 0;                            // Zerowanie rejestru TCNT2
       TCCR2 = 0;                           // Zerowanie rejestru TTCR2
       TCCR2 |= (1<<WGM21)|(1<<WGM20)|(1<<CS21);   // T/C2 w trybie CTC z preskaler 8
       OCR2 = 0;                           // Zerowanie rejestru OCR2
       OCR2 = 125;                           // 125imp = 1ms
       TIMSK = 0;                           // Zerowanie rejestru TIMSK
       TIMSK |= (1<<OCIE2);                  // Załączenie przerwania gdy TCNT2 = OCR2
    }

    SIGNAL (SIG_INTERRUPT1)
    {
       if (pomiar==0)
       {
          TCNT1 = 0;                        // Zerowanie czasomierza T/C1
          TCCR1B |= (1<<CS10)|(1<<CS12);       // Zalaczenie timera1 - preskaler ck/1024
          MCUCR = 0;                        // Zerowanie rejestru MCUCR
          MCUCR |= (1<<ISC11)|(1<<ISC10);      // Przestawienie przerwania na zbocze narastajace
          pomiar = 1;
       }
       else
       {
          TCCR1B &= ~((1<<CS12)|(1<<CS10));      // Wylaczenie timera1
          MCUCR = 0;                        // Zerowanie rejestru MCUCR
          MCUCR |= (1<<ISC11)|(0<<ISC10);      // Przestawienie przerwania na zbocze opadajace
          pomiar = 0;
       }
    }

    SIGNAL (SIG_OUTPUT_COMPARE2)
    {
       ++cw_impuls;
       impuls=(cw_impuls*2)/1000;
    }

    int main( void )
    {
       Inicjalizacja();
       
       while(1)
       {   
          LCD_xy(0,0);
          sreg = SREG;                     // Zapis ustawień rejestru SREG
          cli();                           // Wyłączenie obsługi przerwań
          tc1 = TCNT1;                     // Zapis rejestru TCNT1 do zmiennej tc1
          SREG = sreg;                     // Ustawienie rejestru SREG
          dtostrf(tc1*1.024,8,0,czas);         // Konwersja float do ascii
          LCD_putstr_P(PSTR("TCNT1: "));
          LCD_putstr(czas);
          LCD_xy(0,1);
          LCD_putstr_P(PSTR("Sekundy: "));
          LCD_putint(impuls,10);
       }
    }


    Do zliczania czasu trwania pojedyńczych impulsów używam timera1, do zliczania czasu próbuję użyć timera2.

    Atmega jest taktowana z wewnętrzym oscylaotrem 1Mhz timer 2 pracuje w trybie CTC i przy zliczeniu do 125 (1ms) wywołuje przerwanie w którym zliczane są przerwania które się powinne pojawiać równo co 1ms.

    I teraz zliczony czas spóźnia mi się ok 2-3s na każdą minutę i dlaczego wartość:
    Code:
    impuls=(cw_impuls*2)/1000;
    muszę mnożyć przez 2 aby otrzymać sekundy?

    Wiem że przerwania z INT1 mogą się nakładać w czasie z przerwaniami z timera2 i powodować błędy, ale na chwile obecną bez uruchamiania timera1 timer2 źle zlicza.

    Timer2 domyślnie ma zliczać czasy tylko do 60s (jak wcześniej wspomniałem w celu zliczania ilości impulsów na minutę) więc się zadowoliłbym dokładnością <1s.

    Z góry dziękuję za wszelkie rady i pomoc :)

    --
    pozdrawiam
    demeus

    Dodano po 13 [minuty]:

    Co ciekawe kiedy kod do wyświetlania wrzucę do funkcji obsługi przerwania:

    Code:
    SIGNAL (SIG_OUTPUT_COMPARE2)
    
    {
       ++cw_impuls;
       impuls=(cw_impuls*4)/1000;
       LCD_xy(0,1);
          LCD_putstr_P(PSTR("Sekundy: "));
          LCD_putint(impuls,10);
    }


    Zliczony czas znowu dodatkowo musze mnożyć tym razem przez 4 by otrzymać sekundy, ale mimo wszystko opóźnienie dalej występuje.

    --
    pozdrawiam
    demeus
  • Poziom 18  
    wyłączanie zegara i konfigurowanie go w przerwaniu - to nie wygląda za dobrze...Szczególnie jeśli chodzi o rejestry MCUCR
  • Poziom 18  
    Kabuto15 napisał:
    wyłączanie zegara i konfigurowanie go w przerwaniu - to nie wygląda za dobrze...Szczególnie jeśli chodzi o rejestry MCUCR


    Akurat ta część działa prawidłowo i została zrobiona wg. wskazówek i porad, które znalazłem tutaj na elektrodzie.

    Mam problem z drugą częścią, z tym przerwaniem:
    Code:
    SIGNAL (SIG_OUTPUT_COMPARE2) 
    
    {
       ++cw_impuls;
       impuls=(cw_impuls*2)/1000;
    }


    Zlicza ono ciut za wolno i powoduje opóźnienie jak wyżej pisałem.

    --
    pozdrawiam
    demeus
  • Pomocny post
    Poziom 33  
    Po pierwsze jesli ustawiasz bity WGM21 i WGM20 to licznik dziala w trybie fast PWM. Po drugie, ale pewnie mniej wazne, sei() daje sie po skonfigurowaniu wszystkich licznikow. Po trzecie timer2 odlicza tylko 1ms a ty potrzebujesz 1s co daje ci 100 przerwan ray ponad sto taktow opoznienia, ale to i tak nie powinno wyjsc okolo 2 -3 s.
  • Poziom 18  
    _Robak_ napisał:
    Po pierwsze jesli ustawiasz bity WGM21 i WGM20 to licznik dziala w trybie fast PWM. Po drugie, ale pewnie mniej wazne, sei() daje sie po skonfigurowaniu wszystkich licznikow. Po trzecie timer2 odlicza tylko 1ms a ty potrzebujesz 1s co daje ci 100 przerwan ray ponad sto taktow opoznienia, ale to i tak nie powinno wyjsc okolo 2 -3 s.


    Witam
    Tak mój błąd źle skonfigurowałem licznik, rzeczywiście mam ustawiony fast PWM. Tak timer2 odlicza ms następnie to jest przeliczane na sekundy czy minuty. Zaraz przekonfiguruje program i sprawdzę wyniki.

    --
    pozdrawiam
    demeus
  • Poziom 33  
    Łoo wlasnie policzylem ze jezeli z tych przerwan co 1ms chcesz odmierzyc minute to przy 150 taktach na obsluge przerwania (okolo 67 zajmuje samo wwolanie bez obliczen w C) to opoznienie masz okolo 9 sekund. Musisz koniecznie zwiekszyc prescaller albo liczyc na timerze 1.
  • Poziom 18  
    _Robak_ napisał:
    Łoo wlasnie policzylem ze jezeli z tych przerwan co 1ms chcesz odmierzyc minute to przy 150 taktach na obsluge przerwania (okolo 67 zajmuje samo wwolanie bez obliczen w C) to opoznienie masz okolo 9 sekund. Musisz koniecznie zwiekszyc prescaller albo liczyc na timerze 1.


    Timer1 jest zajęty przez liczenie czasu trwania impulsu. Niedługo przekonfiguruje timer2 i sprawdze jak on działa w trybie CTC, póki
    co próbuję go uruchomić by działał bez opóźnień, a następnie postaram się zwiększyć prescaller.


    --
    pozdrawiam
    demeus
  • Poziom 33  
    Jesli bedziesz odmierzac stala dlugosc czasu to musisz dobrac dosiwadcalnie do jakiej wartosci ma zliczac i/lub ile ma wystapic przerwan. Przerwan musialby byc niewiele zebys uniknal opoznien.
  • Poziom 18  
    Po przekonfigurowaniu timera w tryb CTC zlicza on sekundy prawidłowo, bez żadnych opóźnień.
    Więc nic nie muszę wyliczać doświadczalnie, wszystko to prosta matematyka ;)

    A i oczywiście mnożenie czasu przez 2 nie jest konieczne.


    --
    pozdrawiam
    demeus
  • Poziom 33  
    No po odpowiednim ustawieniu pewnie ze nie trzeba doswiadczalnie jak przerwan wystapi kilka a nie kilkadzisiat tysiecy ;)