Architektura softwaru VS bez operačních systémů

Použit SW i HW INTERRUPTů, cyklické čtení periferii, softwarové řešení připojení pomalých a rychlých periferii (sofistikované displaye, kamery, joystick apod.)

Návrh architektury SW pro vestavěné systémy se dosti liší od návrhu architektury pro běžné aplikace. Standardní SW architektury upřednostňují modularitu, škálovatelnost a oddělenost celků na úkor efektivity daného řešení. U vestavěných systémů je však efektivita často jedním z nejdůležitějších aspektů (což ovšem neznamená, že výše zmíněné aspekty nehrají roli).

Rovněž běžné architektonické vzory (MVC, MVP, MVVM, ...) cílí na aplikace, se kterými interaguje uživatel, což se však u vestavěných systémů mnohdy neděje.

U vestavěných systémů nejsou přesně stanovené architektonické vzory, avšak jisté aspekty se objevují u většiny řešení:

  • oddělení vrstev
    • interakce s HW bývá oddělena od aplikace samotné (většinou se mezi nimi nachází API)
    • to umožňuje značné zpřehlednění aplikace s minimálním snížením efektivity
  • modularizace
    • související části aplikace jsou spojeny do modulů
    • dosti se protíná s běžnými aplikacemi
    • je zde však kladen důraz na maximální zachování efektivity
    • bývá důležitá, pokud je nutná bezpečnost (oddělení chráněných bloků od těch, které nesou rizika)
  • zaměření na datové pohyby
    • při řešení struktury se bere v potaz tok dat systémem

Zde jsme si zběžně popsali architekturu SW VS z hlediska návrhu a struktury, níže se však budeme věnovat spíše konkrétním principům a technikám, které se při psaní vestavěného SW užívají.

SW a HW interrupt

Interrupt (neboli přerušení) je metoda, která umožňuje (obvykle) asynchronní zásahy do běžné činnosti zařízení. Spočívá v tom, že na základě nějakého popudu dojde k vyvolání obslužné rutiny (ISR). To nám umožňuje např. reagovat na stisk tlačítka nebo zařízením samotným si vzájemně posílat příkazy.

HW interrupt

HW interrupty (někdy též vnější interrupty) vyvolává externí zařízení (z pohledu procesoru, takže např. paměť či IO zařízení) prostřednictvím řadiče přerušení (nebo u jednodušších systémů přímo prostřednictvím procesoru). Ten zároveň rozhoduje, o jaké přerušení se jedná a rozhoduje, jaká ISR se má na jeho základě vykonat.

Z pohledu SW je ideální tvořit ISR co nejmenší, aby jejich vykonávání bylo co nejkratší. To především proto, že je během nich zakázáno další přerušení a my tak můžeme přijít o další informace. Proto je dobrou praxí si v ISR pouze uložit informaci o vyvolaném přerušení a zpracovat ji následně během kontroly již v klasickém výkonném kódu pomocí kontrolního cyklu. Toto je ovšem možné pouze v případě, jsme-li schopni zajistit vykonání reakce v přijatelném čase (např. pro stisk tlačítka na termostatu klidně stovky ms, ale pro nouzový spínač u soustruhu může být každá ms nepostradatelná).

Postup:

  1. externí zařízení vyvolá přerušení
  2. procesor dokončí aktuálně vykonávanou instrukci a uloží obsah program counteru na zásobník
  3. procesor (nebo řadič přerušení) zjistí z tabulky vektorů přerušení adresu odpovídající ISR
  4. procesor zakáže přerušení
  5. procesor vykoná ISR
  6. procesor povolí přerušení
  7. po dokončení procesor načte uloženou adresu následující instrukce ze zásobníku a uloží ji do PC

SW interrupt

SW interrupty jsou vyvolány kódem, který na zařízení běží. Obvykle se k tomu užívá instrukce INT. SW interrupty jsou běžnější v zařízeních s operačním systémem, který na ně reaguje a zpracovává je svým standardním způsobem (typickým případem je zpracování výjimek (výjimky jsou SW přerušení)). V zařízeních bez OS si musíme ISR navrhovat sami, což je ovšem naprosto v pořádku. Stojí však za zvážení, zdali je to pro nás výhodné (např. platforma Arduino je má zakázané). Jsme-li schopni navrhnout SW tak, aby k situacím vyžadujícím SW přerušení nedocházelo, nemusíme je implementovat.

Rozdílem oproti HW přerušení je především synchronicita, protože SW přerušení je vyvoláno instrukcí, dochází k němu jen při tiku interních hodin, a tedy je synchronní.

Postup:

  1. vyvolá se instrukce přerušení
  2. uloží se stavové informace o právě zpracovávaném programu
  3. zakáže se další přerušení
  4. procesor zjistí, o jaké přerušení se jedná (podle operandu)
  5. nalezne odpovídající ISR a vykoná ji
  6. po návratu z podprogramu obnoví uložené stavové informace

Cyklické čtení periferií

Jedním z nejběžnějších způsobů práce s periferiemi je jejich cyklické čtení. Ve vestavěných systémech jsou velmi běžné periferie, které provádějí svou činnost nezávisle na řídícím mikrokontroléru (např. neustále snímají teplotu okolí), její výsledky ukládají do svých registrů a při dalším měření je přepíší bez ohledu na to, jestli je předtím mikrokontrolér četl. Jedná se o protiklad k přerušujícím periferiím, protože je zde čistě na mikrokontroléru, kdy data přečte.

Tento princip je nutné podpořit SW návrhem, tedy implementací cyklického čtení. Na jednodušších jednojádrových systémech je to naprosto běžný přístup - máme cyklus, který se vykonává neustále dokola, dokud je mikrokontrolér napájen (např. Arduino). Ten bývá často doplněn o úvodní možnost nastavení prostředků (část kódu, která se vykoná jen jednou - na začátku).

U vícejádrových systémů jsou možnosti širší. Můžeme mít např. jedno jádro, které pořád dokola pouze čte data z periferií a předává je ostatním ke zpracování.

Softwarové řešení připojení pomalých a rychlých periferii

Způsoby SW řešení připojení periferií opět dosti záleží na typu a vlastnostech kontroléru. Máme-li jednojádrový kontrolér, může pro nás být řízení pomalých periferií (jako např. LCD displeje) poměrně náročné.

Vezmeme-li si např. běžně používaný LCD 1602, máme dvě možnosti jeho řízení. Můžeme jej připojit přímo, což nám zabere minimálně šest GPIO pinů (plus další prostor na DPS pro napájení a zem), ale budeme s ním moci komunikovat relativně rychle. Nebo využijeme I2C převodník, který nám sníží počet pinů na dva (navíc společné pro všechna I2C zařízení), ale výrazně prodlouží komunikaci.

Rozhodnutí závisí na naší aplikaci. Pokud nám nevadí čekání, můžeme zvolit pomalejší metodu s tím, že zbytek systému bude během interakce s displejem čekat. Potřebujeme-li však co nejvíce minimalizovat prostoje, zvolíme metodu rychlejší (ovšem jen pokud máme dostatek IO pinů).

Podobné dilema nám vyvstává i u dalších pomalých periférií, jako jsou např. kamery. Zde navíc musíme brát v potaz i další prodlevu způsobenou následnou komunikací s externími systémy, kterým budeme velice pravděpodobně chtít získané snímky předat.

Tyto problémy značně redukují vícejádrové systémy, které umožňují vyhradit obsluze pomalých periferií konkrétní jádro a zbytek systému jimi není bržděn.

Rychlé periferie, jako např. joystick (což je interně jen sada tlačítek) často užívají přerušení a tedy na ně systém nemusí čekat. Obsluha přerušení však přináší vlastní problémy, které jsou popsané výše.

Zdroje