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

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

demeus 17 Sie 2008 21:08 4127 9
REKLAMA
  • #1 5448510
    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).

    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ść:
    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:

    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
  • REKLAMA
  • #2 5448945
    Kabuto15
    Poziom 19  
    wyłączanie zegara i konfigurowanie go w przerwaniu - to nie wygląda za dobrze...Szczególnie jeśli chodzi o rejestry MCUCR
  • #3 5449192
    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:
    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
  • REKLAMA
  • Pomocny post
    #4 5449855
    _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.
  • REKLAMA
  • #5 5449919
    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
  • #6 5449958
    _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.
  • REKLAMA
  • #7 5449993
    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
  • #8 5450009
    _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.
  • #9 5470737
    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
  • #10 5473736
    _Robak_
    Poziom 33  
    No po odpowiednim ustawieniu pewnie ze nie trzeba doswiadczalnie jak przerwan wystapi kilka a nie kilkadzisiat tysiecy ;)
REKLAMA