Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek 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

demeus 17 Sie 2008 21:22 3863 9
  • #1 17 Sie 2008 21:22
    demeus
    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

    0 9
  • #2 17 Sie 2008 22:44
    Kabuto15
    Poziom 18  

    wyłączanie zegara i konfigurowanie go w przerwaniu - to nie wygląda za dobrze...Szczególnie jeśli chodzi o rejestry MCUCR

    0
  • #3 17 Sie 2008 23:59
    demeus
    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

    0
  • Pomocny post
    #4 18 Sie 2008 11:40
    _Robak_
    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.

    0
  • #5 18 Sie 2008 12:07
    demeus
    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

    0
  • #6 18 Sie 2008 12:23
    _Robak_
    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.

    0
  • #7 18 Sie 2008 12:37
    demeus
    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

    0
  • #8 18 Sie 2008 12:43
    _Robak_
    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.

    0
  • #9 24 Sie 2008 20:42
    demeus
    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

    0
  • #10 25 Sie 2008 22:47
    _Robak_
    Poziom 33  

    No po odpowiednim ustawieniu pewnie ze nie trzeba doswiadczalnie jak przerwan wystapi kilka a nie kilkadzisiat tysiecy ;)

    0