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

[C][ATmega16] - Niechciana zmiana częstotliwości przerwania (Timer1)

mopsiok 12 Sie 2012 16:13 1404 3
  • #1 11201177
    mopsiok
    Poziom 14  
    Witam. Pisząc program natknąłem się na pewien problem, który uniemożliwia mi pójście dalej. Otóż potrzebuję wykonać określone operacje w równych odstępach czasu. Docelowo będzie to coś koło 2-5 minut, ale w czasie testów pracuję z mniejszymi odstępami. Ustawiłem Timer1 w tryb CTC i ustawiłem rejestr OCR1A na 15625, co przy taktowaniu 16MHz daje przerwanie co 1 sekundę. Oto istotne fragmenty kodu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Po odpaleniu komputer faktycznie odbiera kolejne liczby. Napisałem skrypt, który dokładnie zmierzy czas odbierania pierwszych 100 liczb. Liczby, rzecz jasna, powinny wysyłać się co 5 sekund. Wynik całkowicie mnie zadowala, bo okazuje się że między odebraniem pierwszej i setnej liczby minęło 499.982 sekund.
    Problem natomiast pojawia się wówczas, gdy funkcja obsługi przerwania wykonuje się długo. Docelowo będzie sterować portami przez około 16 sekund. Bezpośrednio pod USART_Transmit(z); wstawiłem linijkę która będzie udawać właśnie takie opóźnienie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    oraz zmieniłem warunek z if (x == 5) na if (x == 30). Odpaliłem skrypt ponownie. Spodziewałem się, że liczby będą przychodzić równo co 30 sekund, a tu niestety klops - przychodzą co 46 sekund!
    Nie do końca rozumiem dlaczego tak się dzieje... przecież w przerwaniach chodzi właśnie o to, żeby wykonywały się równo co zadaną ilość taktów, bez względu na to co jest wewnątrz funkcji obsługi przerwania...

    Czy ma ktoś jakiś pomysł jak można temu zaradzić? Taki bubel całkowicie krzyżuje mi plany. Na chwilę obecną równie dobrze mógłbym wykonywać te 16-sekundowe operacje a następnie po prostu czekać odpowiednią ilość czasu - wyszłoby to na plus, bo różnica wynosiłaby kilka sekund po 100 wywołaniach, a nie po 16 za każdym następnym.

    Dzięki i pozdrawiam
    mopsiok
  • Pomocny post
    #2 11201220
    szulat
    Poziom 23  
    mopsiok napisał:
    Taki bubel całkowicie krzyżuje mi plany

    Masz rację, zrobiłeś bubel ;) który na szczęście można naprawić na kilka sposobów, z których najprostszy to wywołanie sei() w funkcji obsługującej przerwanie.
    Dlaczego tak się dzieje - po prostu dlatego, że na początku obsługi przerwania samo z siebie wywołuje się cli().

    Najczęściej unika się tak długo trwających funkcji obsługujących przerwania a wywoływanie przerwania z przerwania może być początkiem katastrofy jeżeli nie jest dobrze przemyślane (dlatego dzieje się to tylko na twoje wyraźnie życzenie jeżeli sam sobie wywołasz sei() w przerwaniu). W twoim wypadku wyjątkowo możesz mieć pewność że normalne przerwanie będzie trwało ułamek sekundy a tylko raz na 30 sekund obsługa będzie trwała 16 sekund więc możesz sobie pozwolić na tę "groźną" sytuację.
    Ale najlepiej naucz się robić to inaczej, tak żeby główna praca programu była poza przerwaniami a przerwania tylko zarządzały synchronizacją czasu.
  • Pomocny post
    #3 11201228
    dondu
    Moderator na urlopie...
    1. Nie definiuj zegara w kodzie tylko w opcjach kompilatora: http://mikrokontrolery.blogspot.com/2011/03/fcpu-gcc-gdzie-definiowac.html

    2.

    mopsiok napisał:
    Problem natomiast pojawia się wówczas, gdy funkcja obsługi przerwania wykonuje się długo.
    Docelowo będzie sterować portami przez około 16 sekund.

    Przerwanie ma się wykonywać krótko i tego zawsze się trzymaj, bo łatwo się pogubić: http://mikrokontrolery.blogspot.com/2011/04/problemy-c-przerwania.html

    W przerwaniu ustaw jakąś flagę, a w main dopiero obsługę tego zdarzenia.
  • #4 11201353
    mopsiok
    Poziom 14  
    Ojej... rzeczywiście strasznie głupi błąd. Po prawdzie kod był pisany na szybko i nawet nie przyszło mi do głowy że to może być wina umieszczenia całej funkcji w przerwaniu. Dzięki wielkie Panowie :D. Wrzucam kod, być może komuś się kiedyś przyda.

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


    Pozdrawiam!
REKLAMA