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

Atmega8, C - Niedokładny pomiar czasu z dokładnością do 1ms, 8MHz oscylator

harmichalh 16 Kwi 2013 21:33 2679 17
  • #1 12207492
    harmichalh
    Poziom 12  
    Witam. Robię układ do pomiaru czasu. Potrzebuję dokładność do 1ms. Napisałem taki kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Procesor ustawiony na 8Mhz, wewnętrzny oscylator.

    Program powinien wysyłać do komputera informacje o czasie jaki upłynął, porównałem czas z stoperem na telefonie i okazało się, że wartość "k" jest dobra (drobne różnice, ale nie duże ok 1-3%), ale wartość "val" jest niepoprawna. Zostawiłem telefon ze stoperem i mikrokontroler na 30 min. I wartość na telefonie była ok 2 lub 20 (teraz nie pamiętam ile zer było) razy mniejsza niż na komputerze.
    Oto jakie wartości mam w terminalu:
    Według zmiennej K - czas w sekundach | Według zmiennej VAL - czas w ms
    1|1
    2|2
    3|4
    4|6
    5|8
    6|10
    7|12
    8|14
    9|16
    10|18
    11|20
    12|22
    13|24
    14|26
    15|28

    Gdzie jest jakiś błąd? Bo nie mam pojęcia dlaczego to nie działa.
  • #2 12207609
    Milek79
    Poziom 15  
    Ojj, to nie tak! Nie należy odmierzać dłuższych czasów delayem! Więc na dluzsza metę K to kompletna bzdura. Ponadto wewnętrzny kwarc jest niedokładny. Ja bym to zrobił tak (chociaż to też pewnie zły pomysł) ze pod Timer2 w trybie asynchronicznym podpiął kwarc 32768khz z preskalerem 128. W przerwaniu z niego inkrementowac zmienna sekund i zerowac zmienna milisekund. W pętli głównej wyswietlalbym wynik, inkrementowal zmienna milisekund i czekał delayem 1ms. Wtedy nad sekundami czuwałby zewnętrzny kwarc a opoznenia delaya w ciągu sekundy były by może do zniesienia.

    Dodano po 7 [minuty]:

    Tak pomyślałem i chyba można by do milisekund użyć Timera0 i by nie było tego delaya

    Dodano po 4 [minuty]:

    Co do samego kodu, to coś pomieszales z konfiguracją. Pierwsze zerujesz bity CS 12, 11, 10 a później ustawiasz CS 11, 10. Reszty nie sprawdzam, ale przeanalizuj konfiguracje z datasheetem.
  • #3 12207739
    excray
    Poziom 41  
    harmichalh napisał:
    TCCR1B &= ~((1<<CS12) | (1<<CS11) | (1<<CS10));

    Jak już coś to TCCR1B = 0;. Poza tym ten rejestr po resecie i tak jest wyzerowany.
    harmichalh napisał:
    UBRRL = 103;
    UBRRH = 0;
    A nie prościej UBRR = 103; ? Efekt ten sam. To samo się tyczy OCR1A (zamiast OCR1AH i OCR1AL).
    harmichalh napisał:
    OCR1AL = 125; //co 1 ms
    Co 1,01ms. Dla 1ms powinno być 124.
    harmichalh napisał:
    while(1)
    {
    k++;
    USART_putString("Czas ");
    USART_putInt(k);
    USART_putString(" [ms]:");
    USART_putInt(val);
    USART_transmit(0x0d);
    USART_transmit(0x0a);
    _delay_ms(1000);
    }


    Super. A teraz sprawdź w debuggerze ile czasu trwa taka jedna pętla. Wysłanie "nastu" bajtów przez RSa z prędkością 4800bps z oczekiwaniem na koniec transmisji to jednak trochę trwa.
  • #4 12207764
    harmichalh
    Poziom 12  
    Zapominałem o tym napisać. Timer2 używam do obsługi multipleksowania LED. Kodu nie ma bo w celu rozwiązania problemu z pomiarem czasu go wyciąłem. Ten kod nie robi żadnych problemów i wszystko działa. Jeśli chodzi o delay to wcisnąłem go tylko, żeby pokazać rozbieżność wyników. Po to bawię się z timerami, żeby go nie używać ;) Docelowo program nie będzie nic wyświetlał do czasu naciśnięcia przycisku startu, potem nastąpi pomiar czasu, a potem dopiero zostaną wyświetlone dane.

    Jeśli dobrze wyczytałem to Timer0 nie może pracować w trybie CTC.

    @Milek79
    Jest to jakieś rozwiązanie, ale nadal nie wyjaśnia to dlaczego rozbieżności są takie duże (dla 15s z delay różnica to ponad 500%, a niedokładność wewnętrznego oscylatora nie powoduje aż takich różnic). Sprawdziłem jeszcze raz kod i nadal nie widzę błędów.

    @excray
    Pętla rzeczywiście długo trwa, ale przerwania mają wyższy priorytet niż taka pętla, więc nie powinno to zaburzać pomiaru czasu, a co najwyżej może wywoływać problemy z przesyłaniem wyniku.
  • #5 12207792
    excray
    Poziom 41  
    harmichalh napisał:
    @excray
    Pętla rzeczywiście długo trwa, ale przerwania mają wyższy priorytet niż taka pętla, więc nie powinno to zaburzać pomiaru czasu, a co najwyżej może wywoływać problemy z przesyłaniem wyniku

    O jakim zaburzaniu wyniku piszesz?? Chyba nie zrozumiałeś o co chodzi. Twoja pętla z "k" wykonuje się nie co 1ms ale co 2-3ms i z tego powodu rozjeżdżają się wyniki "k" z "val".
  • #7 12207797
    harmichalh
    Poziom 12  
    @Milek79
    Sprawdziełm jeszcze raz zgodność z datasheetem
    CS12=0 CS11=1 CS10=1 to clkI/O/64 (from prescaler)
    więc chyba tu nie ma błędu.
    Możesz dokładniej opisać jak użyć timera w trybie asynchronicznym i co oprócz kwarcu będę do tego potrzebował?

    Dodano po 5 [minuty]:

    @excray
    Albo źle rozumiem działanie przerwań, albo nie rozumiem o co Ci chodzi.
    Dobrze rozumiem, że jak timer wywołuje przerwanie to ono przerywa wykonywanie pętli, a po zakończeniu przerwania, pętla zostaje wznowiona? Jeżeli dobrze rozumiem to że pętla trwa dłużej + delay powoduję że k jest zwiększane co 1010ms. Jednak nadal nie wyjaśnia to aż takiej różnicy. W ciągu 1s "k" powinno zwiększyć się o 1, a "val" o 1000, a niestety się tak nie dzieje. Nawet jeżli "k" zwiększa się o 1 co 2000ms, to "val" nie powinno zwiększać się o 2 tylko o 1000.
  • #9 12207876
    excray
    Poziom 41  
    To ja źle zrozumiałem kod. Mea culpa. A co do Twojego problemu to jest on tutaj: "TCCR1A |= (1<<WGM12);". WGM12 jest w rejestrze TCCR1B a nie TCCR1A.
  • #10 12207886
    harmichalh
    Poziom 12  
    @Milek79
    Jeżeli jest to dużo lepsze rozwiązanie, to mogę wykorzystać Timer1 do multipleksowania wyświetlaczy LED, a Timer2 do pomiaru. Mam taki kwar pod ręką to mogę go dołożyć do płytki stykowej i spróbować. Do czego podpiąć ten kwarc? Wystarczy, że jak mam ustawione wszystko na CTC, zmienię preskaler i wartość porównania?
  • #11 12207893
    excray
    Poziom 41  
    Oczywiście poza WGM12 popraw też OCR1A na 124 tak jak pisałem.
  • #12 12207927
    Milek79
    Poziom 15  
    Kwarc pomiędzy TOSC1 i TOSC2.
    Fusebit CKOPT=0.
    Tryb zwykły, asynchroniczny
    Preskaler 128.
    Wartość porównania domyślna.
    Wtedy przerwanie od timera2 będzie wykonywać się co równa sekundę.

    Dodano po 1 [minuty]:

    Jednak jeżeli to będzie stoper na krótkie "dystanse" kilkunastu minut to poprawka excray powinna wystarczyć.
  • #13 12207950
    harmichalh
    Poziom 12  
    @Milek79
    Tylko ja muszę mierzyć czas z dokładnością do ms, a nie sekund.

    Napisałem coś takiego:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Powinno to po 100ms wyświetlać napis "10s", a wyświetla go po ok 50s. Więc chyba raczej kod pętli nie jest przyczyną problemu.
  • Pomocny post
    #14 12207964
    excray
    Poziom 41  
    W dalszym ciągu nie poprawiłeś WGM12. Jak wspominałem bit ten znajduje się w TCCR1B a nie w TCCR1A.
  • #15 12208010
    harmichalh
    Poziom 12  
    @excray
    Rzeczywiście to pomogło. Zastanawia mnie, dlaczego jak licznik był w trybie normalnym wywoływane było przerwanie...
    A wytłumaczysz mi dlaczego OCR1AL ma wynosić 124, a nie 125?
    Jak policzyłem to:
    8 000 000/64 = 125000
    125000/1000 = 125 (1000, żeby było co 1ms)
    Więc 125?

    @excray
    jak zrobiłem:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    to otrzymałem błąd 'UBRR' undeclared...

    @Milek79 da rade zastosować twoje rozwiązanie, ale żeby mierzyło sekundy?
  • Pomocny post
    #16 12208081
    excray
    Poziom 41  
    harmichalh napisał:
    Rzeczywiście to pomogło. Zastanawia mnie, dlaczego jak licznik był w trybie normalnym wywoływane było przerwanie...

    Bo przerwanie jest od porównania. Gdy TCNT1 = OCRA1 to jest wywoływane przerwanie.
    harmichalh napisał:
    A wytłumaczysz mi dlaczego OCR1AL ma wynosić 124, a nie 125?

    Bo tak jest napisane w datasheet. Zresztą zauważ że taka sytuacja jest sensowna. Zwróć uwagę co by się działo gdybyś wpisał OCRA1 = 0; Dlatego wartość wpisywana do OCR jest mniejsza o 1 od wyliczonej.
    harmichalh napisał:
    to otrzymałem błąd 'UBRR' undeclared...

    Ok, dobra ten jeden rejestr trzeba niestety rozpisać jako H i L ale w większości wypadków (OCR, TCNT, itd) da się pisać tak jak wspominałem co jest bardziej wygodne.
    A jak chcesz bardziej precyzyjny pomiar to zamiast wewnętrznego RC zastosuj zewnętrzny kwarc 8MHz.
  • #17 12208107
    harmichalh
    Poziom 12  
    Dzięki za pomoc. Teraz mam już inny problem:)

    Nie wiem jak uint32_t przekonwertować na tablicę znaków...
  • #18 12208130
    excray
    Poziom 41  
    Zapewne potrzebujesz funkcji ultoa.
REKLAMA