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

Programowanie systemów wbudowanych: architektura pierwszy plan - tło („superloop”)

ghost666 26 Kwi 2023 04:22 1056 8
  • Poniższy artykuł poświęcono opisowi architektury i projektowania oprogramowania wbudowanego zgodnie z tzw. architekturą pierwszego planu i tła, znaną również jako: „superloop” lub „main+ISR”. To podstawowe ujęcie jest bardzo ważne i ułatwia zrozumienie wszystkich innych, bardziej zaawansowanych, takich jak systemy operacyjnego czasu rzeczywistego (RTOS).

    Jak sama nazwa wskazuje, architektura składa się z dwóch głównych części: nieskończonej pętli while(True){…} w tle, wewnątrz funkcji main() oraz procedur obsługi przerwań (ISR) obejmujących działania na: „pierwszym planie”. Przerwania, operujące na pierwszym planie (np. SysTick_Handler()), wywłaszczają pętlę funkcjonującą w tle, ale zawsze wracają do punktu wywłaszczania. Dwie części systemu komunikują się za pośrednictwem wspólnych zmiennych (np. l_tickCtr). Aby uniknąć występowania tzw. wyścigu w dostępie do zmiennych, spowodowanego asynchronicznym uruchamianiem przerwań, wspólne muszą być chronione przez krótkie wyłączanie przerwań, związanych z akcesem do nich, gdy są one modyfikowane.





    Blokowanie pętli w tle

    Pętla tła może być ustrukturyzowana na wiele sposobów. Opcja: „Blinky” pokazana na początku wklejonego powyżej filmu demonstruje najprostszą strukturę sekwencyjną (używaną również w przykładzie dla Arduino Blink). Implementacja opiera się na funkcji BSP_delay(), której jedynym celem jest blokowanie pętli w tle (zapobieganie jej działaniu dalej). Taka struktura kodu nazywana jest sekwencyjną, ponieważ porządek oczekiwanych zdarzeń jest zakodowany na stałe w wywołaniach blokujących, z których każde wyraźnie czeka na określone zdarzenie. Np. po włączeniu diody wywołanie blokujące BSP_delay(BSP_TICKS_PER_SEC/4) oczekuje na zdarzenie timeout za 1/4 sekundy, a po jej wyłączeniu kolejne BSP_delay(BSP_TICKS_PER_SEC*3/4) wyczekuje na przekroczenie czasu przez następne 3/4 sekundy.

    Kod sekwencyjny jest prosty, ale trudno go rozszerzyć. Pętla w tle zapchana wywołaniami blokującymi wykonuje się powoli (tylko raz na sekundę w przypadku Blinky). W konsekwencji żadne zdarzenie wymagające szybszej reakcji nie może zostać obsłużone w odpowiednim czasie.

    Nieblokująca pętla w tle

    Jednak możliwy jest również inny rodzaj ustrukturyzowania, co pokazano w przykładzie na końcu filmu. W takim przypadku pętla w tle stale sprawdza zdarzenia przekroczenia limitu czasu w zależności od bieżącego stanu diody LED (w materiale ON_STATE lub OFF_STATE). Dopiero po wykryciu odpowiedniego timeout, pętla wykonuje adekwatne akcje, takie jak włączenie lub wyłączenie diody LED, ponownie w zależności od stanu. Taka nieblokująca struktura kodu jest nazywana w filmie sterowaną zdarzeniami.

    Najważniejszą zaletą kodu ww. rodzaju jest rozszerzalność, ponieważ pętla zdarzeń może obsłużyć dowolną ich sekwencję. Ceną za to jest to, że kod sterowany zdarzeniami jest znacznie mniej czytelny niż ten sekwencyjny, jako że oczekiwany porządek nie jest już łatwo widoczny (brak wywołań blokujących). Równie ważne jest to, że nieblokująca pętla tła działa bardzo szybko (kilka tysięcy razy na sekundę w przypadku omawianego tutaj i powyżej przykładu). Dzięki temu bez problemu poradzi sobie z wszelkimi nowymi zdarzeniami, także tymi wymagającymi dynamicznej reakcji.

    Uwagi

    W pełni blokujące i całkowicie nieblokujące pętle tła tworzą całe spektrum architektur pierwszego planu i tła. W praktyce, realne mieszczą się gdzieś pomiędzy tymi dwoma ideami. Typowa pętla w tle często zaczyna się od prostej struktury sekwencyjnej. Jednak w miarę dodawania nowych zdarzeń staje się coraz bardziej nimi sterowana, a czas jej trwania musi zostać skrócony. Nierzadko kończy się to najgorszym z obu światów — sekwencja zdarzeń staje się niewidoczna, ale program też nie do końca jest łatworozszerzalny, z powodu: „zatkania” przepływu sporadycznymi wywołaniami blokującymi. Próby zapobieżenia takiemu rozpadowi architektury wiodą w dwóch kierunkach: wysiłki mające na celu uratowanie struktury sekwencyjnej prowadzą do powstania Systemu Operacyjnego Czasu Rzeczywistego (RTOS), który omówić trzeba już zupełnie osobno. Starania na rzecz zapobiegania zbytniemu komplikowaniu kodu, wynikającemu z improwizowanego podejścia systemu: „sterowanego zdarzeniami”, zmierzają do programowania sterowanego zdarzeniami, maszyn stanowych i obiektów aktywnych, co również należy wyjaśnić oddzielnie.

    Źródło: https://www.embedded.com/programming-embedded-systems-foreground-background-architecture-superloop/

    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 20555910
    Mlody_Zdolny
    Poziom 30  
    Szersze omówienie szczegółów wzorca typu maszyna stanów jest jak najbardziej przydatne. To jest już inny poziom programowania od tych przykładów w szeroko dostępnych tutorialach z delayami.
    Dla amatora czy w prostych projektach to najlepszy sposób aby zwiększyć performance programu i wycisnąć z procka ile się tylko da.
    Jednak odmienny i być może nieco zagmatwany szkielet tego typu programów odstrasza początkujących i nieco bardziej zaawansowanych, którzy utknęli w swojej strefie komfortu z inf loop i przerwaniami, bez warstwy pośredniej, którą stanowią zdarzenia i stany.
  • #3 20555913
    LightOfWinter
    Poziom 38  
    Mlody_Zdolny napisał:
    Dla amatora czy w prostych projektach to najlepszy sposób aby zwiększyć performance programu i wycisnąć z procka ile się tylko da.
    Jednak odmienny i być może nieco zagmatwany szkielet tego typu programów odstrasza początkujących i nieco bardziej zaawansowanych, którzy utknęli w swojej strefie komfortu z inf loop i przerwaniami, bez warstwy pośredniej, którą stanowią zdarzenia i stany.


    Witam,
    Zupełnie mnie to nie przekonało. Uważam że warto stosować takie podejścia jak również dużo bardziej zaawansowane nawet w prywatnych projektach bo raz że "dostajemy wydajność za darmo" a dwa że poznajemy lepsze techniki zarządzania zasobami.
  • #4 20555928
    Mlody_Zdolny
    Poziom 30  
    Dodam jeszcze, że programy napisane jako maszyna stanów bardzo dobrze testuje się poza środowiskiem targetowym i z pomocą innych języków programowania.
    Wystarczy dobrze odizolować strukturę SM od hardware'u, w tym od ISR i można testować np za pomocą ulubionego języka wysokiego poziomu, np Pythona program na MCU napisany w C. Oczywiście, nie obejdzie się bez użycia #define w przypadku różnic między kompilatorami C na MCU i na PC.

    LightOfWinter napisał:
    Zupełnie mnie to nie przekonało. Uważam że warto stosować takie podejścia jak również dużo bardziej zaawansowane nawet w prywatnych projektach

    Skoro jesteś zwolennikiem tego typu podejścia to chyba znaczy, że już jesteś przekonany, logicznie patrząc :)
  • #5 20555976
    khoam
    Poziom 42  
    LightOfWinter napisał:
    bo raz że "dostajemy wydajność za darmo" a dwa że poznajemy lepsze techniki zarządzania zasobami.

    Kontrolowanie dostępu do współdzielonych zasobów przez "krótkie" wyłączanie przerwań to moim zdaniem kiepski pomysł, ale w tym omawianym przypadku inaczej się nie da.

    Dodano po 3 [minuty]:

    Mlody_Zdolny napisał:
    Dodam jeszcze, że programy napisane jako maszyna stanów bardzo dobrze testuje się poza środowiskiem targetowym i z pomocą innych języków programowania.

    Zwykle też taka maszyna stanów w środowisku "pozatargetowym" nie wykazuje tych błędów, które później przytrafiają się środowisku rzeczywistym embedded.
  • #6 20556356
    pixel7
    Poziom 23  
    khoam napisał:
    Kontrolowanie dostępu do współdzielonych zasobów przez "krótkie" wyłączanie przerwań to moim zdaniem kiepski pomysł, ale w tym omawianym przypadku inaczej się nie da.

    Szczególnie gdy zasoby są modyfikowane przez różne mechanizmy, bez dokładnego sprawdzania czy wszystkie zakończyły pracę.
    Problematyczne wtedy staje się kontrolowanie chronologii gdy pojawiają się większe ilości danych, a to z kolei wymaga czasu i nie zawsze jest opłacalne używanie SM.
    Mlody_Zdolny napisał:
    Dodam jeszcze, że programy napisane jako maszyna stanów bardzo dobrze testuje się poza środowiskiem targetowym i z pomocą innych języków programowania.

    Jako logikę zmian flag tak, ale w środowisku docelowym może być różnie.
  • #7 20556447
    ex-or
    Poziom 28  
    Ten film i artykuł jest wyrwany z absolutnie fantastycznego, kompleksowego kursu programowania systemów wbudowanych: https://www.state-machine.com/video-course.
  • #8 20556448
    khoam
    Poziom 42  
    pixel7 napisał:
    Szczególnie gdy zasoby są modyfikowane przez różne mechanizmy, bez dokładnego sprawdzania czy wszystkie zakończyły pracę.
    Problematyczne wtedy staje się kontrolowanie chronologii gdy pojawiają się większe ilości danych, a to z kolei wymaga czasu i nie zawsze jest opłacalne używanie SM.

    W takiej sytuacji lepiej użyć jest spinlocków, zamiast wyłączania/włączania przerwań. Stan spinlocków można również bezpiecznie kontrolować i sprawdzać z ISR. Spinlocków używa się dla oznaczenia "atomowości" zmiennych w języku C (_Atomic) oraz C++ (std::atomic<>), o czym w artykule nie ma słowa.
  • #9 20556459
    Mlody_Zdolny
    Poziom 30  
    khoam napisał:
    Zwykle też taka maszyna stanów w środowisku "pozatargetowym" nie wykazuje tych błędów, które później przytrafiają się środowisku rzeczywistym embedded.

    Jeżeli zwykle tak jest to znaczy, że ktoś stworzył kiepskie testy. Dobre testy wykonują nie tylko scenariusze true-positive ale również pozostałe trzy klasy macierzy pomyłek.
REKLAMA