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

[GCC][M162] Powrót z przerwania w inne miejsce niż wywołano

Pawel1812 04 Maj 2009 00:03 1624 9
REKLAMA
  • #1 6486962
    Pawel1812
    Poziom 26  
    Witam.
    Pisze prosty program, w którym użytkownik w czasie jego wykonywania ma możliwość przerwać przerwaniem INT0 wykonywanie tego, co program aktualnie robi i skoczyć do innej funkcji niż się aktualnie wykonuje, nie powracając już z przerwania w miejsce, gdzie zostało ono wywołane, ale właśnie skacząc w inne miejsce bez powrotu w punkt rozpoczęcia przerwania. Jednak nie mam pomysłu jak ten problem rozwiązać. Byłbym wdzięczny za jakiekolwiek sugestie.

    Rozważałem nawet programowy reset, bez zerowania niczego ale to znacząco komplikowałoby program, więc nie jest to najlepsze rozwiązanie - zresztą i tak nie wiem jak to w C zrealizować.
  • REKLAMA
  • #2 6487011
    __Maciek__
    Poziom 20  
    A może procedura przerwania z atrybutem "Naked" ?? i zamiast reti skok w jakieś konkretne miejsce, np. do etykiety etc.
    Popatrz w PDF-a opisującego avrlibc w dział poświęcony obsłudze przerwań.
  • REKLAMA
  • #3 6487036
    Pawel1812
    Poziom 26  
    Rzeczywiście, można zrobić tak:
    
    void (INT0_vect) (void) __attribute__ ((naked)); \
    void (INT0_vect) (void) { __asm__ __volatile__ ("reti" ::); }
    

    tylko jak mam ustalić, gdzie skoczyć? Trzeba by podac adres asemblerowy, a ja piszę program w C.
    Jedyne, jaki problem to rozwiazuje, to programowy reset bez zerowania uC.
  • #4 6487228
    Freddie Chopin
    Specjalista - Mikrokontrolery
    To co chcesz zrobić jest niesamowicie skomplikowane, więc jeśli nie masz wielkiej potrzeby tak robić, to może warto rozważyć ten programowy reset - na przykład poprzez watchdoga - program po restarcie sprawdzałby co spowodowało reset i jeśli był to watchdog, to znaczy, że powinien zrobić coś innego niż po zwykłym resecie.

    To jest rozwiązanie o wiele prostsze - to co rozpatrywałeś początkowo zahacza mocno o dispatcher systemu operacyjnego, więc tak szukaj informacji.

    4\/3!!
  • REKLAMA
  • Pomocny post
    #5 6487514
    szelus
    Poziom 34  
    Powiem tak: od realizacji mniej wiecej takiej funkcjonalności istnieje w C para funkcji setjmp()/longjmp().

    I pewnie nawet na AVR ma to szansę zadziałać wywołane z obsługi przerwania, ale nie jest to najlepszy pomysł - bo nie kontrolujemy co się dzieje w programie w momencie zgłoszenia przerwania. Teoretycznie można się zabezpieczać przez blokowanie przerwań, ale zdecydowanie bezpieczniej będzie przerywać aktualnie realizowaną funkcję sprawdzając flagę ustawianą w przerwaniu.
    Wszystko zależy jednak od konretnej sytuacji...
  • REKLAMA
  • #6 6487792
    Konto nie istnieje
    Poziom 1  
  • #7 6488469
    Freddie Chopin
    Specjalista - Mikrokontrolery
    A co z rejestrami odłożonymi na stos przez dotychczasowy przebieg programu? Tłumaczę:

    f1()
    {
    f2();
    }

    f2()
    {
    f3();
    }

    f3()
    {
    ...
    }

    main()
    {
    f1();
    }

    main odkłada rejestry, f1 po wejściu odkłada rejestry, odkłada je przed wywołaniem f2. f2 odkłada rejestry na poczatku i odklada je przed wywolaniem f3. f3 odklada rejestry na poczatku. Jesli teraz wystapi przerwanie i bedzie chcialo wrocic na przyklad do maina, to jakim cudem chcecie uwolnic pamiec zajeta przez caly ten ciag wywolan?

    4\/3!!
  • Pomocny post
    #8 6488501
    Dr.Vee
    VIP Zasłużony dla elektroda
    W C goto jest lokalne, tj. działa jedynie wewnątrz danej funkcji.

    Największy problem to "zwijanie" stosu i przywracanie rejestrów zachowywanych przez funkcje. Tą funkcjonalność implementuje para funkcji setjmp/longjmp. Na AVR setjmp/longjmp zachowuje/przywraca:
    • rejestry r0, r2-r17, r28-r31
      stack pointer SPH,SPL
      rejestr stanu procesora SREG


    Można zrobić tak:
    #include <interrupt.h>
    #include <setjmp.h>
    
    static jmp_buf env;
    
    ISR(INT0_vect, ISR_NAKED) {
        longjmp(env, 1);
    }


    Trzeba pamiętać też o tym, żeby zablokować przerwanie od INT0 przed powrotem z funkcji, w której wywołano setjmp, np:
    int test(void)
    {
        if (setjmp(env) == 0) {
            /* aktywuj obsługę INT0 */
        } else {
            /* został wykonany skok przez longjmp */
        }
    
        while (1) {
            /* licz sobie coś-tam */
        }
    
        /* deaktywuj obsługę INT0 */
        return 1;
    }

    Pozdrawiam,
    Dr.Vee
  • #9 6492171
    BoskiDialer
    Poziom 34  
    Czy nie można po prostu w programie głównym sprawdzać, czy wystąpiło przerwanie (przez testowanie flagi przerwania lub przez testowanie zmiennej ustawianej przez przerwanie) a jeśli tak, to wyjść z funkcji i przejść do innej? Jeśli już trzeba coś przerwać, to najlepiej kontrolować w którym momencie funkcji (żeby czegoś nie uszkodzić w pamięci). To co często wydaje się, że powoduje natychmiastowe przerwanie funkcji nie zawsze tym musi być.

    __Maciek__: "naked" tutaj dużo nie zmienia, na stos będzie odkładany adres powrotu, więc dodatkowo trzeba by było go zdjąć aby nie doprowadzić do przepełnienia stosu. Rozwiązaniem jest to, co zaprezentował atom1477, jednak nie polecam tego typu konstrukcji. Skoro nie dajemy możliwości powrotu do kodu głównego, to można od razu przesunąć SP na koniec pamięci czyszcząc stos, którego wartość i tak będzie niepoprawna po wywołaniu (tu się rozwiązuje problem zaprezentowany przez Freddie Chopin). Bez czyszczenia sekcji bss i ładowania sekcji data zachowają się wartości zmiennych globalnych, więc to jest prawie reset.

    Ja jestem jednak zwolennikiem tego, aby program główny sam zdecydował w którym momencie przerwać, nie powstają wtedy żadne częściowe zapisy, wszystko funkcjonuje poprawnie dalej.
  • #10 6492274
    Nawigator
    Poziom 33  
    >>>Pawel Jak to jest prosty program to go napisz w asemblerze bo C nie przewiduje takich sztuczek.
    I tak musisz dokładnie wiedzieć co ze stosem więc to sprawa ryzykowna. Na pewno istnieją inne rozwiązania, klasyczne to flaga ustawiana w przerwaniu a potem sprawdzana w pętli głównej.
    To rozwiązanie też ma wadę bo gdy pętla główna ma pewien długi cykl obiegu to rozpoznanie flagi ma różne przypadkowe opóźnienie.

    N.
REKLAMA