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

[atmega...][c]Masakrowanie przerwań - luźne dywagacje...

PO. 31 Maj 2010 20:31 7128 91
  • #1 8139124
    PO.
    Poziom 20  
    Luźne dywagacje to znaczy takie sobie gdybania i rozważania. Czego nie wolno i dlaczego nie wolno, sztuczki i kruczki itp...

    Temat nie tylko dla mnie, mam nadzieję :) .

    Na początek konkretne zapytanie.
    Wywołujemy sobie przerwanie, załóżmy INT1. Wiemy, że dopóki trwa, nie wywoła się żadne inne przerwanie.
    Możemy sobie jednak włączyć globalne zezwolenie (SREG bit I) i wtedy się wywoła dowolne inne a nawet to samo INT1. Czy wywołując kolejne zagnieżdżone iteracje INT1 (np umieszczając w przerwaniu wieczną pętlę :) , wiem, tego też nie wolno) możemy doprawadzić do przepełnienia pamięci? Czy stos jest zrzucany zawsze w to samo miejsce dla danego przerwania czy za każdym razem w inne?

    Czy istnieją jeszcze jakieś sposoby na uruchomienie w c przerwania w przerwaniu poza (SREG bit I).
  • #2 8139193
    m.bartczak
    Poziom 16  
    Hmm, przerwanie to po prostu funkcja, więc zawsze można do niej jakoś skoczyć...

    A jeśli chodzi o stos i przerwania: podczas wywołania przerwania na stos odkładany jest adres instrukcji następującej po tej przy której nastąpiło przerwanie. To co odkładane jest później zależy już od kompilatora (albo programisty)

    A stos jest jeden.
  • #3 8139199
    Konto nie istnieje
    Poziom 1  
  • #4 8139325
    michalko12
    Specjalista - Mikrokontrolery
    Jeśli funkcja ma być wywoływana sama w sobie musi być zadeklarowana jako reentrant, nie może być w niej żadnych zmiennych typu static i nie może tez korzystać z funkcji które nie są reentrant
  • #5 8139401
    tmf
    VIP Zasłużony dla elektroda
    Nie tyle musi być zadeklarowana jako reentrant, bo takiej deklaracji nie ma:) co musi być tak napisana, aby była reentrant. Co do zmiennych statycznych to mogą one być wykorzystywane - w końcu taka jest idea zmiennych statycznych, żeby były dzielone przez wszystkie instancje/wywołania funkcji. Co do innych zmiennych to dopóki się używa zmiennych lokalnych nie ma problemu.
    Generalnie wielokrotne wchodzenie do funkcji obsługi przerwania na tak małych procesorkach nie jest dobrym pomysłem - łatwo o przepełnienie stosu. Proste AVRy nie mają hierarchicznych przerwań więc wiele pokombinować nie można, ale już na ATMedze wejście w procedurę obsługi przerwania blokuje tylko przerwania o niższym priorytecie niż aktualnie obsługiwane. Podobny mechanizm mają "poważniejsze" procesory, w stylu ARM, czy AVR32.
  • #6 8139467
    sulfur
    Poziom 24  
    tmf napisał:
    Proste AVRy nie mają hierarchicznych przerwań więc wiele pokombinować nie można, ale już na ATMedze wejście w procedurę obsługi przerwania blokuje tylko przerwania o niższym priorytecie niż aktualnie obsługiwane. Podobny mechanizm mają "poważniejsze" procesory, w stylu ARM, czy AVR32.


    A możesz podać która ATmega lub od której ATmegi w górę powyższa hierarchia występuje? Mile widziane również źródło, jeśli jesteś w stanie podać z głowy, jeśli nie, nie zadawaj sobie trudu, sam poszukam.
  • #8 8139830
    sulfur
    Poziom 24  
    A szkoda, bo już myślałem, że coś przegapiłem :D
  • #9 8139948
    PO.
    Poziom 20  
    m.bartczak napisał:

    A jeśli chodzi o stos i przerwania: podczas wywołania przerwania na stos odkładany jest adres instrukcji następującej po tej przy której nastąpiło przerwanie. To co odkładane jest później zależy już od kompilatora (albo programisty)

    A stos jest jeden.


    No dobra, a jak zrobić żeby się odkładało to co chcę? I co mogę chcieć :) , biorąc pod uwagę język i kompilator?
  • #10 8140112
    sulfur
    Poziom 24  
    Zadałeś nieco nieprecyzyjne pytanie.

    Jeśli piszesz w C, to obsługę przerwania definiujesz z parametrem ISR_NAKED i wtedy ani prolog, ani epilog nie będą dołączane. Oczywiście o wszystko musisz wtedy zatroszczyć się sam. Jak to działa? Nie wiem, nie próbowałem. Teoretyzuję.
  • #11 8140277
    mirekk36
    Poziom 42  
    PO. --> ciekawy jestem komu może się przydać taka wiedza - "jak zepsuć działanie przerwań?" jeśli się nawet samemu nie wie dokładnie najpierw jak się je obsługuje w C czy w asemblerze. Podejrzewam, że szybciej byś się czegoś nauczył czy dowiedział czegoś ciekawego, gdybyś po kolei czytał i się uczył o C dla AVR albo o asemblerze.

    Tak jak mówił kolega wyżej, zrób sobie przerwanie bez prologu i epilogu a potem coś tam sobie napisz - byle co - nie znając się na asemblerze i tak popsujesz sobie program że nawet nie będziesz wiedział o co chodzi ? - taki jest cel tego wątku ? ;)
  • #12 8140290
    utak3r
    Poziom 25  
    Popieram. Asemblera warto się nauczyć - i nie chodzi wcale o to, żeby coś w nim pisać! Chodzi o to, że w trakcie nauki asemblera danego procesora, poznajemy go od podszewki. A wtedy pisanie w czymkolwiek przychodzi z łątwością, gdy już rozumiemy wszystkie mechanizmy.
  • #13 8140328
    PO.
    Poziom 20  
    Cel tego wątku jest żeby się czegoś dowiedzieć i czegoś nauczyć :) . Tyle się słyszy (czyta) o tym czego "nie wolno" - a czasami wolno tylko po prostu trzeba wiedzieć kiedy.
    Wszystko rozbija się o wiedzę. To nie jest temat "jak zepsuć", wbrew nazwie ;) , tylko temat jakie nietypowe działania zastosować i nie zepsuć.

    Właśnie siedzę nad jednym projektem i zacząłem intensywnie używać przerwań od zdarzeń zewnętrznych, więc tak mi to przyszło do głowy.
    Do tego stopnia sobie poszalałem, że początkowo główna pętla programu była pusta :) ale to nie był dobry pomysł :P .
    Tak sobie eksperymentuję i rozmyślam.

    Dobra, ISR_NAKED - i wtedy będzie używał wszystkiego jak popadnie, nieważnie co było wcześniej w danym rejestrze?
    Jak "zadbać" o resztę, skoro piszę w c i nie mam dostępu do żadnej reszty?

    Dodano po 1 [minuty]:

    m.bartczak napisał:
    Hmm, przerwanie to po prostu funkcja, więc zawsze można do niej jakoś skoczyć...


    PS: jak skoczyć - poza ustawieniem odpowiedniej flagi ręcznie, żeby się wywołało?

    Dodano po 1 [minuty]:

    michalko12 napisał:
    Jeśli funkcja ma być wywoływana sama w sobie musi być zadeklarowana jako reentrant, nie może być w niej żadnych zmiennych typu static i nie może tez korzystać z funkcji które nie są reentrant


    Nie ma żadnych static, parametry przekazuję przez zmienne globalne :) - które mogą być modyfikowane w kilku miejscach jednocześnie i od tego zależy co się dalej dzieje w programie.
  • #14 8140335
    utak3r
    Poziom 25  
    PO. napisał:
    Do tego stopnia sobie poszalałem, że początkowo główna pętla programu była pusta :) ale to nie był dobry pomysł :P .


    No nie.
    Generalnie, w przerwaniu powinno być to, co się powinno wykonywać albo a) bardzo często, albo b) bardzo precyzyjnie (czasowo lub zdarzeniowo).
    Procedura obsługi przerwania powinna być tak krótka (inaczej: tak szybka), jak się tylko da. Jeżeli któraś operacja trwa długo - wywalić ją do głównej pętli programu.

    Przykład: jeżeli np. zliczasz tyknięcia z przerwania zewnętrznego, w przerwaniu umieść zwiększanie jakiejś zmiennej o jeden, natomiast wynikające z tego faktu obliczenia (jakieś mnożenia, itp.) wywal do pętli głównej.
  • #15 8140353
    sulfur
    Poziom 24  
    Skoczyć można bardzo prosto: sprawdzasz gdzie dla danej konfiguracji jest wektor danego przerwania i skaczesz pod dany adres. Z danego adresu jest skok do funcji jego obsługi. Pytanie jest tylko, jakim poleceniem wykonać skok (RJMP/JMP/RCALL/ICALL/CALL czy jeszcze coś innego).

    Skoro używasz ISR_NAKED to przeważnie znaczy, że obsługa przerwania musi być maksymalnie krótka i nie możesz sobie pozwolić na jakiekolwiek zbędne operacje. Obsługa pustego przerwania nie licząc reti zajmuje 18 bajtów i składa się z 9 poleceń! W przypadku użycia ISR_NAKED kompilator nie wstawia nawet reti, musisz to zrobić sam. Jedynce co zapewnia to wpisanie odpowiedniego adresu do wektora przerwań. W ogóle bez optymalizacji obsługa pustego przerwania zajmuje jeszcze więcej.
  • #16 8140367
    PO.
    Poziom 20  
    utak3r napisał:
    PO. napisał:
    Do tego stopnia sobie poszalałem, że początkowo główna pętla programu była pusta :) ale to nie był dobry pomysł :P .


    No nie.
    Generalnie, w przerwaniu powinno być to, co się powinno wykonywać albo a) bardzo często, albo b) bardzo precyzyjnie (czasowo lub zdarzeniowo).
    Procedura obsługi przerwania powinna być tak krótka (inaczej: tak szybka), jak się tylko da. Jeżeli któraś operacja trwa długo - wywalić ją do głównej pętli programu.

    Przykład: jeżeli np. zliczasz tyknięcia z przerwania zewnętrznego, w przerwaniu umieść zwiększanie jakiejś zmiennej o jeden, natomiast wynikające z tego faktu obliczenia (jakieś mnożenia, itp.) wywal do pętli głównej.


    Albo zapętlamy przerwania tak, że się wszystko wzajemnie wywołuje kiedy trzeba. Aż się pamięć skończy i wszystko się wywali :D . Mnie się to nie zdarzyło,
    ale to był prototyp i mało klikałem - natomiast wcześniej doszedłem do wniosku że może się tak stać ;) .

    Albo wiemy kiedy jakie przerwanie się wywoła i wiemy kiedy się skończy (w jakich warunkach) - oraz co jest ważnego albo nieważnego poza przerwaniem. Bo np jest łańcuch zdarzeń, typu w jednym przerwaniu zezwalamy na inne itd...
    To oczywiście nie jest zgodne ze "sztuką" ale to jest właśnie clou tego tematu :) .

    Dodano po 1 [minuty]:

    sulfur napisał:
    Skoczyć można bardzo prosto: sprawdzasz gdzie dla danej konfiguracji jest wektor danego przerwania i skaczesz pod dany adres. Z danego adresu jest skok do funcji jego obsługi. Pytanie jest tylko, jakim poleceniem wykonać skok (RJMP/JMP/RCALL/ICALL/CALL czy jeszcze coś innego).

    Skoro używasz ISR_NAKED to przeważnie znaczy, że obsługa przerwania musi być maksymalnie krótka i nie możesz sobie pozwolić na jakiekolwiek zbędne operacje. Obsługa pustego przerwania nie licząc reti zajmuje 18 bajtów i składa się z 9 poleceń! W przypadku użycia ISR_NAKED kompilator nie wstawia nawet reti, musisz to zrobić sam. Jedynce co zapewnia to wpisanie odpowiedniego adresu do wektora przerwań. W ogóle bez optymalizacji obsługa pustego przerwania zajmuje jeszcze więcej.


    Ciekawe rzeczy piszesz. Tylko to wszystko jako wstawki asemblerowe? Nie chodzi o to, że jestem "na nie" ale czy są jakieś rodzime narzędzia w c do tego?

    PS: w c goto czyli skok do etykiety działa tylko w obrębie jednej funkcji - czyli jak skoczyć gdziekolwiek indziej bez używania asemblera :) ?
  • #17 8140375
    utak3r
    Poziom 25  
    gdybyś zrobił sobie wskaźnik do funkcji, po czym zmienił jego wartość na taką, jak wektor przerwania...
  • #18 8140380
    sulfur
    Poziom 24  
    Ale po co wskaźnik ? Normalnie nie da się wywołać funkcji obsługi przerwania ?
  • #19 8140396
    utak3r
    Poziom 25  
    W zasadzie... to się da :)
  • #20 8140774
    tmf
    VIP Zasłużony dla elektroda
    Da się wywołać procedurę obsługi przerwania poprzez skok do odpowiedniego wektora, ale w C poprawniej będzie po prostu wywołać tą funkcję. W AVR istnieją także inne możliwości - np. przerwania od pinów można generować softwarowo - jak poczytaj w nocie. Wykorzystanie atrybutu naked w procedurze obsługi przerwania właściwie implikuje użycie wstawki assemblerowej - kompilator nie generuje prologu i epilogu, a ty z kolei nie wiesz jakie rejestry zostaną użyte przez kod w c, w dodatku nie masz w c możliwości odłożenia czegoś na stosie i potem zdjęcia (właściwie jest, ale jej użycie może być ryzykowne).
    Piszesz, że masz zmienne globalne, które używasz w procedurach reentrant - zdajesz sobie sprawę, że wszelkie operacje na tych zmiennych muszą być atomowe?
    Ogólnie tak jak ktoś już wcześniej napisał odnoszę wrażenie, że starasz się zepsuć coś co działa i wymyślasz nieistniejące problemy. Pisanie o trickach jest o tyle bez sensu, że dla początkujących to będzie koszmar i spowoduje, że ich programy na 100% się wykrzaczą, a jak już ktoś dojrzeje do tego, żeby sztuczek używać to takie porady do niczego już mu nie będą potrzebne.
  • #21 8140869
    Konto nie istnieje
    Poziom 1  
  • #22 8143583
    zdziwiony
    Poziom 27  
    Z takich sztuczek to jeszcze - po wywołaniu przerwania zdejmujemy ze stosu oryginalny adres powrotu i zapisujemy swój i procek po zakończeniu przerwania wraca tam gdzie chcemy, ale to raczej tylko w asemblerze.Podobny trik można zastosować do podprogramów ( call ret ).
  • #23 8143708
    mirekk36
    Poziom 42  
    zdziwiony napisał:
    Z takich sztuczek to jeszcze - po wywołaniu przerwania zdejmujemy ze stosu oryginalny adres powrotu i zapisujemy swój i procek po zakończeniu przerwania wraca tam gdzie chcemy, ale to raczej tylko w asemblerze.Podobny trik można zastosować do podprogramów ( call ret ).


    Bardzo przepraszam za określenie ale to nie żadne sztuczki czy triki tylko głupota w najczystszej formie szczególnie w aspekcie języka C i wstawek asemblerowych.

    Podmiana adresów powrotów z poleceń typu call to mniej więcej to samo co pisanie całego programu w Basicu przy użyciu jedynie poleceń GOTO zamiast podprogramów, procedur, funkcji itp itd itp

    Ale o ile to powyżej można w pewnych szczególnych przypadkach zaakceptować czy czasem użyć - to i tak nie należy to do żadnych tricków. Zostawcie panowie tricki dla Dejwida Koperfilda i spróbujcie się uczyć od przodu a nie od tyłu ;)

    .... Jednak już podmiana poprzez stos adresu powrotu z procedur obsługi przerwań to kolejna masakra i godny pożałowania przykład pseudo sztuczek nawet w czystym asemblerze. Pokazuje tylko totalny brak zrozumienia tematu.

    Więc "karmienie" się na forum takimi pomysłami wśród ludzi nie mających jeszcze dużego pojęcia o programowaniu w jakimkolwiek języku, to już całkowita fantasmagoria.
  • #24 8143899
    tmf
    VIP Zasłużony dla elektroda
    Jakkolwiek zgadzam się z tym co napisał Mirek to jak od każdego ogólnego stwierdzenia, także tu są wyjątki. Taka podmiana adresu powrotu ma sens w przypadku pisania OS do wywłaszczenia wątku i zmiany na inny. Ale oczywiście to pewien wyjątek potwierdzający ogólną zasadę - to głupota w czystej formie :) No i w tym przypadku procedura obsługi przerwania robi się tak naprawdę odpowiednikiem pętli głównej programu, a sam program - wątki - procedurami obsługi przerwań :) Ale myślę, że nie ma sensu dalej w to brnąć, bo możemy tylko zamieszać, zamiast coś wyjaśnić.
  • #25 8144190
    zdziwiony
    Poziom 27  
    Zgadzam się z szanownymi przedmówcami że większego sensu to nie ma ale jak ktoś wie co robi i jakie są efekty...
    Nie zawsze chcemy żeby kod był jasny i zrozumiały dla wszystkich ale to takie moje skrzywienie z czasów C64 kiedy to jeszcze stosowało się kod samo modyfikowalny no ale na AVR się nie da.:cry:
    No a poza tym autor wątku chciał sztuczki.:D
  • #26 8144696
    PO.
    Poziom 20  
    I to mi się podoba, ostatnio myślałem, że by mi się przydało w jednym miejscu. Można poprosić o jakieś odnośniki, gdzie poczytać dalej o podmianie adresu powrotu?

    Wiem a umiem to dwie różne kwestie. Nie rozwijajmy tego aspektu ;) bo jednemu umiejętność przyjdzie jak pozna narzędzia (wiedzę) a drugemu nie...

    Atomowe to znaczy inkrementacja, dekrementacja, przesunięcia bitowe, zerowanie, podstawienie wartości. Coś jeszcze?

    Zgadza się, nie wolno mnóstwa rzeczy - ale dobrze wiedzeć czego i dlaczego. Pewne formy i zasady programowania zalecane są bo kod jest przejrzysty, bo łatwiej i lepiej kompilator optymalizuje, bo przeciętny programsta nie zrobi jakejś głupoty. To wszystko nie znaczy, że mam się odciąć od tego i nie próbować ;) .
  • #27 8144856
    tmf
    VIP Zasłużony dla elektroda
    Jeśli chcesz podmienić adres powrotu to praktycznie procedura musi być naked, bo musisz napisać swój epilog - adres powrotu jest najniżej, więc trzeba najpierw wszystko pozdejmować, ew. w prologu zapamiętać gdzie jest adres powrotu. Żeby móc całą procedurę ciągle napisać w C najłatwiej jest wtedy na stos odłożyć wszystkie rejestry mogące być zmodyfikowane przez procedurę... czyli robi się to trochę nieefektywne, chociaż strata kilku cykli przy procedurze trwającej kilkaset zapewne nie jest dotkliwa, szczególnie jeśli robisz task switching z koniecznym przełączeniem kontekstu procesora.
    Co do atomowości to zapewnić trzeba niestety nie tylko atomowość procedur arytmetycznych, ale także pewnych bloków programu, co już jest bolesne. Np. If(counter) counter--; musi być wykonane atomowo - nie wystarczy, że sama dekrementacja counter będzie atomowa. Trzeba pamiętać, że problemem staje się wiele funkcji bibliotecznych - np. free, malloc itd.
  • #28 8144915
    utak3r
    Poziom 25  
    mirekk36 napisał:
    ...[ciach]... to mniej więcej to samo co pisanie całego programu w Basicu przy użyciu jedynie poleceń GOTO ...[ciach]...


    Gadasz... nie tak dawno na forum widziałem program napisany w C, którego pętla główna została zrealizowana przy pomocy... labelki i goto :D
  • #29 8145633
    zdziwiony
    Poziom 27  
    Osobiście nie za bardzo widzę taki kod w C.Tak jak mirekk36 pisał w C takie coś to proszenie się o kłopoty.Ten numer przejdzie raczej tylko programie pisanym w asemblerze.A jak to zrobić? No tutaj muszę zdradzić straszne tajemnice ( AVRStudio datasheet jakiegoś AVR jakaś książka , net i trening i jeszcze raz trening... )
  • #30 8145760
    mirekk36
    Poziom 42  
    zdziwiony napisał:
    ....A jak to zrobić? No tutaj muszę zdradzić straszne tajemnice ( AVRStudio datasheet jakiegoś AVR jakaś książka , net i trening i jeszcze raz trening... )


    Nooo nareszcie na prawdę dobra i porządna porada w takim temacie ;) przyłączam się do tego nogami i rękami, żeby takie sztuczki i triki zdradzać a w szczególności początkującym, którzy próbują się uczyć "od tyłka strony"
REKLAMA