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

Attiny2313 [C] złe zliczanie timer0

bkawlatow 28 Paź 2011 02:04 2153 7
REKLAMA
  • #1 10073537
    bkawlatow
    Poziom 10  
    Witam!
    Od niedawna zmagam się z problemem którego nie mogę rozwiązać, dlatego zwracam się do was z prośbą o pomoc.A więc tak: potrzebuję zrobić odmierzanie czasu.Zamysł jest taki: taktuję Timer0 (8 bitowy) zegarem 4Mhz bez preskalera.Licznik przepełnia się po 256 taktach zegara czylo flaga przepełnienia ustawiana jest co 64us.Następnie wywoływane jest przerwanie które zlicza te przepełnienia.Po czasie powiedzmy 100ms powinna w zmiennej liczącej być wartość 1562.A jest 2195 a więc wygląda tak jakby zmienna zliczająca przepełnienia była inkrementowana szybciej.Czyli tak jakby licznik nie zliczał do 256 tylko przepełniał się wcześniej.Licznik pracuje w trybie Normal Mode gdzie maksymalna wartość licznika wynosi 256 właśnie.Nie wiem co robię źle...
    fragmenty kodu:

    ustawienia timera
    TCCR0A=0x00;//timer0 normal mode
    TIMSK|=(1<<TOIE0);
    TCCR0B=0x00;//timer zatrzymany


    obsługa przerwania
    
    ISR(TIMER0_OVF_vect)
    {
    if (licznik<65535) licznik++;
    }


    pomiar
    licznik=0;
    TCNT0=0;
    timer_start;
    _delay_ms(100);
    timer_stop;
    LCD_CLEAR;
    LCD_LOCATE(0,0);
    lcdwynik((licznik*256)+TCNT0);//wynik w mikrosekundach
    LCD_LOCATE(0,1);
    lcdwynik(licznik);
    


    ponadto startowanie i stopowanie timera:
    #define timer_start TCCR0B|=(1<<CS00) // preskaler 1
    #define timer_stop TCCR0B&=~(1<<CS00)


    funkcja lcdwynik() działa poprawnie i służy do wyświetlania liczby typu long int.
    Czekam na sugestie z góry dziękuje za pomoc;)
  • REKLAMA
  • REKLAMA
  • Pomocny post
    #3 10073617
    mirekk36
    Poziom 42  
    bkawlatow napisał:

    Czekam na sugestie z góry dziękuje za pomoc;)


    Robisz totalnie złe założenie niestety. Timer sprzętowy działa bardzo dobrze i precyzyjnie. Nie może on przepełniać się w trybie normal wcześniej niż po 256 tyknięciach zegara ;) ... zatem przez złe założenia - jeszcze wyciągasz złe wnioski i nie w tym miejscu co trzeba szukasz błędu.

    A błąd polega na tym, że ty zakładasz pomiar czasu trwania funkcji

    _delay_ms(100)

    za pomocą timera sprzętowego z użyciem jego przerwań i jakiejś - jak na taki przypadek strasznie długiej procedury tego przerwania nie wspominając już o pomiarze straaasznie długiego czasu.

    Zastanów się, odpalasz timer, zaczyna się wykonywać jego przerwanie no i rozpoczyna się _delay_ms(). Toż przecież to _delay_ms() będzie przerywane co 64us na czas wykonywania się przerwania, które u ciebie musi:

    1. odłożyć rejestry na stos
    2. sprawdzić (w ogóle nie potrzebny tu warunek - więc dodatkowe cykle/czas)
    3. inkrementować zmienną dwubajtową
    4. pobrać rejestry ze stosu

    przecież to zajmuje X czasu. Zatem musisz sobie uświadomić, że ten X czasu * 64us, spowoduje wydłużanie się trwania samego _delay_ms(100). To chyba oczywiste. Czyli łatwo sobie wyobrazić, że zanim ono się skończy - takie przedłużone - to twoja zmienna będzie miała wartość dużo większą niż się tu spodziewasz.

    Gdybyś chciał mierzyć w ten sposób np, długość trwania np stanu 0 na jakimś pinie (czas szybkiego kliknięcia klawisza) czy coś w tym stylu - to wtedy miałoby to jakiś sens.
  • #4 10073782
    bkawlatow
    Poziom 10  
    Kolego dondu:
    deklaracja zmiennej licznik:
    unsigned long int licznik=0;//zmienna globalna licznik

    Fuse bity ustawione prawidłowo (temat wałkowany setki razy).Zegar definiowałem początkowo w ustawieniach konfiguracyjnych AVR Studio, później gdy zaczęły się problemy przez #define F_CPU.

    Do mirekk36:
    Cytat:
    Timer sprzętowy działa bardzo dobrze i precyzyjnie. Nie może on przepełniać się w trybie normal wcześniej niż po 256 tyknięciach zegara
    To samo wyczytałem z noty katalogowej i takie były założenia właśnie trafniejsze niż złe założenia było stwierdzenie
    Cytat:
    nie w tym miejscu co trzeba szukasz błędu

    Nie myślałem jednak że użycie delaya może spowodować aż ponad pięciokrotne przekroczenie odmierzania danego odcinka czasu.Docelowo program ma liczyć czas od zapalenia sygnalizatora, do naciśnięcia przycisku:
    
    zapalenie_sygnalizatora;
    timer_start;//start timera
    while(!przerwanie);
    

    W przerwaniu które jest wywoływane przez naciśnięcie przycisku ustawiana jest zmienna przerwanie, i zatrzymywany jest timer.
    Cały program działał prawidłowo, do momentu w którym chciałem orientacyjnie zmierzyć dokładność odmierzania.Liczyłem się z tym że _delay wywoła błąd, jednak nie domyślałem się ze aż tak spory(zamiast 100ms na wyświetlaczu jest 560).
    Kolego mirekk36 czy następujące instrukcje spowodują że do funkcji lcdwynik zostanie przekazana poprawna wartość odmierzanego czasu?:
    
    //zegar 4Mhz
    //preskaler 1
    ISR(PCINT_vect)//obsługa przerwania pochodzącego od przycisku(są cztery)
    {
    
    if((!(PINB & 0x01)) && (dioda==0)) { timer_stop; przerwanie=1;}
    if((!(PINB & 0x02)) && (dioda==1)) { timer_stop; przerwanie=1;}
    if((!(PINB & 0x04)) && (dioda==2)) { timer_stop; przerwanie=1;}
    if((!(PINB & 0x08)) && (dioda==3)) { timer_stop; przerwanie=1;}
    }
    
    ISR(TIMER0_OVF_vect)//przerwanie od przepełnienia timera
    {
    if (licznik<65535) licznik++;
    }
    
    //a w mainie
    
    
    timer_start;
    while(!przerwanie);
    lcdwynik((licznik*256)+TCNT0);//wynik w mikrosekundach
    

    :?:
    pozdrawiam bkw20
  • #5 10073851
    mirekk36
    Poziom 42  
    No teraz nie będzie takiego problemu jak wcześniej. Powinno być ok - tylko musisz to sobie jakoś w main'ie już jakoś ładnie odpalać żeby cyklicznie mierzyć.

    A i dodam, że to nie użycie _delay_ms() powoduje wydłużenie / przekroczenie czasu, bo to funkcja jak każda inna, tyle że gdy w grę wchodzą przerwania to naturalne że będzie ona relatywnie dłużej trwać.

    Tak czy inaczej, teraz powinno ci już wyjść to co chcesz zrobić.
  • REKLAMA
  • #6 10073959
    bkawlatow
    Poziom 10  
    Tak rozumiem _delay podczas swoich 100ms był przerywany co 64us a więc 1562 razy:D mogło mu się to nie spodobać;) Jeszcze jedno pytanie czy używanie makr timer_start i timer stop które cyklicznie ustawiają i wyłączają preskaler jest poprawne? Czy jest może jakiś inny bardziej "elegancki" sposób na startowanie i zatrzymywanie timera??
  • REKLAMA
  • Pomocny post
    #7 10074015
    mirekk36
    Poziom 42  
    bkawlatow napisał:
    Czy jest może jakiś inny bardziej "elegancki" sposób na startowanie i zatrzymywanie timera??


    Ale to już jest bardzo elegancki sposób ;) nic lepszego nie wymyślisz.
  • #8 10074024
    bkawlatow
    Poziom 10  
    Dzięki wszystkim za pomoc temat zamykam;)
REKLAMA