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

Przerwania proste i przyjemne - część 1 - dobre praktyki programowania

ghost666 19 Lut 2022 21:16 2955 11
  • Każda aplikacja osadzona na ogół zawiera w sobie zestaw funkcji. I nawet najprostsza, na przykład do regulowania temperatury, obejmuje szereg zadań, takich jak odczytywanie danych wejściowych od użytkownika lub z czujnika temperatury/wejścia ADC, wyświetlanie zasobów na wyświetlaczu LCD i sterowanie wyjściem zarządzającym pracą wentylatora/grzałki itp. Przepustowość sterownika musi być podzielona między wszystkie te funkcje w taki sposób, aby użytkownikowi końcowemu wydawało się, że są wykonywane równolegle. Zaprojektowanie tego obejmuje określenie działań w tle – tj. głównego procesu kontrolera – i zaniechanie pracy w regularnych odstępach czasu dla wszystkich innych zadań. Należy zauważyć, że mogą występować również przerwania asynchroniczne, na przykład, w czasie gdy master próbuje się komunikować ze sterownikiem podrzędnym w razie potrzeby. Właściwa obsługa danego procesu staje się zatem zadaniem krytycznym.

    Przerwania muszą być traktowane ostrożnie i rozważnie, głównie dlatego, że niedbale rozpisana ich obsługa może prowadzić do występowania bardzo tajemniczych błędów w czasie wykonywania. Usterki te są trudne do wykrycia i zrozumienia, ponieważ kontroler może wejść w niezdefiniowany stan, zgłosić nieprawidłowe dane, zatrzymać się, zresetować lub zachować w inny, niezrozumiały dla programisty sposób. Znajomość kilku prostych praktyk obsługi przerwań może pomóc w zapobieganiu takim zdarzeniom.

    W tej serii artykułów omawiamy wraz z odpowiednimi przykładami następujące proste, ale ważne modele użytkowe i tematy pomagające zapobiegać danym błędom, a dotyczące procesowania przerwań, w tym:

    * Jak wybrać główny proces;
    * Właściwe ustalanie priorytetu przerwań;
    * Niech będą krótkie — używaj flag;
    * Zachowaj prostotę — używaj maszyn stanu;
    * Zmienne globalne — monitorowanie, kiedy są modyfikowane;
    * Zmienne lokalne — poznaj swój kompilator;
    * Korzystanie z buforów danych — uważaj na przepełnienia;
    * Pamięć współdzielona — odczytanie kompletne od razu;
    * Trochę więcej o buforach;
    * Bufory wielobajtowe;
    * Bufory strukturalne — poznaj dopełnienie struktury;
    * Wywoływanie funkcji w ISR — bądź ostrożny;
    * Zadania krytyczne czasowo — zrozum opóźnienia;
    * Przerwanie LVD — spraw, aby blokowały.

    Jak wybrać główny proces

    Chociaż brzmi to łatwo, jest jednak istotne i dosyć złożone. Gdy kontroler ma wiele zadań do wykonania należy zrozumieć, że funkcja „main()” jest tylko procesem w tle. Ma również najniższy priorytet w tym sensie, że każde zaniechanie może zakłócić jej pracę, powodując, że procesor uruchomi procedurę przerwania, a nie główny proces.

    Na przykład, można skanować klawiaturę matrycową w tle, gdzie opóźnienia spowodowane przez wszystkie inne przerwania razem wzięte są mniejsze niż szacowany czas przytrzymania klawisza przez użytkownika. Podczas gdy awaryjny przełącznik STOP musi być obsługiwany przerwaniem.

    Właściwie ustal priorytety przerwań

    Określenie kolejności wykonywania przerwań jest ważne, na przykład, w sytuacji, gdy dwa lub więcej danych procesów występuje jednocześnie. Tutaj ważność, pilność i częstotliwość zadań decydują o priorytecie.

    Rozważmy również system ze sterownikiem wykorzystującym przetwornik cyfrowo-analogowy (DAC) i urządzenie podrzędne I²C. Możliwe są dwa scenariusze:

    * Kontroler użytkuje przetwornik cyfrowo-analogowy do wyprowadzania przebiegu o stałej częstotliwości, podczas gdy master I²C komunikuje się niezależnie ze sterownikiem. W takim przypadku przetwornik cyfrowo-analogowy musi być aktualizowany w stałych odstępach czasu, aby kształt fali wyjściowej pozostał taki sam, niezależnie od tego, czy kontroler komunikuje się z masterem I²C, czy nie. Dlatego DAC musi mieć wyższy priorytet niż przerwanie pochodzące od I²C.
    * Kontroler wykorzystuje przetwornik cyfrowo-analogowy do wyprowadzania przebiegu o zmiennej częstotliwości, przy czym częstotliwość jest ustalana przez mastera I²C. W takim przypadku przerwanie pochodzące od I²C może mieć priorytet, ponieważ samo taktowanie wyjścia DAC jest kontrolowane przez polecenia z I²C.

    Często występujące przerwania powinny mieć wyższy priorytet, aby wszystkie żądania były przetwarzane. W przeciwnym ujęciu istnieje możliwość, że wiele żądań przerwań nagromadzi się i spowoduje obsłużenie wydarzenia tylko raz. Może się to zdarzyć, jeśli drugie, trzecie lub kilka przerwań wystąpi przed podjęciem pierwszej dyrektywy.

    Niech będą krótkie – używaj flag

    Dobrze zrozumianą i często podkreślaną poprawną praktyką przy implementacji obsługi przerwania jest to, że kod danego procesu powinien być jak najkrótszy. Gwarantuje to, że procesor może powrócić do głównego zadania w możliwie szybkim czasie.

    Procedura obsługi przerwań powinna wykonywać tylko kod krytyczny; resztę zadania można przenieść do procesu głównego, ustawiając zmienne flagi. Warto zauważyć, że ponieważ flagi zazwyczaj przyjmują wartości binarne (0 lub 1), powinny one być deklarowane w pamięci bitowej, gdy tylko jest to możliwe (jak w układach 8051). Zmniejsza to narzut na push/pop i czas przetwarzania przerwania. Oto przykład:

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


    Zachowaj prostotę – używaj maszyn stanu

    Maszyny stanu sprawiają, że pozornie duże procedury są krótkie i proste do wykonania. Istnieje wiele przypadków, w których decyzje muszą być realizowane wewnątrz procedury obsługi przerwań (ISR), a funkcja wykonywana przez ISR zależy od stanu aplikacji przed wyzwoleniem przerwania. Rozważmy następujący hipotetyczny przypadek, na przykład, gdzie ISR timera ma być zaimplementowany w taki sposób, aby generować odcinki czasu o długości: 10 ms, 14 ms, 19 ms, przy czym po kolei, a następnie cykl powinien być kontynuowany. Prosty sposób na zmianę okresu wewnątrz ISR może wyglądać następująco:

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


    Należy zwrócić uwagę, że dobrą praktyką w kodowaniu w C jest posiadanie instrukcji default w każdym przypadku wykorzystania switch. Pomaga to w łatwym odzyskaniu panowania nad procesorem po sytuacji wejścia do dowolnego niezdefiniowanego stanu.

    W niektórych scenariuszach obejmujących bardzo mało stanów (2 do 3), konstrukcje: „if-else” mogą generować krótszy kod. Jednak w odniesieniu do większych maszyn stanu, konstrukcje „if-else” odpowiadają za znacznie pokaźniejszą listę wytycznych niż implementacja tabeli skoku formułowana przez instrukcję switch. Dlatego ogólną zasadą dla kodu o dowolnej złożoności jest użycie instrukcji switch, aby zachować elastyczność systemu.

    Powyżej omówiono kilka podstawowych praktyk obsługi przerwań, które pomogą w zestawieniu właściwej struktury ISR-ów. W następnej części tego artykułu przedstawione zostaną aspekty ISR odnoszące się do pamięci. Przyjrzymy się implikacjom wynikającym ze zbyt liberalnego używania zmiennych globalnych/lokalnych, buforów danych, pamięci współdzielonej itp. Poruszone zostaną również zagadnienia związane z opóźnieniami w przerwaniach, następstwa wywołania zewnętrznych funkcji C wewnątrz ISR. A także przerwania LVD (wykrywanie niskiego napięcia).

    Źródło: https://www.embedded.com/interrupts-short-and-simple-part-1-good-programming-practices/

    Fajne? Ranking DIY
    O autorze
    ghost666
    Tłumacz Redaktor
    Offline 
    Fizyk z wykształcenia. Po zrobieniu doktoratu i dwóch latach pracy na uczelni, przeszedł do sektora prywatnego, gdzie zajmuje się projektowaniem urządzeń elektronicznych i programowaniem. Od 2003 roku na forum Elektroda.pl, od 2008 roku członek zespołu redakcyjnego.
    https://twitter.com/Moonstreet_Labs
    ghost666 napisał 11960 postów o ocenie 10197, pomógł 157 razy. Mieszka w mieście Warszawa. Jest z nami od 2003 roku.
  • #2 19894347
    khoam
    Poziom 42  
    ghost666 napisał:
    #pragma interrupt_handler ISR

    Dla jakiego kompilatora i MCU ten kod jest napisany? :)
  • #3 19894432
    inot
    Poziom 35  
    khoam napisał:
    Dla jakiego kompilatora i MCU ten kod jest


    Np. ICC11 compiler dla mikrokontrolerów firmy Freescale (Motorola).
  • #4 19895274
    _lazor_
    Moderator Projektowanie
    inot napisał:
    Np. ICC11 compiler dla mikrokontrolerów firmy Freescale (Motorola).


    freescale został wykupiony przez NXP i pod tym szyldem można od już długiego czasu zakupić układy.



    Ogólnie to źródło jest dość stare i wymagało by odświeżenia. Przede wszystkim w embedded coraz bardziej powinno się uciekać od... kodu. Rozwój hardware spowodował że wiele funkcjonalności można zrealizować właśnie poprzez konfigurację odpowiedniego hardware.
    Jest tutaj przykład ADC, aktualnie na wielu nowszych sprzętach (ale i na starszych pewnie też) można skonfigurować ADC, DMA oraz kontroler UART aby próbki były przerzucane z ADC do pamięci, a przy odpowiedniej ilości próbek wysyłane po UART np do większej maszyny zbierającej te dane i przetwarzające.

    lub to:
    ghost666 napisał:
    Kontroler wykorzystuje przetwornik cyfrowo-analogowy do wyprowadzania przebiegu o zmiennej częstotliwości, przy czym częstotliwość jest ustalana przez mastera I²C. W takim przypadku przerwanie pochodzące od I²C może mieć priorytet, ponieważ samo taktowanie wyjścia DAC jest kontrolowane przez polecenia z I²C.


    Można spokojnie dzisiaj zrealizować przez kontroler I²C oraz DMA, który będzie wysyłać wartość bezpośrednio do DAC

    Inną sprawą że wygląda mi tutaj na sprzęt który miał tylko kilka wejść z przerwania (coś jak w cortex-A) przez co wymagało to maszyny stanów. Dzisiaj np cortex-m posiada wektor przerwań, dzięki czemu mamy "maszynę stanu" zrealizowaną w samym sprzęcie, przez co przerwanie z konkretnego źródła będzie odpalać odpowiedni kod.


    Materiał ok, ale trzeba mieć na uwadze, że się trochę zestarzał i są dzisiaj lepsze rozwiązania.
  • #5 19895400
    Marek_Skalski
    VIP Zasłużony dla elektroda
    _lazor_ napisał:
    Inną sprawą że wygląda mi tutaj na sprzęt który miał tylko kilka wejść z przerwania (coś jak w cortex-A) przez co wymagało to maszyny stanów.
    Nie, to było pisane pod kątem C51. Stąd też zalecenie umieszczenia flag bitowych w pamięci bitowej. Oryginał został opublikowany gdzieś w październiku 2012, a wtedy jeszcze C51 i AVR królowały. ARM-Cortex M3 dopiero wchodziły na rynek. Wszystkie artykuły odnoszą się do struktur 8-bitowych.

    A ten fragment, to jest klasyczna głupota początkujących programistów-ekstremistów oszczędzających czas procesora w przerwaniu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Przecież wystarczy sprawdzić flagę źródła przerwania przez polling, co będzie tak samo skuteczne w sensie czasu, a nie będzie zbędnego wejścia i wyjścia z ISR, przerzucania danych na stos i nie będzie tej nieszczęsnej zmiennej globalnej zadeklarowanej jako bit flag.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Kolejne części są niewiele lepsze. Ot, zwykłe podstawy napisane przez studenta, który chciał się podzielić swoimi przemyśleniami, albo wręcz skopiował notatki wykładowcy. Szału nie ma.
  • #6 19895501
    _lazor_
    Moderator Projektowanie
    Marek_Skalski napisał:
    Przecież wystarczy sprawdzić flagę źródła przerwania przez polling, co będzie tak samo skuteczne w sensie czasu, a nie będzie zbędnego wejścia i wyjścia z ISR, przerzucania danych na stos i nie będzie tej nieszczęsnej zmiennej globalnej zadeklarowanej jako bit flag.


    Pamiętaj że każdy procek będzie miał inaczej zrealizowane przerwania, cortex-m zrzuca do rejestry automagicznie, cortex-A wymaga już zrobienia tego ręcznie. C51 nie znam, ale możliwe że tam tez przerwanie mogło być szybkie bez zrzucania rejestrów na stos.

    Marek_Skalski napisał:
    Przecież wystarczy sprawdzić flagę źródła przerwania przez polling, co będzie tak samo skuteczne w sensie czasu, a nie będzie zbędnego wejścia i wyjścia z ISR, przerzucania danych na stos i nie będzie tej nieszczęsnej zmiennej globalnej zadeklarowanej jako bit flag.


    A jeśli robisz w userlandzie i nie masz dostępu do rejestrów z poziomu main a przerwania tak? Bodajże nawet cortex-m4 zmienia stan Privileg w przerwaniu.

    Takie cofnięcie się w czasie też jest ciekawe bo przedstawia problemy i rozwiązania które albo dziś są pomijane/niewystępują.
  • #7 19895785
    Marek_Skalski
    VIP Zasłużony dla elektroda
    @_lazor_ Dlaczego patrzysz przez pryzmat architektury, którą znasz, a nie odnosisz się do artykułu? Wszystkie 3 części dotyczą prostych struktur 8-bitowych jakie były na rynku 10-20 lat temu, gdzie jedynym efektem przejścia do obsługi przerwania jest załadowanie licznika rozkazów adresem ISR i ustawienie flagi obsługi przerwania.
    _lazor_ napisał:
    A jeśli robisz w userlandzie i nie masz dostępu do rejestrów z poziomu main a przerwania tak?
    Wtedy nadal uważam, że działanie polegające na ustawieniu flagi w ISR jest głupotą, ponieważ w reakcji na przerwanie trzeba wykonać działania na rejestrach HW, choćby kasowanie flagi. Funkcja obsługi przerwania powinna je obsługiwać w sposób kompleksowy, a nie tylko informować aplikację, że coś się wydarzyło i trzeba zareagować, ponieważ przestaje to realizować podstawową funkcję jaką jest przerwanie działania programu i reakcja na zdarzenie.
  • #8 19895934
    _lazor_
    Moderator Projektowanie
    Marek_Skalski napisał:
    @lazor1 Dlaczego patrzysz przez pryzmat architektury, którą znasz, a nie odnosisz się do artykułu? Wszystkie 3 części dotyczą prostych struktur 8-bitowych jakie były na rynku 10-20 lat temu, gdzie jedynym efektem przejścia do obsługi przerwania jest załadowanie licznika rozkazów adresem ISR i ustawienie flagi obsługi przerwania.


    Gdyż nie piszę na jedną architekturę i każde rozwiązanie software będzie inne od tego na jaką maszynę się pisze. Cóż konkretnej architektury tutaj nie ma omówionej, więc do kodu podchodzę ogólnie. Ciężko mówić czy coś jest dobre/złe bez szerszego kontekstu.
    Jeśli ograniczymy to np do cortex-m to odpowiem, tak masz rację. Takie rozwiązanie jest bez sensu.

    Zaznaczam że 8bitowe procesory nadal dominują na rynku, a że w nich nie piszemy to cóż może nasze szczęście, albo i nie.
  • #9 19896809
    kekon
    Poziom 19  
    _lazor_ napisał:
    Pamiętaj że każdy procek będzie miał inaczej zrealizowane przerwania, cortex-m zrzuca do rejestry automagicznie, cortex-A wymaga już zrobienia tego ręcznie. C51 nie znam, ale możliwe że tam tez przerwanie mogło być szybkie bez zrzucania rejestrów na stos.


    Tylko kilka rejestrów jest odkładanych na stos automatycznie; reszta - jeśli jest to konieczne - musi być odłożona "ręcznie".

    _lazor_ napisał:
    Zaznaczam że 8bitowe procesory nadal dominują na rynku, a że w nich nie piszemy to cóż może nasze szczęście, albo i nie


    To się już zmienia ponieważ ceny procesorów 32-bitowych są już nawet niższe niż 8-bitowych. Np. mikrokontrolery STM32 bywają już tańsze niż AVR.
    Architektura C51 jest już przestarzała (choć nadal używana), nieprzystosowana do kompilatorów języków wysokiego poziomu.
  • #10 19896853
    _lazor_
    Moderator Projektowanie
    kekon napisał:
    To się już zmienia ponieważ ceny procesorów 32-bitowych są już nawet niższe niż 8-bitowych. Np. mikrokontrolery STM32 bywają już tańsze niż AVR.


    Tylko AVR nie jest znaczącym 8bitowym układem. Podejrzewam że nadal króluje 8051 w produkcji, co też jeden z naszych użytkowników często pokazuje co układy za kilka centów potrafi.
  • #11 19897049
    jvoytech
    Poziom 21  
    dla 8051 kilka lat temu przedawniły się patenty, więc ci co nie chcą płacić za licencje ISA mogą ją sobie zaimplementować bez dodatkowych opłat we własnych scalakach. Zdaje się, że w Polsce jest nawet jakaś firma (DTD?) co projektuje nadal te CPU i sprzedaje kod źródłowy (verilog?,vhdl?).
  • #12 19900967
    kekon
    Poziom 19  
    _lazor_ napisał:
    Podejrzewam że nadal króluje 8051 w produkcji, co też jeden z naszych użytkowników często pokazuje co układy za kilka centów potrafi.


    Niekoniecznie. W sprzęcie RTV/AGD często używane są mikrokontrolery Renesas, NEC, STM8, MSP430. Wątpię aby takie urządzenia jak np. ekspresy do kawy z dotykowym wyświetlaczem TFT były obsługiwane przez procek 8051 - no chyba że są wersje z FLASH 1MB i RAM przynajmniej z 256K ale takich nie znalazłem.
    Kolejna sprawa to użycie popularnych mikrokontrolerów pod inną nazwą i z zupełnie innym rozkładem wyprowadzeń. Opisu takich układów próżno szukać w internecie, są one dostępne tylko dla firm używających tych układów. Ma to na celu zabezpieczenie przed kopiowaniem. Jeśli produkujesz jakieś urządzenie w dużych ilościach to można zamówić np. w firmie ST znane ich mikrokontrolery pod innym oznaczeniem i z innymi wyprowadzeniami - oczywiście to będzie dodatkowo kosztować ale wtedy raczej nikt nie skopiuje tego urządzenia a firma produkująca te mikrokontrolery sprzeda je wyłącznie Tobie.

    Cytat:
    Tylko AVR nie jest znaczącym 8bitowym układem


    Moja lutownica ERSA i-CON2 ma w środku AVRa, więc niekoniecznie ;)
    Również aktywne obciążenie firmy Array typu 3721, które używam w pracy również ma AVR (z ciekawości obydwa urządzenia właśnie otworzyłem)

Podsumowanie tematu

Dyskusja koncentruje się na praktykach programowania związanych z obsługą przerwań w aplikacjach osadzonych, szczególnie w kontekście mikrokontrolerów. Uczestnicy wymieniają różne architektury, takie jak C51, ARM Cortex-M oraz Cortex-A, podkreślając różnice w obsłudze przerwań i rejestrów. Zwracają uwagę na ewolucję sprzętu, wskazując na rosnącą popularność mikrokontrolerów 32-bitowych, takich jak STM32, w porównaniu do starszych 8-bitowych architektur, jak 8051. Wskazują również na znaczenie efektywnej obsługi przerwań, unikając zbędnych operacji w ISR oraz na konieczność dostosowania kodu do konkretnej architektury sprzętowej.
Podsumowanie wygenerowane przez model językowy.
REKLAMA