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

[ATMEGA16/32][C] Timer2 odmierzanie czasu

rsikon 20 Paź 2008 09:22 4325 5
  • #1 5649549
    rsikon
    Poziom 26  
    Mam problem z TIMER2 - odmierzanie czasu , najlepiej 1s.

    Wartość b będzie zmieniała się - tylko co jaki odstep czasu.

    Może ktoś poratować? Albo ma może jakiś swój przykładowy programik na Timer2 z wywoływanym przerwaniem?

    Radzio


    
    // Testowanie timera 2 
    
    #define F_CPU 8000000UL
    
    #include <avr/io.h>                // dostęp do rejestrów
    #include <avr/interrupt.h>        // funkcje sei(), cli()
    #include <avr/signal.h>                // definicje SIGNAL, INTERRUPT
    
    int b=0;
    
    volatile uint8_t opoznienie;        // zmienna określająca częstotliwość
    
    
    SIGNAL (SIG_OVERFLOW2)                // przerwanie od przepełnienia
    {
      TCNT2 = opoznienie;                 // przeładuj TIMER2
      b++;
    }
    
    int main(void)                        // program główny
    {
    
      opoznienie = 0x01;                        // domyślna wartość dla TIMERA2
      TIMSK=_BV(TOIE2);         // włącz przerwania od przepełnienia 
      TCNT2=opoznienie;                         // zainicjuj TIMER1
      TCCR2=_BV(CS20)|_BV(CS21)|_BV(CS22); // czasomierz 2 taktowany F_CPU/1024
    
      sei();                        // włącz obsługę przerwań
      while(1)                        // pętla nieskończona
      {
     
     // tu wypisuje wartosc b = co ile bedzie sie zmieniac ??
        
      }
    }
    
    
  • #2 5653089
    Limonit
    Poziom 13  
    rsikon napisał:
    Mam problem z TIMER2 - odmierzanie czasu , najlepiej 1s.

    Wartość b będzie zmieniała się - tylko co jaki odstep czasu.

    Może ktoś poratować? Albo ma może jakiś swój przykładowy programik na Timer2 z wywoływanym przerwaniem?

    Radzio


    
    // Testowanie timera 2 
    
    #define F_CPU 8000000UL
    
    #include <avr/io.h>                // dostęp do rejestrów
    #include <avr/interrupt.h>        // funkcje sei(), cli()
    #include <avr/signal.h>                // definicje SIGNAL, INTERRUPT
    
    int b=0;
    
    volatile uint8_t opoznienie;        // zmienna określająca częstotliwość
    
    
    SIGNAL (SIG_OVERFLOW2)                // przerwanie od przepełnienia
    {
      TCNT2 = opoznienie;                 // przeładuj TIMER2
      b++;
    }
    
    int main(void)                        // program główny
    {
    
      opoznienie = 0x01;                        // domyślna wartość dla TIMERA2
      TIMSK=_BV(TOIE2);         // włącz przerwania od przepełnienia 
      TCNT2=opoznienie;                         // zainicjuj TIMER1
      TCCR2=_BV(CS20)|_BV(CS21)|_BV(CS22); // czasomierz 2 taktowany F_CPU/1024
    
      sei();                        // włącz obsługę przerwań
      while(1)                        // pętla nieskończona
      {
     
     // tu wypisuje wartosc b = co ile bedzie sie zmieniac ??
        
      }
    }
    
    


    No to po kolei. Sprawa nie jest skomplikowana. Wygląda, że chcesz napędzac procesor zegarem 8MHz i zmieniać wartośc b w przerwaniu co 1 sekundę. trzeba sobie policzyć. Licznik 2 jest 8 bitowy, zatem przepełnia się co 256 zliczeń i wtedy zgłasza przerwanie OVF. Najwolniej, bez użycia zewnętrznych elementow licznik ten można napedzać 1024 razy wolniej od zegara procesora, co juz zauważyłeś. Da to nam 7812,5Hz a to kiedy zostanie podzielone na 256 da około 30,5 przerwania na sekundę. Wniosek jest taki, że z tego dzielnika precyzyjnie co 1 sekundę nie uzyskamy.
    Da się to zrobic np tak, licznik z szybkością zegara, czyli 8MHz, wtedy wyjdzie 31250 przerwan na sekundę, Obsługa przerwania powinna zawierac wtedy licznik 16 bitowy. Może to wyglądac tak:
    
    ISR (TIMER2_OVF_vect)
    {
    if (++licznik==31250) { licznik = 0; b++;}
    }
    


    Nie ma potrzeby ingerencji w rejestr TCNT2 w czasie pracy licznika, sam się "przekręca" i zeruje dzięki temu.
    Jako volatile należy deklarować zmienne, które są modyfikowane w procedurze przerwania, czyli b oraz int licznik, gdyż to gwarantuje, że kompilator nie zapomni, że mogą być modyfikowane poza głownym programem.
    I na koniec, teraz procedury przerwań deklaruje się wlasnie tak:
    ISR (TIMER2_OVF_vect)
    Tutaj jest to opisane: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
    aczkolwiek pewnie Twój sposób też zadziała.
    Jeśli uzywasz już licznika2, to pamiętaj, że posiada on mozliwość pracy asychron. z kwarcem 32kHz. Z takiego tempa pracy łatwiej jest uzyskać przerwania co 1 sek. a koszt elementu znikomy.
  • #3 5653266
    rsikon
    Poziom 26  
    łał :)


    Super opis - co prawda wszystkiego się domyślałem i upewniałem przy próbach uruchomienia.

    A jak wygląda dokładniej ta praca asynchroniczna - technicznie wiem tylko programowo jak to obsluzyc.

    Drugi obecnie problem to wieszanie się / resetowanie mojego programu.
    Troszkę go zmodyfikowałem obecnie ma on postać:

    
    
    // Testowanie timera 2
    
    
    #define F_CPU 8000000UL
    #include <inttypes.h>
    #include <lcd.h>
    #include <avr/io.h>                // dostęp do rejestrów
    #include <avr/interrupt.h>        // funkcje sei(), cli()
    
    
    int b=0;
    char buff[3];
    volatile uint8_t opoznienie;        // zmienna określająca częstotliwość
    uint32_t sss=0;
    
    
    SIGNAL (SIG_OVERFLOW2)                // przerwanie od przepełnienia
    {
      TCNT2 = opoznienie;                 // przeładuj TIMER2
      b++;
     if (b==46) {
    PORTD ^= _BV(5);
    b=0;
    sss++;
    if (sss>86399) sss=0;
    }
    }
    
    
    
    
    void godzina(int wx, int wy)
    {
    
        LCD_xy(wx,wy);
    	write_text(itoa(sss/3600,buff,10));
    	write_text(":");
    	write_text(itoa(sss/60-(sss/3600*60),buff,10));
    	write_text(":");
    	write_text(itoa(sss-(sss/60*60),buff,10));
    	write_text(" ");
    
    }
    
    
    
    int main(void)                        // program główny
    {
    LCD_init();
    LCD_clr();
    
    DDRD=0xFF;		// cały port C jako wyjscie
    PORTD=0x00;
    
    
      opoznienie = 0x55;                        // domyślna wartość dla TIMERA2
      
      
      
      
      TIMSK=_BV(TOIE2);         // włącz przerwania od przepełnienia 
      TCNT2=opoznienie;                         // zainicjuj TIMER2
      TCCR2=_BV(CS20)|_BV(CS21)|_BV(CS22); // czasomierz 2 taktowany F_CPU/1024
    
     
     
      sei();                        // włącz obsługę przerwań
      while(1)                        // pętla nieskończona
      {
     
        godzina(0,0);
    
      }
    }
    
    
    


    Nie wiem czy ATMEGA uszkodzona czy problemy inne typu zasilanie - aczkolwiek pracuje na "firmowej" płycie testowej i do tej pory nie miałem problemów.

    Program w różnych odstępach (20 minut ... pół godziny) resetuje się.
    Co raczej wyklucza błędu w samym programie.
    Nie wiem, za dużo poleceń w obsłudze przerwania...

    Albo może to to:

    Cytat:
    Jako volatile należy deklarować zmienne, które są modyfikowane w procedurze przerwania, czyli b oraz int licznik, gdyż to gwarantuje, że kompilator nie zapomni, że mogą być modyfikowane poza głownym programem.




    Jeśli można to proszę jeszcze o poruszenie tematu (najlepiej z przykładem) Watchdoga.

    Radzio
  • #4 5653864
    Limonit
    Poziom 13  
    rsikon napisał:


    A jak wygląda dokładniej ta praca asynchroniczna - technicznie wiem tylko programowo jak to obsluzyc.


    Wszystko jest doskonale opisane w dokumentacji, np do m16 strona 132. Generalnie jest rejestr ASSR. Ustawienie odpowiedniego bitu przełącza źródło taktowania timera2 na kwarc podłączony tdo wyjść TOSC. Mozna tam podłączyć wyłącznie kwarc 32768Hz i z tą częstotliwością bedzie pracował licznik (po uwzględnieniu dzielników).
    rsikon napisał:


    Drugi obecnie problem to wieszanie się / resetowanie mojego programu.
    Troszkę go zmodyfikowałem obecnie ma on postać:

    W zasadzie powinno działać. Ale tak: zmiennej opóźnienie nie zmieniasz w obsłudze przerwania. Nie musi być volatile. Za to b i sss powinny. Jako b wystarcz unsigned char.
    I kolejna sprawa: po co tak często uaktualniac wyswietlacz. Można to od biedy robić co przerwanie, albo co jakieś opóźnienie.

    Generalnie obsluga przerwanai powinna się wykonywac w ustalonym czasie i powinna być możliwie krótka, raczej Twoja za długa nie jest.

    Cytat:


    Jeśli można to proszę jeszcze o poruszenie tematu (najlepiej z przykładem) Watchdoga.

    Radzio


    A co konkretnie chcesz wiedzieć? Watchdog to rodzaj timera, który, kiedy nie zostanie wyzerowany (specjalną instrukcją) resetuje procesor. Ma to zabezpieczać system, kiedy program "pójdzie w maliny", przed zawieszeniem i zapewnić wieksza bezobsługowość.
    Poczytaj datasheet, a jak coś bedzie niejasne to dopytaj.
  • #5 5653999
    rsikon
    Poziom 26  
    wlasnie o pujscie w maliny mi chodzi - inny program po jakims czasie dziwnie mi sie wiesz - czas jest uaktualniany a temperatura nie. chce wylapac moment ze temperatura stoi - czujnik nie reaguje i wowczas wykonac reset procka.

    Mozesz krotki przyklad na obsluge dac?

    Cos prostego byle zawieralo kompletna obsluge watchdoga - uaktywnienie, reset itd.

    Pozniej pozmieniam te zmienne i kostke atmega - zobacze czy to cos da.

    Radzio
  • #6 5655402
    Limonit
    Poziom 13  
    Jest rejestr WDTCR.
    Ustawienie bitu 3 włącza watchdoga.
    Bity 0..2 odpowiadają za częstotliwośc pracy.
    
    0 0 0 16K (16,384) 17.1 ms 
    0 0 1 32K (32,768) 34.3 ms 
    0 1 0 64K (65,536) 68.5 ms 
    0 1 1 128K (131,072) 0.14 s
    1 0 0 256K (262,144) 0.27 s 
    1 0 1 512K (524,288) 0.55 s 
    1 1 0 1,024K (1,048,576) 1.1 s 
    1 1 1 2,048K (2,097,152) 2.2 s 
    


    i już. Co ustalony czas musisz Dać WDR w asmblerze, a chyba wdr(); w C.
    Czas ten nie może być dłuższy niż ustawiony bitami 0..2. Jesli WDR (watchdog reset) nie zostanie wykonane, nastąpi reset.
    Żeby wyłączyc watchdoga należy wykonać poniższą funkcję.
    
    void WDT_off(void)
    {
    /* Reset WDT*/
    _WDR();
    /* Write logical one to WDTOE and WDE */
    WDTCR |= (1<<WDTOE) | (1<<WDE);
    /* Turn off WDT */
    WDTCR = 0x00;
    }
    
REKLAMA