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

[Rozwiązano] Atmega328 timer0 nie odlicza czasu/nie generuje przerwania (tryb normalny)

LinuksowyElektronik 06 Kwi 2019 19:28 744 9
  • #1 17889762
    LinuksowyElektronik
    Poziom 5  
    Próbuję zrozumieć zasadę działania timera0 w normalnym trybie pracy, ale niestety mój kod i sposób rozumowania najprawdopodobniej jest zły, ponieważ dioda nie miga. W związku z tym chciałbym poprosić kogoś o weryfikację kodu oraz mojego rozumowania, bo może coś robię źle. W kodzie zamieściłem stosowne komentarze więc nie będę ich dublował.

    Częstotliwość taktowania procesora wynosi 8 MHz.

    main.c
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    led.h
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    led.c
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    #2 17889859
    Andrzej__S
    Poziom 28  
    LinuksowyElektronik napisał:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Twoje obliczenia są (z grubsza) OK, jednak w mikrokontrolerze ATmega328 rejestr TCCR0A nie posiada bitów CS00 oraz CS02. Bity te znajdują się w rejestrze TCCR0B.
    Notabene, bitów WGM0n nie musisz zerować. W trakcie Power on Reset zostają one ustawione na wartość 0. Zerowanie może być konieczne tylko wtedy, kiedy wcześniej sam ustawisz je na wartość 1.

    LinuksowyElektronik napisał:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Niczego nie musisz zerować, a właściwie nawet nie powinieneś, bo to spowoduje niedokładne odmierzanie zadanego czasu.
  • #3 17889909
    LinuksowyElektronik
    Poziom 5  
    Andrzej__S napisał:
    Twoje obliczenia są (z grubsza) OK, jednak w mikrokontrolerze ATmega328 rejestr TCCR0A nie posiada bitów CS00 oraz CS02. Bity te znajdują się w rejestrze TCCR0B.


    Faktycznie, prawdopodobnie w tym tkwił cały problem! Skasowałem również zerowanie TCNT0. Nie pozostaje mi nic innego jak podziękować oraz przetestować mój "algorytm" w praktyce na różnych wartościach.
  • #5 17890640
    Andrzej__S
    Poziom 28  
    kaczakat napisał:
    TCNT0 możesz przypisać określoną wartość po przepełnieniu po to by uzyskać dokładną częstotliwość przerwania, a nie wynikającą z częstotliwości i prescalera

    Uzyskanie dokładnej częstotliwości poprzez modyfikowanie wartości rejestru TCNTn daje niepewne rezultaty, czyli czasami to zadziała zgodnie z oczekiwaniem, czasami nie. Wszystko zależy od tego, jaki prescaler masz ustawiony, jaka długa jest procedura obsługi przerwania i w którym miejscu tej procedury następuje wpisanie wartości do rejestru TCNTn oraz jaką wartość wpisujesz do tego rejestru.

    W celu uzyskania dokładniejszej częstotliwości przerwania, o której piszesz, stosuje się tryb CTC, w którym rejestr TCNTn jest zerowany sprzętowo po osiągnięciu przez niego odpowiedniej wartości.
  • #6 17891590
    kaczakat
    Poziom 34  
    Andrzej__S napisał:
    Uzyskanie dokładnej częstotliwości poprzez modyfikowanie wartości rejestru TCNTn
    No zapewne czasami tak jest, ale ten screen, który wrzuciłem to akurat zabawa z Atmaga8 i timer2. Z jakiegoś powodu program napisany w C działa OK i na ile oscyloskop jest to w stanie zmierzyć jest faktycznie 200us jak wyliczyłem. W Arduino dopóki jest zostawiona funkcja setup i loop to działa w jakiś zupełnie niezwiązanych częstotliwościach rzędu 1-16ms (nie używam PWM "arduinowego", po prostu został pusty loop). TCNT jest do zapisu i odczytu więc po prostu go sobie tak użyłem. Dopóki cały kod jest znany to raczej nie ma z tym problemu, problem może się pojawiać faktycznie gdy się dorzuci inne biblioteki, które mieszają po swojemu przy tym samym timerze lub wyłączają przerwania. Jedyne co zauważyłem to rozjeżdżanie się wbudowanych funkcji delay o czas obsługi przerwania, szczególnie to widać przy interwałach poniżej 50us.
    Pomogłem? Kup mi kawę.
  • #7 17891714
    Andrzej__S
    Poziom 28  
    kaczakat napisał:
    Z jakiegoś powodu program napisany w C działa OK i na ile oscyloskop jest to w stanie zmierzyć jest faktycznie 200us jak wyliczyłem.


    Ja nie napisałem, że to nigdy nie będzie działać prawidłowo (zapewne w wielu przypadkach będzie, szczególnie kiedy użyjesz jakiejś dużej wartości prescalera). Kiedy dokładność i powtarzalność czasu okresu nie są krytyczne, z powodzeniem można to rozwiązanie stosować. Chodzi tylko o to, że mając do dyspozycji sprzętowe zerowanie licznika (czyli rejestru TCNTn) w trybie CTC, nie ma sensu kombinować z programowym zerowaniem, bo taki sposób nie zawsze odliczy czas prawidłowo.

    Powiedzmy, że masz prescaler 8 i w procedurze obsługi przerwania musisz wpisać do rejestru TCNTn jakąś obliczoną przez Ciebie wartość w celu odliczenia przez timer określonego czasu. Kiedy jednak zostanie wywołana procedura obsługi przerwania, timer nie przestaje liczyć, więc do momentu, w którym procedura dojdzie do miejsca wpisania wartości do TCNTn, wartość tego rejestru będzie już najprawdopodobniej niezerowa, czyli timer już naliczy jakiś (bliżej nieokreślony) czas, który (w efekcie) zostanie doliczony do obliczonego przez Ciebie czasu. Błąd ten może być nieduży, ale może też nie być stały (czyli przy każdym przerwaniu może być inny) w zależności od tego, jaki będziesz miał kod w procedurze obsługi przerwania, np. które warunki w kodzie procedury obsługi przerwania będą w danej chwili spełnione, a które nie.

    W przypadku użycia trybu CTC, czas (liczony w taktach) zawsze będzie zgodny z założeniami, praktycznie niezależnie od kodu procedury obsługi przerwania (przy założeniu, że nie będziesz manipulował wartością TCNTn lub prescalerem).
  • #8 17891879
    LinuksowyElektronik
    Poziom 5  
    Z racji tego, że temat jest jeszcze aktywny, panie Andrzeju czy mógłbym jeszcze poprosić o rozwinięcie wątku:

    Andrzej__S napisał:

    Twoje obliczenia są (z grubsza) OK


    Obliczenia są dobre, ale jest jakieś "ale". W ramach możliwości chętnie dowiedziałbym się więcej na ten temat. Myślę, że wtedy można byłoby już zamknąć temat o ile nikt nie będzie miał niczego do dodania.
  • Pomocny post
    #9 17892611
    Andrzej__S
    Poziom 28  
    LinuksowyElektronik napisał:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Po pierwsze, timer liczy od 0 do 255, ale to oznacza 256 impulsów pomiędzy przepełnieniami, czyli powinno być 256, a nie 255 * 128us.
    Po drugie zaokrągliłeś wynik z 32.64 do 32 przed dzieleniem, co w efekcie dało niedokładny wynik ilości przepełnień.
    W rzeczywistości częstotliwość przerwań można obliczyć dużo prościej:
    ( 8000000 / 1024 ) / 256 = ok. 30,5176
    Wynik należy zaokrąglić do liczby całkowitej, czyli do 31, więc w efekcie w tym konkretnym przypadku otrzymujemy tę samą wartość, którą wyliczyłeś, czyli błąd wynikający z Twoich przybliżeń nie wpłynął na wynik końcowy. Nie zawsze jednak tak musi być. Czasami błąd wynikający z takich zaokrągleń może być dużo większy.

    Tak czy inaczej, przy takich ustawieniach timera - jak pewnie zauważyłeś - nie uzyskasz dokładnego czasu (błąd będzie rzędu 1,6%, czyli ok. 16 sekund na każdy 1000 odmierzonych, czyli prawie 1 minutę w ciągu 1 godziny). Zmieniając prescaler na 256 uzyskamy częstotliwość:
    ( 8000000 / 256 ) / 256 = ok. 122,07
    W tym przypadku wynik jest dużo bardziej zbliżony do liczby całkowitej, więc błąd będzie znacznie mniejszy. Nadal jednak czas nie będzie idealny. Dokładny czas 1 sekundy przy taktowaniu 8MHz można uzyskać na przykład konfigurując timer w tryb CTC z prescalerem 256, z wartością TOP=249 (czyli okres zliczania timera to 250 impulsów) i odliczając 125 przepełnień. Natomiast timer 16-bitowy można skonfigurować tak, by przerwanie było od razu co 1s, czyli nie będzie konieczności zliczania przepełnień.

    Oczywiście cały czas mowa o dokładności w odliczaniu taktów. Należy pamiętać, że faktyczny czas odmierzony przez timer będzie zależny od dokładności i stabilności źródła taktowania, które to parametry w przypadku wewnętrznego oscylatora nie są zbyt korzystne.
  • #10 17899345
    LinuksowyElektronik
    Poziom 5  
    Wszystko zostało wyjaśnione z pomocą użytkownika Andrzej_S.
REKLAMA