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

[C/asm] - Jak dynamicznie przypisać funkcję do wektora przerwań w C?

MrKiel 04 Mar 2014 14:38 1866 22
  • #1 13368815
    MrKiel
    Poziom 9  
    Witam,
    Czy ktoś z kolegów może wie jak, lub spotkał się z "ręcznym" ustawianiem wektora przerwań w C ? Chciałbym osiągnąć w moim kodzie następujący efekt :

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

    Podany przeze mnie przykład jest oczywiście niepoprawny, ale mam nadzieję, że oddaje to co mam na myśli.
    Chciałbym w swoim kodzie zdefiniować kilka funkcji obsługi przerwań, ale bez przypisywania ich do wektora przy pomocy makra ISR. Samo wpisanie adresów funkcji przerwań chciałbym wykonać "w trakcie" pracy programu.
  • #2 13368829
    BlueDraco
    Specjalista - Mikrokontrolery
    Lepiej po prostu w procedurze obsługi przerwania wywoływać jedną z tych dwóch. Tym bardziej, że w każdym procesorze, który nie jest Cortexem, procedura obsługi przerwania nie jest zwykłą procedurą i musi być specjalnie traktowana przez kompilator.
  • #3 13368850
    MrKiel
    Poziom 9  
    BlueDraco napisał:
    Lepiej po prostu w procedurze obsługi przerwania wywoływać jedną z tych dwóch

    Tak mam to zrealizowane w tym momencie, ale straszliwie komplikuje to kod.
  • #4 13368878
    el2010tmp
    Poziom 25  
    MrKiel napisał:
    Tak mam to zrealizowane w tym momencie, ale straszliwie komplikuje to kod.


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

    Gdzie jest ta "straszliwa komplikacja" ?
  • #5 13368888
    MrKiel
    Poziom 9  
    el2010tmp napisał:
    Gdzie jest ta "straszliwa komplikacja" ?

    W wywołaniu funkcji wewnątrz ISR, czego ze względu na dość krytyczne ograniczenia czasowe bardzo chciałbym uniknąć.
  • #6 13368892
    BlueDraco
    Specjalista - Mikrokontrolery
    To nie wołaj funkcji, tylko wklej kody tych funkcji pod if () i else.
  • #7 13368904
    MrKiel
    Poziom 9  
    BlueDraco napisał:
    To nie wołaj funkcji, tylko wklej kody tych funkcji pod if () i else.

    Tak wiem, że są inne metody realizacji tej funkcjonalności. Interesuje mnie po prostu możliwość "emulacji" nadpisywania wektora przerwań.

    Dodano po 35 [minuty]:

    Konkretny przykład :
    Mam dwie biblioteki realizujące kolejno :
    1) programową komunikację UART
    2) programową komunikację USB
    Obie działające, zarówno razem jak i osobno. Punktem "spięcia" jest to, że obie używają dokładnie tej samej ISR a dokładnie ISR(TIM0_COMP_vect).
    Chciałbym ich teraz obu jednocześnie użyć w projekcie. W obecnej postaci nie mogę, z powodu dwukrotnej definicji ISR.
    To co zrobiłem, to dyrektywami pre-procesora wyłączyłem kod ISR'a w obu z nich i napisałem (metodą kopiuj/wklej + sprawdzanie warunku) ISR w projekcie. Działa, ale komplikuje zmiany w kodzie, szczególnie w zakresie przerwania.
    Dalej idąc, w związku z ograniczeniami czasowymi, obie biblioteki nie "działają" jednocześnie a naprzemiennie, co w rezultacie prowadzi do mojego pytania. Czy istnieje jakiś elegancki sposób przełączania ISR pomiędzy dwoma funkcjami ?
  • #8 13369154
    mi14chal
    Poziom 28  
    To nie lepiej użyć dwóch różnych timerów? Co do tego, że biblioteki nie działają jednocześnie używając jednego timera to ciężko coś powiedzieć nie znając żadnych konkretów.
  • #9 13369238
    MrKiel
    Poziom 9  
    Ale to nie jest kwestia tego, czy kod działa czy nie, bo działa, więc nie widzę potrzeby przepinania jednej z bibliotek pod inny zegar. Tutaj bardziej jest kwestia jego czytelności i elegancji. Dlatego interesuje mnie czy ktoś próbował takiego lub podobnego rozwiązania z podmianą ISR. Idealnie uprościło by mi to powiązania pomiędzy projektem i bibliotekami.
  • Pomocny post
    #10 13369279
    tmf
    VIP Zasłużony dla elektroda
    Zastanów się - wektor przerwania jest w pamięci FLASH - jak więc chciałbyś go zmienić? Wymagałoby to przeprogramowania FLASH. Jedyna możliwość to tak jak koledzy pokazali wyżej dodać if i wywołanie kodu. Kod możesz umieścić jako oddzielne funkcje, możesz też w ISR. Możesz użyć do definicji tych funkcji atrybutu always_inline, dzięki czemu kompilator osadzi te funkcje zamiast je wywołać, możesz też zdefiniować ISR z atrybutem flatten i też uzyskasz jeden ciągły kod, zamiast wywołań. A jeśli chcesz automatyczne łączenie funkcji, to użyj tzw. symboli słabych (weak symbols), takowe można redefiniować w kodzie.
  • #11 13369319
    MrKiel
    Poziom 9  
    tmf napisał:
    wektor przerwania jest w pamięci FLASH - jak więc chciałbyś go zmienić?

    Wiem, że to niemożliwe dlatego pisałem o uzyskaniu efektu podobnego do nadpisywania wektora.
    tmf napisał:
    możesz użyć do definicji tych funkcji atrybutu always_inline

    Dziękuję, właśnie coś w tym stylu miałem na myśli. Jestem po pierwszej próbnej kompilacji i kod (assembler) wygląda świetnie.

    EDIT:
    Sprawdziłem dokładniej z różnymi poziomami optymalizacji i wygenerowany kod nie różni się niczym. Jeszcze raz dziękuję !
  • #12 13369972
    Brutus_gsm
    Poziom 25  
    Czy atrybut always_inline nie daje dokładnie tego samego, co wklejenie kodu tych funkcji do procedury obsługi przerwania?
  • #13 13369999
    tadzik85
    Poziom 38  
    Brutus_gsm napisał:
    Czy atrybut always_inline nie daje dokładnie tego samego, co wklejenie kodu tych funkcji do procedury obsługi przerwania?


    Zależy od miejsca deklaracji tychże funkcji.
    Atrybut oznacza jedynie, że inlinowanie ma nastąpić zawsze, nawet gdy globalnie inlinowanie funkcji jest wyłączone (np. optymalizacja o0).
  • #14 13370101
    tmf
    VIP Zasłużony dla elektroda
    Brutus_gsm napisał:
    Czy atrybut always_inline nie daje dokładnie tego samego, co wklejenie kodu tych funkcji do procedury obsługi przerwania?


    W dużej mierze daje to samo, z wyjątkami o których pisze kolega powyżej. Natomiast trudno w praktyce włączyć kod do ISR, jeśli poszczególne funkcje są w osobnych plikach :) Stąd always_inline ma pewnąprzewagę.
    Ewnetualnie rozważyłbym jednak dodanie atrybutu flatten do ISR, dzięki czemu programista nie będzie musiał pamiętać o umieszczeniu always_inline w deklaracji funkcji, które mają się w ISR znaleźć.
  • #15 13371841
    MrKiel
    Poziom 9  
    Myślę, że należy się podsumowanie. Ponieważ zauważyłem, że mam problem z jasnym formułowaniem myśli, posłużę się przykładem.

    Załóżmy, że mamy biblioteki Lib1, Lib2 i projekt używającego obu.
    Plik nagłówkowy biblioteki Lib1 wygląda następująco :
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Plik źródłowy Lib1 :
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Pliki nagłówkowy biblioteki Lib2:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Plik źródłowy biblioteki Lib2:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Plik main.c projektu używającego obu bibliotek :
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Taka konstrukcja pozwala w łatwy sposób decydować na etapie kompilacji, którą "wersję" obsługi przerwania chcemy użyć. Wybór następuje poprzez makro ISR_ALIAS(wektor, funkcja). Minusem użycia tego makra jest jedna dodatkowa instrukcja RJMP w wygenerowanym kodzie.
    Funkcje xxx_TIM0_OVF() zostały zdefiniowane w każdej bibliotece i projekcie. Wersja zdefiniowana w projekcie wykonuje w zależności od warunku funkcje zdefiniowane w bibliotekach. Ponieważ zostały one zdeklarowane w pliku nagłówkowym (ważne!) z atrybutem always_inline w kodzie wynikowym nie pojawi się ich wywołanie, a jedynie ich kod. Ponieważ w funkcji obsługi przerwania nie ma wywołania nie zostanie ona automatycznie rozszerzona o zapis/odczyt rejestrów co znacząco wpływa na szybkość jej wykonania.
    Jakie są plusy takiego rozwiązania ?
    Przede wszystkim porządek w kodzie poprzez umieszczenie elementów odpowiedzialnych za realizację określonej funkcjonalności w bibliotekach (np. programowy UART). Ułatwia to też dalsze prace nad funkcjami zawartymi w bibliotekach (optymalizacja istniejących, dodawanie nowych) o ile nie zmieni się sposobu ich wywoływania.
    W załączniku dodałem przykładowy projekt Atmel Studio 6.
  • #16 13371989
    el2010tmp
    Poziom 25  
    MrKiel napisał:
    Minusem użycia tego makra jest jedna dodatkowa instrukcja RJMP w wygenerowanym kodzie.


    Jaki jest sens użycia tego makra skoro można napisać:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #17 13372031
    perlon
    Poziom 20  
    No właśnie MrKiel pisał o tym. Nie ma prologu i epilogu funkcji wywoływanej. W efekcie przerwanie wykonuje się szybciej. U ciebie oprócz sprawdzenia warunku dochodzi narzut na wywołanie funkcji publicznych.
  • #18 13372064
    el2010tmp
    Poziom 25  
    perlon napisał:
    U ciebie oprócz sprawdzenia warunku dochodzi narzut na wywołanie funkcji publicznych.

    Funkcje są zdefiniowane jako always_inline więc ich kod jest wklejany w miejscu wywołania.
  • #19 13372469
    tadzik85
    Poziom 38  
    el2010tmp napisał:
    perlon napisał:
    U ciebie oprócz sprawdzenia warunku dochodzi narzut na wywołanie funkcji publicznych.

    Funkcje są zdefiniowane jako always_inline więc ich kod jest wklejany w miejscu wywołania.


    Bzdura, inlinowane są funkcje zdefiniowane jedynie w danej jednostce kompilacji.

    MrKiel. Skoro wybierane funkcje masz w innych bibliotekach, to żaden inline nie zadziała. NIGDY!!

    Jedynym ulepszeniem będzie umieszczenie tych funkcji w pliku nagłówkowym tychże bibliotek jako

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


    Jednak wydaje mi się do to co próbujesz zrobić jest niebezpieczne i wcale nie takie ulepszające.
    Bo skoro 2 biblioteki mają dzielić 1 przerwanie oznacza to, że działa również dane peryferium a jest to błędogenne. W takim przypadku trudno nazwać je bibliotekami a raczej szablonami, ze względu na każdorazową konieczność edytowania.

    Polecam przemyśleć koncepcję. Może lepiej wydzielić cześć wspólną?
  • #20 13372527
    MrKiel
    Poziom 9  
    tadzik85 Spokojnie kolego, to działa. Zwróć uwagę, że funkcje definiuję w plikach nagłówkowych, a nie źródłowych, właśnie po to, żeby wymusić ich obecność w poszczególnych jednostkach kompilacji.
    el2010tmp Można, ale powoduje to niespójność nazw. W mojej propozycji możesz sobie na początku zdefiniować makra określające z której biblioteki będziesz brał domyślnie funkcje obsługi przerwań. Gdy masz jedną taką funkcję - można zmieniać ręcznie. Przy kilku - pokusiłbym się o moje rozwiązanie.
  • #21 13375012
    Konto nie istnieje
    Konto nie istnieje  
  • #22 13375121
    perlon
    Poziom 20  
    _marek napisał:
    Może by tak wywoływać w przerwaniu funkcje poprzez wskaźnik do niej a w programie głównym manipulować adresami tych funkcji.


    Kiedyś to testowałem. Wymyśliłem sobie wołanie call_back z przerwania. Niestety tak wywołane funkcje z natury rzeczy nie mogą być __inline__ występuje narzut na prolog i epilog. Upraszcza się kod w C ale rośnie kaskada wywołań funkcji w efekcia czas wykonania przerwania.
  • #23 13375344
    tmf
    VIP Zasłużony dla elektroda
    Takie wywołanie przez wskaźnik to kompletny koszmar. Inline to nigdy nie będzie bo jak? Przecież wskaźnik z natury może wskazywać na różne funkcje, w efekcie, którą włączyć? To zawsze jest realizowane przez skok pośredni. I tu pojawia się kolejny problem, jeśli robimy to z ISR - kompilator nie wie jakie rejestry będą modyfikowane, bo przecież nie wie jaka funkcja będzie wywołana - konsekwencja - konieczność zachowania prawie całego kontekstu procesora (z wyjątkiem rejestrów call-save zależnych od ABI), czyli 32 rejestrów + dodatkowe rejestry segmentowe (RAMPx). Koszmar.
REKLAMA