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

[ATtiny45V] Priorytety przerwań

Circuit Chaos 03 Sie 2010 03:22 1912 10
REKLAMA
  • #1 8359483
    Circuit Chaos
    Poziom 13  
    W projekcie założyłem sobie dwa przerwania z dwóch timerów. Jedno (nazwijmy je A) wywoływane często i krytyczne czasowo (przerwanie musi być obsłużone jak najszybciej po jego pojawieniu się), trwające tylko kilkanaście cykli. Drugie (B) mniej krytyczne (właściwie w ogóle nie krytyczne), wołane rzadziej, trwające kilkaset cykli. Główna pętla programu będzie zawierała jedynie sleepa, całość ma się dziać w przerwaniach.

    Ogólnie A obsługuje odbiór transmisji asynchronicznej (z odbiornika podczerwieni) a B całą resztę. Przerwanie A to tak naprawdę dwa przerwania - najpierw PCINT a potem output compare z timera (będzie pracował w CTC), ale konstrukcja programu sprawi że albo będzie wołany PCINT (początek transmisji - inicjalizacja timera, wyłączenie siebie) albo timer (podczas samej transmisji - po jej zakończeniu wyłączenie timera i włączenie PCINT-a), więc dla uproszczenia nazwałem je jednym przerwaniem.

    Czyli w skrócie - przerwanie A ma mieć priorytet nad przerwaniem B. Nie dosłownie (dwa przerwania w jednym cyklu), bo dosłownie to priorytety w AVR maleją wraz ze wzrostem adresu w tablicy wektorów, ale ma się móc w każdej chwili wciąć w trwającą obsługę B, a w odwrotnej sytuacji (trwa A, pojawia się B) to B ma grzecznie poczekać na zakończenie A (z tego co wiem to właśnie tak działają przerwania w AVR - są opóźniane).

    Wymyśliłem żeby obsługiwać przerwanie A normalnie - z wyłączonymi innymi przerwaniami (tak żeby B nie mogło przeszkodzić), a B z włączonymi (instrukcja SEI na początku ISR, avr-libc to załatwia). To ma sens i zadziała tak jak tego chcę? Mógłbym sam sprawdzić i nie zawracać wam głowy, ale akurat race conditions są dosyć trudne do wytropienia a działać może, bo sygnał chcę samplować w środku trwania bodu, więc jak się trochę przesunie, to zazwyczaj zadziała... Ale ma działać zawsze a nie tylko zazwyczaj.

    Alternatywnie myślałem nad przeniesieniem obsługi B do głównej pętli - tak żeby B tylko ustawiało flagę, ale to rodzi inne problemy (race condition między sprawdzeniem flagi a sleepem), chciałbym ich uniknąć.

    Co myślicie?
  • REKLAMA
  • #2 8359671
    tmf
    VIP Zasłużony dla elektroda
    Tak, można zrobić tak, że B pierwsze co zrobi to odblokuje przerwania. Będzie to działać ok. O priorytetach na ATTiny zapomnij - nie ma ich.
  • REKLAMA
  • #3 8359745
    Andrzej__S
    Poziom 28  
    gophi napisał:

    instrukcja SEI na początku ISR, avr-libc to załatwia

    Dla ścisłości, powinieneś użyć:
    
    ISR(XXX_vect, ISR_NOBLOCK)
    {
    ...
    }
    

    bo wpisanie na początku obsługi przerwania sei(); to nie to samo. Wtedy przerwania zostają włączone dopiero po prologu, czyli np. po odłożeniu na stos używanych rejestrów - to wprowadza pewne opóźnienie.

    gophi napisał:

    To ma sens i zadziała tak jak tego chcę?

    Zadziała pod warunkiem przestrzegania pewnych zasad, czyli np.: przerwania nie mogą modyfikować tych samych zmiennych globalnych, nie mogą korzystać z tych samych funkcji, jeśli nie są one 'reentrant', warto też zadbać o to, żeby obsługa przerwania B przypadkowo nie zagnieździła się sama w sobie (bo to może doprowadzić np. do przepełnienia stosu) maskując odpowiednią dla tego przerwania flagę 'interrupt enable'. Zagnieżdżanie przerwań trzeba wykonywać z należytą ostrożnością. Po szczegóły odsyłam do avr-libc-user-manual.
  • #4 8359790
    Circuit Chaos
    Poziom 13  
    Andrzej__S napisał:
    gophi napisał:

    instrukcja SEI na początku ISR, avr-libc to załatwia

    Dla ścisłości, powinieneś użyć:
    
    ISR(XXX_vect, ISR_NOBLOCK)
    {
    ...
    }
    

    bo wpisanie na początku obsługi przerwania sei(); to nie to samo. Wtedy przerwania zostają włączone dopiero po prologu, czyli np. po odłożeniu na stos używanych rejestrów - to wprowadza pewne opóźnienie.

    Wiem - to właśnie miałem na myśli mówiąc że że avr-libc to załatwia :)

    Tak w ogóle to zastanawiałem się czy nie napisać tego fragmentu w assemblerze (chciałem puścić procka na 128kHz i to musiałoby trwać jak najkrócej), ale przy tej częstotliwości częstotliwość wywoływania ISR za bardzo pływała (pomierzyłem to i było ok. 80us różnic przy kolejnych przerwaniach). Zostawiłem 8MHz, trudno, i tak przez większość czasu zegar będzie wyłączony...

    Cytat:
    Zadziała pod warunkiem przestrzegania pewnych zasad, czyli np.: przerwania nie mogą modyfikować tych samych zmiennych globalnych

    No niektóre modyfikują. Sprawę bardzo ułatwia fakt że wywołanie jednego przerwania jest atomiczne (nieprzerywalne).

    Przerwanie A po wypełnieniu tych zmiennych ustawia jeszcze jedną zmienną (flagę) informującą że tamte dane są już spójne. B sprawdza je tak:

    - cli
    - sprawdzenie flagi
    - jeżeli jest ustawiona to skopiowanie sobie danych i wyczyszczenie flagi
    - sei
    - ewentualne operacje na skopiowanych danych

    To powinno działać dobrze?

    Cytat:
    warto też zadbać o to, żeby obsługa przerwania B przypadkowo nie zagnieździła się sama w sobie (bo to może doprowadzić np. do przepełnienia stosu) maskując odpowiednią dla tego przerwania flagę 'interrupt enable'.

    Można zamaskować... Ale przy tej częstotliwości wywołań (ok. 10Hz) raczej nie ma szans żeby to nastąpiło, czas wykonywania procedury będzie o rzędy wielkości krótszy.

    Cytat:
    Zagnieżdżanie przerwań trzeba wykonywać z należytą ostrożnością. Po szczegóły odsyłam do avr-libc-user-manual.

    Od tego zacząłem :)

    Dzięki, pozdrawiam.
  • REKLAMA
  • #5 8359967
    Andrzej__S
    Poziom 28  
    gophi napisał:

    - cli
    - sprawdzenie flagi
    - jeżeli jest ustawiona to skopiowanie sobie danych i wyczyszczenie flagi
    - sei
    - ewentualne operacje na skopiowanych danych

    To powinno działać dobrze?

    Myślę, że tak. Jest tylko jedno małe 'ale'. Jeśli przerwanie A pojawi się pomiędzy cli a sei, to zostanie o pewien czas odroczone. Nie wiem ile tam masz instrukcji, ale jeżeli takie opóźnienie jest do przyjęcia, to powinno być OK.

    gophi napisał:

    Można zamaskować... Ale przy tej częstotliwości wywołań (ok. 10Hz) raczej nie ma szans żeby to nastąpiło, czas wykonywania procedury będzie o rzędy wielkości krótszy.

    Jeżeli przerwania pojawiają się w przewidywalnych odstępach czasu dużo większych niż czas potrzebny na ich obsługę, to nie trzeba maskować. Maskowanie to tylko jedna z metod. "Samo-zagnieżdżania" można także uniknąć właśnie w przedstawiony powyżej sposób. Nie w każdym wypadku daje się jednak przewidzieć, czy przerwanie nie pojawi się podczas procedury jego obsługi (a im dłuższa procedura, tym większe ryzyko).
  • #6 8360077
    Circuit Chaos
    Poziom 13  
    Andrzej__S napisał:
    gophi napisał:

    - cli
    - sprawdzenie flagi
    - jeżeli jest ustawiona to skopiowanie sobie danych i wyczyszczenie flagi
    - sei
    - ewentualne operacje na skopiowanych danych

    To powinno działać dobrze?

    Myślę, że tak. Jest tylko jedno małe 'ale'. Jeśli przerwanie A pojawi się pomiędzy cli a sei, to zostanie o pewien czas odroczone.

    Ano. Dlatego czas w "sekcji krytycznej" chcę poświęcić tylko na kopiowanie, dalsza obróbka już będzie na lokalnej kopii.

    Cytat:
    Nie wiem ile tam masz instrukcji, ale jeżeli takie opóźnienie jest do przyjęcia, to powinno być OK.

    Przy 8MHz powinno się wyrobić.

    Pozdrawiam.
  • REKLAMA
  • #7 8610592
    wjeszak
    Poziom 12  
    Witam,
    Mam taki oto problem. Mianowicie:
    W przerwaniu INT0 (wywoływanym z PCFa) wysyłam co sekundę ramkę danych (konkretnie 13 bajtów) na USART0 na którym to działa pewien czujnik. Następnie oczekuję od niego odpowiedzi (47 bajtów) w przerwaniu od USART0. Odpowiedź dopisuję do pliku na karcie SD. I wszystko jest OK. Co pewien czas (np. raz na dobę) wysyłam zawartość tegoż pliku przez moduł GSM który działa na USART1 (dane z modułu również odbieram w przerwaniu). Problem zaczyna się w momencie, gdy komunikuję się z modułem. Gubię wtedy niektóre bajty od niego pochodzące. Nie mogę jednak wyłączyć przerwania INT0 na czas wysyłania danych przez GPRS gdyż musi zostać zachowana ciągłość danych w pliku. Zgodnie z tablicą wektorów przerwań w przypadku gdy 2 przerwania są wywołane równocześnie (a sądzę, że tak się właśnie dzieje) pierwsze jest obsługiwane to o niższym adresie (USART0). Dodatkowo istnieje możliwość, że równocześnie zostanie wywołane przerwanie INT0 oraz USART1.
    Wyłączenie przerwania USART1 i odbieranie danych za pomocą poolingu jest chyba średnie :/
    Myślę, że intuicyjnie da się złapać o co chodzi.
    Co z tym fantem zrobić ?

    --
    Wjeszak
  • #8 8610629
    tadzik85
    Poziom 38  
    INT0 typu no_block albo skrócić obsługę INT0.
  • #9 8610668
    wjeszak
    Poziom 12  
    INT0 jest krótkie. ISR_NOBLOCK dla INT0 nie rozwiązuje sprawy, gdyż przerwanie USART1 może zostać wywołane dokładnie wtedy, gdy USART0 (nie potrafię przewidzieć kiedy otrzymam dane z GPRS).

    Myślałem o jakiejś namiastce RTOS, ale to chyba strzelanie do muchy z armaty.
    Dla "ułatwienia" sprawy na USART1 obsługuję jeszcze konsolę użytkownika.
  • #10 8610696
    tadzik85
    Poziom 38  
    Nie chce mi się wierzyć, że kilka taktów procka już blokuje ci odbieranie/wysyłanie danych. A może zamiana UARTÓW pomoże? Wówczas zmieni się ich priorytet.
  • #11 8610808
    wjeszak
    Poziom 12  
    Właśnie o tym samym przed chwilą pomyślałem i to zrobiłem, ale wtedy mi się wykrzacza konsola użytkownika. Najgorsze jest to, że program się wysypuje w losowych miejscach. Myślę, że wynika to z tego, że dane z modułu przychodzą w różnym czasie.
    Problemem okazała się zbyt długa obsługa przerwania.
REKLAMA