Systemy embedded. μCyfrowa ewolucja

Systemy embedded. μCyfrowa ewolucja

Fascynującą cechą historii rozwoju systemów wbudowanych jest współistnienie na rynku najnowocześniejszych układów SoC z legendarnymi 8-bitowymi mikrokontrolerami 8051, od których zaczęła się 40 lat temu era mikrokontrolerów. Panorama rozwiązań układowych i programistycznych, jakie pojawiły się przez te 40 lat i ciągle są stosowane, jest szersza niż Panorama Racławicka. Autor tego artykułu oprowadza nas po tej panoramie i jak dobry przewodnik rozjaśnia poszczególne jej fragmenty. Redakcja

Pierwsze systemy komputerowe zajmowały całe pomieszczenia, ważyły kilkanaście ton i konsumowały niezmierzone ilości energii elektrycznej. Uważany za jeden z pierwszych komputerów ENIAC (fotografia 1) (choć tak naprawdę nie był pierwszy) zajmował powierzchnię 140 metrów kwadratowych, ważył 27 ton i pobierał energię o mocy 140 kW. Zbudowany był z ponad 18 tysięcy lamp elektronowych. Z uwagi na ograniczoną trwałość lamp oraz gigantyczny koszt komputery tego typu nie nadawały się do innych celów poza wykonywaniem specjalistycznych obliczeń.

Fotografia 1. ENIAC - jeden z pierwszych komputerów

W tamtych czasach nikt jeszcze nie myślał o używaniu komputerów do sterowania urządzeniami. Układy sterujące realizowane były głównie za pomocą prostej logiki zbudowanej na przekaźnikach oraz innych elementach elektromechanicznych. Pierwszy przełom nastąpił w 1948 roku wraz z wynalezieniem tranzystora, który rozpoczął proces miniaturyzacji oraz stopniowego obniżania kosztów systemów komputerowych. W tym samym roku rozpoczęto również seryjną produkcję komputerów analogowych. Kolejnym ważnym krokiem było opracowanie na początku lat 60. pierwszych układów scalonych, co stanowiło kolejny krok w miniaturyzacji i wiązało się z obniżeniem kosztów i poboru mocy. Wówczas pojawiła się pierwsza idea zastosowania komputera do sterowania urządzeniami lub innymi obiektami. Na początku znalazła zastosowanie w urządzeniach wojskowych oraz w przemyśle kosmicznym.

Za jeden z pierwszych systemów wbudowanych, we współczesnym tego słowa znaczeniu, uważany jest komputer pokładowy zastosowany w programie Apollo, zbudowany w 1966 roku (fotografia 2). Używany był do sterowania systemem dowodzenia i modułem księżycowym. Komputer pracował z częstotliwością 2 MHz, miał 15-bitowe słowo maszynowe z jednym bitem parzystości, pamięć RAM o wielkości 2048 słów maszynowych oraz pamięć ROM o wielkości 36884 słów. Ważył 32 kg, a do jego zasilania wymagane było źródło o wydajności 55 W. Moc obliczeniowa była zbliżona do powstałego w 1981 roku 8-bitowego mikroprocesora 65c02. Jednak był to system zbudowany tylko w kilkunastu egzemplarzach. Pierwszym systemem komputerowym zastosowanym na szerszą skalę był komputer D17-B używany do sterowania pociskami międzykontynentalnymi Minutman I. Został zbudowany z elementów dyskretnych i zawierał ponad 1500 tranzystorów oraz pod 6000 diod.

Fotografia 2. Komputer używany w programie Apollo

Wspomniane systemy były jednak bardzo drogie i jedynie wojsko oraz instytucje rządowe mogły sobie na nie pozwolić. Dalsze obniżenie kosztów i miniaturyzacja nastąpiły wraz z opracowaniem pierwszego 4-bitowego mikroprocesora 4004 a następnie produkowanego na masową skalę 8-bitowego mikroprocesora 8080. Do prawidłowego działania wymagał zewnętrznej pamięci RAM, pamięci ROM oraz kilkunastu dodatkowych układów scalonych, jak generatory sygnału zegarowego, układy czasowo-licznikowe czy układy wejścia-wyjścia. Systemy te były o rząd wielkości tańsze od wspomnianych wcześniej komputerów zbudowanych na układach scalonych małej skali integracji, jednak nadal były one stosunkowo drogie i stosowane tylko w bardziej zaawansowanych urządzeniach. Opracowanie tańszego mikroprocesora 65c02 niewiele zmieniło, ponieważ do prawidłowego działania systemu komputerowego nadal potrzebne były dodatkowe układy zewnętrzne (fotografia 3).

Fotografia 3. System komputerowy z mikrokontrolerem 65c02

Prawdziwy przełom nastąpił w 1976 roku wraz z opracowaniem układu 8048, który integrował w jednym układzie scalonym kompletny system: procesor, 1 kB pamięci ROM, 64 bajty pamięci RAM, układ czasowo-licznikowy oraz zewnętrzne porty GPIO. Integracja komputera w jednym układzie znacząco obniżała koszty systemu i z czasem okazało się, że taniej jest zbudować urządzenie w oparciu o mikrokontroler niż dedykowany układ.

Mikrokontroler do prawidłowego działania potrzebował jedynie kilku elementów zewnętrznych. Dodatkowo okazało się, że wraz z rosnącą wydajnością obliczeniową układ sterujący z mikrokontrolerem może realizować szereg bardziej zaawansowanych funkcji.

Definicja i podział

Zatem czym, od strony formalnej, jest system wbudowany? Jest to system komputerowy specjalnego przeznaczenia, który stanowi integralną część obsługiwanego przez niego sprzętu i służy do sterowania innymi obiektami. Trudno sobie dzisiaj wyobrazić nowoczesny sprzęt elektroniczny, zarówno ten konsumencki, jak i profesjonalny, bez możliwości swobodnego wyboru parametrów, konfiguracji i interakcji z użytkownikiem. Jest to możliwe dzięki zastosowaniu mikroprocesorów sterujących pracą tych urządzeń. Systemy wbudowane spotykamy w wielu miejscach, m.in. w zegarkach elektronicznych, telefonach komórkowych, telewizorach, odtwarzaczach, sprzęcie gospodarstwa domowego, samochodach, inteligentnych budynkach, przyrządach pomiarowych czy automatyce. W zasadzie trudno obecnie znaleźć nawet najprostsze urządzenie, które byłoby zbudowane bez użycia mikroprocesora. Trend ten możemy wyraźnie dostrzec, obserwując publikacje w naszym czasopiśmie. Jeśli spojrzymy na opisy urządzeń opublikowanych na łamach EP w 1994 roku, projekty zbudowane w oparciu o mikrokontrolery stanowią rzadkość. Czasami pojawia się opis jednego lub dwóch urządzeń zbudowanych z użyciem mikrokontrolera, natomiast w niektórych numerach w ogóle ich nie ma. Znaczną większość urządzeń tamtego okresu stanowiły układy analogowe i cyfrowe zbudowane z wykorzystaniem elementów dyskretnych oraz układów scalonych małej skali integracji. Natomiast gdy spojrzymy na dowolny numer czasopisma z bieżącego roku, sytuacja wygląda odmiennie. Urządzenia i układy zrealizowane bez mikrokontrolerów lub układów SoC są w mniejszości, a urządzenia zrealizowane jedynie przy użyciu elementów dyskretnych stanowią zupełną rzadkość.
Cechy, które stanowią o tym, że system komputerowy jest systemem wbudowanym, a nie systemem komputerowym ogólnego przeznaczenia, to:

  • Sterowanie odbywa się poprzez umieszczenie wewnątrz urządzenia (obiektu) określonego systemu komputerowego, trwale połączonego z danym obiektem, wyposażonego w specjalne oprogramowanie realizujące określone funkcje.
  • System komputerowy spełnia określone wymagania zdefiniowane do zadań, które ma wykonywać.
  • Jest oparty na mikroprocesorze (lub mikrokontrolerze) zaprogramowanym do wykonywania ograniczonej liczby zadań a nawet tylko do jednego.
  • Zależnie od złożoności wykonywanych zadań, może zawierać oprogramowanie dedykowane wyłącznie temu urządzeniu (firm
    ware) lub może to być system operacyjny ogólnego przeznaczenia wraz z zainstalowanym specyficznym oprogramowaniem.
  • Podstawową cechą wyróżniającą systemy wbudowane na tle innych systemów komputerowych, oprócz dedykowanego charakteru, jest jakość oprogramowania oraz zastosowane komponenty systemu komputerowego.

Ogólna zasada jest taka, że im mniej złożone jest oprogramowanie i bardziej wyspecjalizowane, tym bardziej niezawodny jest system i pozwala szybciej reagować na zdarzenia krytyczne. Niezawodność systemu może być zwiększona poprzez rozdzielenie zadań na mniejsze podsystemy a także przez redundancję. Może ona polegać na zastosowaniu dwóch identycznych urządzeń do jednego zadania, z których drugie przejmuje zadania pierwszego w przypadku awarii jednego z nich. Jest to szczególnie istotnie w przypadku systemów odpowiedzialnych za nadzorowanie funkcji związanych z życiem i bezpieczeństwem (np. lotnictwo, medycyna). Systemom wbudowanym stawiane są różne wymagania, dlatego zastosowane oprogramowanie oraz systemy komputerowe mogą się znacząco różnić od siebie.

Rysunek 4. Podział systemów wbudowanych

Systemy wbudowane możemy podzielić według dwóch kategorii. Pierwsza określa wymagania funkcjonalne, druga uwzględnia wymaganą moc obliczeniową (rysunek 4). Systemy o małej wydajności realizują proste czynności i współcześnie budowane są z zastosowaniem mikrokontrolerów 8-bitowych np. z rodziny PIC czy AVR lub najprostszych mikrokontrolerów 32-bitowych z rdzeniem Cortex-M0/M0+ np. STM32L0/F0. Projekt płytki PCB z reguły zawiera kilkanaście elementów zewnętrznych, a oprogramowanie pracuje samodzielnie bez nadzoru systemu operacyjnego. Systemy tego typu możemy zbudować z prostych układów skali LSI (dużej skali integracji, LSIlarge scale of integration), jednak z uwagi na dużo niższe koszty i większą funkcjonalność produkowane są w postaci systemu wbudowanego. Interfejs użytkownika jest nieskomplikowany i najczęściej składa się z kilku przycisków i wyświetlacza alfanumerycznego lub 7-segmentowego LED (fotografia 5). W najprostszych przypadkach informacja o stanie systemu obrazowana jest za pomocą diod LED.

Fotografia 5. System wbudowany z mikrokontrolerem 32-bitowym

Systemy operacyjne średniej wydajności najczęściej realizowane są z zastosowaniem pojedynczego 32-bitowego mikrokontrolera, na przykład z rdzeniem MIPS (PIC32) lub ARM Cortex-M3/M4/M7 (STM32/LPC4xxx itp.) czy nawet na bazie prostego procesora DSP. Płytka PCB jest stosunkowo rozbudowana i może zawierać wiele elementów dodatkowych, jednak nie zawiera zewnętrznej pamięci FLASH czy RAM. Oprogramowanie również jest bardziej rozbudowane i najczęściej zawiera system operacyjny czasu rzeczywistego oraz dodatkowe biblioteki służące do realizacji skomplikowanych zadań, takich jak: obsługa protokołu TCP/IP, obsługa stosu USB czy obsługa interfejsu graficznego. Interfejs użytkownika może być dość złożony i rozbudowany, wyposażony w wyświetlacz graficzny monochromatyczny lub kolorowy oraz panel dotykowy.

Duże systemy wbudowane mogą składać się z wielu procesorów, w tym DSP, układów programowalnych, czy rozbudowanych układów SoC wyposażonych w 64-bitowe wielordzeniowe CPU. Płytki PCB w tego typu systemach są bardzo rozbudowane, gdyż pamięć operacyjna, zewnętrzna pamięć FLASH oraz inne komponenty pracujące przy dużej częstotliwości wymagają zastosowania wielobitowych szybkich magistral.

Oprogramowanie, w którym procesor aplikacyjny najczęściej pracuje pod kontrolą systemu Linux, jest również zaawansowane. Inne procesory mogą pracować pod kontrolą systemów czasu rzeczywistego realizujących zadania krytyczne czasowo. Systemy tego typu najczęściej dysponują dużymi ekranami dotykowymi lub zewnętrznymi monitorami dołączonymi np. poprzez port HDMI.

Bardzo istotnym kryterium podziału systemów wbudowanych jest funkcjonalność. Inne wymagania stawiamy systemom sterującym oświetleniem, gdzie czas reakcji nie ma większego znaczenia, a zupełnie inne systemom sterującym robotami przemysłowymi, gdzie konieczne jest zastosowanie systemu czasu rzeczywistego. Możemy wyszczególnić takie grupy:

  • Samodzielne systemy wbudowane - systemy komputerowe zdolne działać niezależnie, bez konieczności współpracy z innymi urządzeniami np. komputerem nadrzędnym. Zbierają dane z wejść analogowych lub cyfrowych, wykonują obliczenia, a wyniki operacji przetwarzają na stany odpowiednich wyjść analogowych lub cyfrowych. Przykłady takich systemów wbudowanych to m.in: sterownik pralki automatycznej, kamera wideo, termostat lodówki, itp.
  • Systemy sieciowe - charakteryzują się tym, że mają możliwość komunikowania się z innymi obiektami, np. za pomocą sieci komputerowych LAN, modemu GSM, modemu Wi-Fi, itp. Przykładami takich systemów są instalacje inteligentnych budynków czy kamery internetowe.
  • Systemy mobilne - systemy przenośne, szczególny nacisk kładziony jest na przenośność oraz minimalizację zużycia energii. z uwagi na to, że korzystają głównie z zasilania bateryjnego. Typowymi przedstawicielami takich systemów są: telefony komórkowe, odtwarzacze MP3, kamery, aparaty fotograficzne itp.
  • Systemy operacyjne czasu rzeczywistego - charakteryzują się reakcją w ściśle zadanym reżimie czasowym. Systemy tego typu muszą odpowiedzieć na zadane wymuszenie w ściśle określonym czasie, ponieważ brak odpowiedzi może skutkować niepoprawnym działaniem, a nawet uszkodzeniem obiektu, którym system steruje. Takie sterowniki najczęściej wykonywane są z zastosowaniem mikrokontrolera lub mikroprocesora bez jednostki zarządzania pamięcią MMU (Memory Management Unit), ponieważ pamięć wirtualna nie gwarantuje odpowiedzi systemu w czasie deterministycznym. Do ochrony pamięci (jeśli jest potrzebna), stosuje się układ MPU (Memory Protection Unit), który umożliwia ustawienie zakresów ochronnych obszarów pamięci fizycznej, bez wirtualnej przestrzeni adresowej.

Przedstawione kategorie mogą się nawzajem przenikać, np. system wbudowany czasu rzeczywistego może być równocześnie systemem sieciowym oraz mobilnym.

Sprzęt stosowany w systemach wbudowanych

W systemach wbudowanych najczęściej stosowany jest sprzęt oparty na mikrokontrolerach jednoukładowych lub układach mikroprocesorowych SoC (System on Chip), przy czym z reguły małe systemy wbudowane (np. myszki, klawiatury, sterowniki klimatyzacji itp.) z uwagi na cenę i niewielkie wymiary korzystają z mikrokontrolerów jednoukładowych. Bardziej skomplikowane średnie i duże systemy mogą korzystać z mikroprocesorowych układów SoC. Najbardziej zaawansowane układy mogą zawierać kilka mikrokontrolerów, układów SoC oraz układy programowalne. Zastosowanie układów programowalnych FPGA nie jest powszechnie spotykane, najczęściej z uwagi na wysoki koszt. Ma miejsce wówczas, gdy nie ma możliwości użycia innego tańszego rozwiązania.

Mikrokontrolery

Mikrokontrolery jednoukładowe stanowią główny wybór w przypadku małych i średnich systemów wbudowanych. Dzięki integracji w jednym układzie jednostki centralnej pamięci FLASH, RAM oraz wszystkich innych układów peryferyjnych (rysunek 6) umożliwiają znaczące obniżenie kosztów urządzenia. Gdy jeden układ stanowi kompletny system mikroprocesorowy, kompletne urządzenie otrzymujemy, dołączając do portów zewnętrznych kilka dodatkowych elementów. Płytka PCB również jest stosunkowo prosta, ponieważ nie ma konieczności prowadzenia szerokich magistral do pamięci. Może być zrealizowana w technice dwuwarstwowej.

Rysunek 6. Uproszczony schemat wewnętrzny typowego mikrokontrolera


Na rynku istnieje bardzo wiele rodzajów mikrokontrolerów, od niewielkich 8-bitowych układów AVR, do bardzo rozbudowanych mikrokontrolerów z rdzeniem ARM, takich jak STM32. Pierwsze mikrokontrolery jednoukładowe miały niewielkie zasoby sprzętowe. Typowym przykładem jest popularny układ 8051 zaprojektowany przez firmę Intel. Zawiera jednostkę CPU o wydajności 1 DMIPS @12 MHz, 128 B pamięci RAM oraz 8 kB pamięci ROM lub FLASH. Mikrokontrolery tej rodziny były powszechnie spotykane w małych systemach do około 2000 roku, później zaczęły stopniowo wychodzić z użycia zastępowane przez lepsze konstrukcje 8-bitowe. Produkowane są do dziś, np. firma Microchip produkuje klasyczne mikrokontrolery z pamięcią FLASH, są to 80c51, 89C2051. Podobne układy znajdziemy także w ofertach takich firm jak: Infineon, Analog Devices czy Silicon Labs. Niektóre mają znacznie ulepszony rdzeń wykonujący instrukcje w 1 lub 2 cyklach zegarowych i występują w nowoczesnych obudowach SMD TQFP, więc mogą być stosowane we współczesnych produktach. Przykładem jest rodzina 8051F00/1/2/5/7 Silicon Labs (rysunek 7) wyposażona w znacznie ulepszony rdzeń 8051 z potokowym przetwarzaniem instrukcji, w większości wykonywanych w jednym lub dwóch cyklach zegarowych. Zawiera 2 kB pamięci XRAM, 256 B pamięci wewnętrznej oraz szeroki zestaw urządzeń peryferyjnych wraz z pamięcią ISP FLASH. Chociaż tworzenie nowych urządzeń w oparciu na mikrokontrolerze z rdzeniem 8051 jest nieco dyskusyjne, układy tego typu mogą znaleźć zastosowanie wszędzie tam, gdzie istnieje starszy kod napisany np. w asemblerze i gdzie nie opłaca się tworzyć nowego oprogramowania od podstaw.

Rysunek 7. Mikrokontroler rodziny C8051F000 firmy Silicon Labs

Pomimo że inwazja 32-bitowych mikrokontrolerów ARM trwa już od jakiegoś czasu i możemy kupić mikrokontroler z rdzeniem Cortex-M0 za kilka centów, nieco nowocześniejsze 8-/16-bitowe mikrokontrolery nadal cieszą się dużą sympatią konstruktorów. Zapewne dlatego, że przygotowanie oprogramowania dla takiego mikrokontrolera jest znacznie prostsze niż w przypadku układów z rdzeniem ARM. Dla wielu peryferii wystarczy ustawić kilka bitów w rejestrach konfiguracyjnych i układ już działa. Obecnie najchętniej stosowanymi układami 8-bitowymi są mikrokontrolery AVRPIC, a wśród układów 16-bitowych - PICdsPIC.

Rysunek 8. Mikrokontroler z rdzeniem AVR


Mikrokontrolery AVR (rysunek 8) swoją znaczącą popularność zyskały po 2002 roku, stopniowo wypierając bardzo popularne w tamtych czasach układy oparte na rdzeniu 8051. Dzięki unowocześnionej jednostce centralnej pracowały kilkanaście razy szybciej, pozwalając na znaczne zmniejszenie poboru energii. Szczególną sympatię zyskały w naszym kraju dzięki umiarkowanej cenie i dobrej polityce dystrybutorów. Do 2003 roku producent (Atmel) sprzedał ponad 500 milionów egzemplarzy mikrokontrolerów tego typu. Rdzeń AVR jest zbudowany w oparciu o architekturę harwardzką, z oddzielnymi instrukcjami dostępu do pamięci programu oraz do pamięci danych. Dzięki przetwarzaniu potokowemu większość 8-bitowych instrukcji jest wykonywana w 1 lub 2 cyklach zegarowych. Mikrokontrolery AVR zostały podzielone na kilka podtypów w zależności od przeznaczenia, dostępnej pamięci oraz układów peryferyjnych:

  • TinyAVR - przeznaczone do najprostszych i najtańszych systemów wbudowanych. Rdzeń mikrokontrolera ma uproszczony zestaw instrukcji i pozbawiony jest np. operacji mnożenia. Zawiera również mocno ograniczony zestaw układów peryferyjnych oraz niewielką pamięć FLASH o rozmiarze od 0,5 kB do 32 kB. Układy tego typu dostępne są w niewielkich obudowach od 6 do 32 wyprowadzeń a ich cena jest na tyle atrakcyjna, że opłaca się je stosować jako zamiennik dla kilku układów cyfrowych małej skali integracji. Najmniejszym układem tego typu jest ATTINY4 w obudowie SOT23-6 mający pamięć FLASH o wielkości 512 B i pamięć RAM o wielkości 32 bajtów.
  • MegaAVR - stanowi główną linię produktową, układy dysponują pamięcią FLASH z przedziału 4...256 kB oraz występują w wielu typach obudów od 28 do 100 wyprowadzeń. Mogą zawierać wiele, ciekawych układów peryferyjnych, a rdzeń dysponuje standardowym zestawem instrukcji, takich jak dostęp do pamięci o rozmiarze większym od 64 kB czy instrukcja mnożenia. Brakuje jednak instrukcji dzielenia DIV (której architektura AVR nie zawiera). Jednymi z najpopularniejszych w naszym kraju były mikrokontrolery ATMEGA8/48/88/16/32 z uwagi na dobry stosunek możliwości do ceny. Duża część projektów publikowanych na łamach EP wyposażona była w któryś ze wspomnianych układów.
  • XMEGA - to najbardziej zaawansowana seria rodziny AVR. Układy zawierają zaawansowane bloki poprawiające wydajność obliczeniową, takie jak m.in. kontroler bezpośredniego dostępu do pamięci (DMA), system obsługi zdarzeń czy koprocesor kryptograficzny. Wyposażone są w pamięć programu o pojemności 16...384 kB i występują w obudowach do 100 wyprowadzeń. Również znacząco podniesiono częstotliwość taktowania rdzenia, która może wynosić 50 MHz. Układy tego typu, z uwagi na to, że powstały w okresie, gdy na rynku zadomowiły się mikrokontrolery 32-bitowe, nie zyskały dużej popularności.

Fotografia 9. Mikrokontrolery PIC w różnych obudowachcc

Kolejną rodziną, która nadal cieszy się stosunkowo dużą popularnością (choć w Europie nie tak wielką), jest rodzina mikrokontrolerów PIC (fotografia 9)opracowana przez firmę Microchip Technology. Mikrokontrolery 8-bitowe i 16-bitowe tej rodziny do dziś mają swoich zagorzałych sympatyków. Pierwsze, dostępne z pamięcią ROM, pojawiły się na rynku już w 1976 roku, a do 2013 roku firma sprzedała ponad 12 miliardów układów tej rodziny. Podobnie jak poprzednik, rdzeń mikrokontrolera oparty jest na architekturze harwardzką i większość instrukcji wykonuje w 2 lub 4 cyklach zegarowych. W przeciwieństwie do nowoczesnych architektur posiada zestaw rejestrów dedykowanych określonym zadaniom np. WO akumulator czy sprzętowy stos systemowy, na którym przechowywane się adresy powrotów z podprogramów. W starszych wersjach stos nie był adresowany programowo, co zostało zmienione dopiero od serii F18. Powyższe cechy architektury sprawiają, że nie jest dobrze dopasowany do zastosowania kompilatorów języków wyższego poziomu jak C czy implementacji systemów operacyjnych z wywłaszczeniem (niemniej jednak takie kompilatory istnieją). Układy zostały podzielone na pocdrodziny służące do segmentacji rynku - do dyspozycji mamy następujące podtypy:

  • PIC10/12 - występują w obudowach o wielkości od 6 do 40 wyprowadzeń. Jest to seria najprostszych układów przeznaczona do podstawowych rozwiązań. Ma niewielką przestrzeń adresową programu do 512 słów (12-bitowych), jednym z najbardziej znanych mikrokontrolerów tej serii jest PIC10f32x.
  • PIC16 - bardzo często spotykane w małych systemach. Wyposażone w pamięci danych o wielkości do 2048 słów 15-bitowych oraz o 8-poziomowym stosie sprzętowym.
  • PICF18 - wraz z nastaniem nowego tysiąclecia programiści stopniowo odchodzili od programowania systemów wbudowanych w asemblerze na rzecz języka C, co spowodowało, że konieczna stała się modyfikacja architektury PIC tak, aby była przyjazna dla kompilatorów. W ten sposób powstała rodzina PICF18, która uzyskała głębszy oraz programowo adresowany stos o głębokości 31 poziomów, dodatkowe instrukcje warunkowe oraz liniową adresowalną przestrzeń rejestrów specjalnego przeznaczenia.
  • PIC24dsPIC - bazując na architekturze PICF18, w kolejnych latach opracowano 16-bitowe układy PIC24 i dsPIC przeznaczone do średnio zaawansowanych rozwiązań oraz obliczeń DSP. Mikrokontrolery mają rejestry o rozmiarze 16 bitów, zwiększony licznik rozkazów do 24 bitów, a rozmiar pamięci programu może wynosić 64 kB. Wersja dsPIC dodatkowo zawiera zestaw instrukcji przydatny podczas cyfrowego przetwarzania sygnałów, takich jak instrukcję mnożenia z akumulacją, odwracanie bitów, dzielenie sprzętowe itp.

Mikrokontrolery 8-bitowe, które jeszcze 15 lat temu pełniły główną funkcję w systemach wbudowanych, wraz z nastaniem "ARM-owej rewolucji" ustąpiły pola mikrokontrolerom 32-bitowym, głównie z rdzeniem ARM. Rewolucję rozpoczęła firma NXP w 2004 roku, wprowadzając konkurencyjne cenowo, dużo szybsze układy LPC2000 z 32-bitowym rdzeniem ARM7TDMI-S, co spowodowało masowe odchodzenie od rozwiązań 8-bitowych.

Rysunek 10. Ewolucja rodziny ARM

Układy ARM swoje pierwotne korzenie mają w mikroprocesorach przeznaczonych do komputerów domowych oraz w większych układach SoC (rysunek 10). Pierwszym układem zmodyfikowanym z myślą o mikrokontrolerach był rdzeń ARM7TDMI-S, który wywodził się z mikroprocesorów ogólnego przeznaczenia, przez co nie do końca był dopasowany do systemów wbudowanych. Największe wady tego układu to brak jasno zdefiniowanych trybów oszczędzania energii oraz zewnętrzny kontroler przerwań VIC, który nie potrafił obsługiwać wywłaszczania. Powyższe wady zmusiły firmę ARM do opracowania jednostek centralnych przeznaczonych do mikrokontrolerów i w ten sposób powstał rdzeń Cortex-M3, a następnie dostosowano do różnych segmentów rynku jego kolejne odmiany, takie jak Cortex-M0/M4/M7. Bardzo istotną cechą jest to, że firma ARM nie produkuje gotowych układów, a jedynie licencjonuje jednostki centralne innym producentom. Na rynku możemy nabyć setki różnych typów układów różnych producentów, z różnymi układami peryferyjnymi, ale mających taki sam zestaw instrukcji. Jest to dużą zaletą, ponieważ możemy wykorzystać ten sam zestaw narzędzi, np. kompilator debugger, bez konieczności zakupu nowych narzędzi przy zmianie producenta. Nie bez znaczenia jest również dostęp do wszelakich narzędzi na licencji open source, co znacząco obniża koszty wdrożenia projektu. Współcześnie możemy powiedzieć, że mikrokontrolery z jednostką centralną ARM stanowią standard przemysłowy, tak jak 30 lat temu funkcję tego standardu pełniły układy oparte na jednostce 8051.

We współczesnych mikrokontrolerach z rdzeniem ARM stosowane są rdzenie Cortex występujące w czterech odmianach:

Cortex-M0/M0+ - przeznaczone do najprostszych układów, cenowo konkurujące z układami 8-bitowymi. Zbudowane w oparciu o architekturę ARMv6m z trzystopniowym przetwarzaniem potokowym, zintegrowanym kontrolerem przerwań obsługującym maksymalnie 32 przerwania i wydajnością wynoszącą 0,9 DMIPS/MHz. Produkowane przez wiele różnych firm, przy czym najpopularniejsze są układy produkcji NXP LPC1100/1200/800, STMicroelectronics STM32F0/L0, Toshiba TX00, Renesas S1 itp. Najczęściej mają pamięć programu o rozmiarze 2...32 kB oraz pamięć danych do 8 kB i dostępne są w obudowach o wielkości od 5 do 64 wyprowadzeń. Zestaw układów peryferyjnych jest stosunkowo ubogi. Najczęściej dostępne są podstawowe układy peryferyjne, takie jak porty szeregowe, kontroler SPI, I2C itp., a ich częstotliwość pracy wynosi maksymalnie kilkadziesiąt MHz.

  • Cortex-M3 - obecnie najpowszechniej używane układy z rdzeniem ARM z uwagi na stosunkowo prostą budowę oraz bardzo dobry stosunek możliwości do ceny. Zbudowane są w oparciu o architekturę ARMv7m z 3-stopniowym przetwarzaniem potokowym oraz kontrolerem przerwań obsługującym do 256 źródeł zewnętrznych. Opcjonalnie mogą zostać wyposażone w jednostkę FPU, realizującą sprzętową obsługę obliczeń na liczbach zmiennoprzecinkowych, co jednak nie jest praktykowane. Typowymi przedstawicielami serii są mikrokontrolery produkowane przez firmę NXP (LPC13xx, LPC17xx), STMicroelectronic (F1, F2). Charakteryzują się pamięcią programu o rozmiarze 32...256 kB oraz pamięcią danych od 6 do 128 kB. Najczęściej wyposażone są w bogaty zestaw układów peryferyjnych, takich jak kontroler bezpośredniego dostępu do pamięci (DMA), kontroler USB, kontroler Ethernet itp. Mogą one również pracować z większą częstotliwością sięgającą 120 MHz.
  • Cortex-M4 - dysponują znacznie większą wydajnością, są nieco droższe, jednak ich cena najczęściej mieści się w granicach 30 zł. Zbudowane są w oparciu o architekturę ARMv7E-m z 3-stopniowym przetwarzaniem potokowym wyposażonym w jednostkę przewidywania rozgałęzień, ponadto wyposażone są w zestaw instrukcji służących do realizacji algorytmów DSP oraz opcjonalną jednostkę zmiennoprzecinkową o pojedynczej precyzji. Typowymi przedstawicielami tej linii są mikrokontrolery NXP LPC40xx/53xx STM32F3/4 czy Texas Instruments (TM4C). Wyposażone są w pamięć o rozmiarze od 32 do 1024 kB, pamięć RAM 8...512 kB oraz bogaty zestaw układów peryferyjnych. Pracują z częstotliwością zbliżoną do 200 MHz i charakteryzują się wydajnością rzędu 1,25 DMIPS/MHz.
  • Cortex-M7 - najbardziej wydajne mikrokontrolery oraz najdroższe. W stosunku do poprzedników zostały znacznie rozbudowane, jednostka centralna bazuje na rdzeniu ARMv7E-M z usprawnionym 6-stopniowym przetwarzaniem potokowym i towarzyszy jej jednostka zmiennoprzecinkowa o podwójnej precyzji. Mogą być wyposażone w pamięć CACHE pierwszego poziomu dla instrukcji oraz danych do 64 KB. Mają pamięć programu o wielkości do 2 MB, pamięć RAM do 1 MB oraz zaawansowane układy peryferyjne. Pracują z częstotliwością ponad 200 MHz, osiągają wydajność rzędu 2,14 DMIPS/MHz. Typowymi przedstawicielami tej linii są układy: ATMEL SAMS70, STMicroelectronic STM32F7 czy Freescale KV5x. Niestety z tak dużą wydajnością wiąże się odpowiednio wysoka cena, sięgająca kilkudziesięciu złotych za sztukę. Często w tego typu układach pamięć wewnętrzna bywa niewystarczająca, zwłaszcza gdy konieczne jest podłączenie kolorowego wyświetlacza TFT. Czyni to wybór tego typu mikrokontrolerów rozwiązaniem dyskusyjnym, zwłaszcza, że w obecnych czasach w porównywalnych cenach możemy dostać całe komputerki zbudowane w oparciu o układy SoC, potrafiące uruchamiać pełnoprawne systemy operacyjne. Jeśli nie potrzebujemy pracować z systemami czasu rzeczywistego w twardym reżimie czasowym, układy SoC często okazują się lepszym wyborem.

Fotografia 11. Mikrokontroler PIC32

Oprócz układów z rdzeniem ARM, które w znacznym stopniu zdominowały rynek, możemy się również spotkać z układami PIC32 (fotografia 11). Firma Microchip jak zawsze lubi chadzać własnymi ścieżkami i nie poddała się "ARM-owej rewolucji", stosując w swoich mikrokontrolerach architekturę MIPS32 o wydajności i parametrach zbliżonych do architektury ARM. Na bazie tej jednostki powstały cztery rodziny mikrokontrolerów oparte o różne rodzaje architektury MIPS32:

  • PIC32MX - oparta o rdzeń M4K, wydajnością zbliżona do Cortex-M3;
  • PIC32MZ - pracująca z częstotliwością maksymalną 250 MHz i wydajnością zbliżoną do układów zbudowanych w oparciu o Cortex-M7;
  • PIC32MM - przeznaczona do zastosowań ekonomicznych oraz energooszczędnych, stanowiąca odpowiednik architektury Cortex-M0;
  • PIC32MK - przeznaczona do zastosowań motoryzacyjnych.

Pomimo, że dostępne są darmowe środowiska programistyczne, układy nie zdobyły wielu zwolenników i stosowane są głównie w motoryzacji oraz przez konstruktorów przyzwyczajonych do firmy Microchip.

Układy SoC

Większymi kuzynami mikrokontrolerów są układy SoC, czyli układy scalone zawierające kompletny system elektroniczny, w tym układy cyfrowe, analogowe (także radiowe) oraz cyfrowo-analogowe. Poszczególne moduły tego systemu, ze względu na swoją złożoność, pochodzą zazwyczaj od różnych dostawców. Jednostka centralna może pochodzić od jednego dostawcy np. ARM, a porty komunikacji szeregowej od innego dostawcy lub stanowić własne opracowanie. Typowym obszarem zastosowań SoC są systemy wbudowane, a najbardziej rozpowszechnione są systemy oparte na procesorach w architekturze ARM. W przypadku układów SoC najczęściej używa się rdzeni procesorów aplikacyjnych rodziny ARM Cortex-A. Mają one jednostkę zarządzania pamięcią MMU (Memory Management Unit) wraz z rozbudowanymi układami pamięci cache. Mogą również występować w odmianach wielordzeniowych, pracujących w układzie multiprocesorowości symetrycznej (SMP).

Jeżeli zintegrowanie wszystkich bloków SoC na jednym podłożu półprzewodnikowym jest niemożliwe, poszczególne moduły wykonuje się na oddzielnych strukturach a całość zamyka w jednej obudowie. Rozwiązanie takie określane jest mianem SiP (System-in-package), jednak jest mniej opłacalne ekonomicznie, zwłaszcza przy produkcji półprzewodników w dużych seriach.

Innym rozwiązaniem, stosowanym np. w telefonach komórkowych, gdy we wnętrzu nie ma wiele miejsca, jest technologia PoP (Package-on-package). Polega na zastosowaniu dwu układów scalonych, z których jeden jest montowany pod drugim. Zwykle układ znajdujący się pod spodem jest układem typu SoC, a na nim umieszczony jest układ pamięci RAM lub/i FLASH (rysunek 12).

Rysunek 12. Budowa układu w technologii PoP

Największą różnicą pomiędzy mikrokontrolerami a układami SoC jest ilość pamięci, jakiej wymagają do pracy. W przypadku mikrokontrolerów jest to zwykle poniżej 100 kB pamięci RAM oraz mniej niż 1 MB pamięci FLASH. Układy SoC potrzebują zewnętrznych układów pamięci RAM i FLASH, a określenie "System on chip" jest, głównie w przypadku większych systemów, tylko przenośnią, która określa ukierunkowanie prac projektowych niż rzeczywistą realizację. Ponadto SoC wyposażone są w CPU o stosunkowo dużej mocy obliczeniowej, które dzięki jednostce zarządzania pamięcią MMU pozwalają uruchamiać systemy operacyjne znane z dużych systemów komputerowych, takie jak Linux czy QNX, a także mogą obsługiwać bardziej wyspecjalizowane peryferia. Musimy jednak pamiętać, że tego typu rozwiązania nie nadają się do zastosowania w twardych systemach czasu rzeczywistego, gdzie potrzebny jest maksymalnie deterministyczny czas odpowiedzi. Pamięć wirtualna oraz jednostka zarządzania pamięcią MMU powoduje, że czas reakcji systemu staje się niedeterministyczny. Dzieje się tak, ponieważ translacja adresu wirtualnego (VA) na adres fizyczny (PA) zajmuje układowi MMU pewien czas potrzebny na znalezienie i przejście tablicy stron znajdujących się w pamięci operacyjnej. Aby skrócić ten czas, stosowany jest mechanizm TLB, zwany pamięcią skojarzeniową, umożliwiający znaczące skrócenie czasu translacji adresu dla najczęściej używanych stron. Istnieje możliwość zablokowania niektórych adresów translacji w TLB na stałe, co często stosuje się w systemach czasu rzeczywistego z miękkim reżimem czasowym. Aby połączyć moc obliczeniową układu SoC oraz szybką reakcję najczęściej integruje się w jednym układzie procesor aplikacyjny np. Cortex-A służący do obsługi zaawansowanych zadań oraz dodatkowy rdzeń Cortex-M, na którym pracuje system operacyjny czasu rzeczywistego.

Rysunek 13. Uproszczony schemat wewnętrzny układu SoC

Przykład typowego układu SoC z rdzeniem ARM pokazano na rysunku 13. We wnętrzu układu znajdują się klasyczne układy peryferyjne znane z mikrokontrolerów, jednak zwykle znacznie bardziej rozbudowane. Charakterystyczny jest brak wewnętrznych pamięci RAM oraz pamięci programu FLASH, które muszą być dołączone w postaci zewnętrznych układów. Z uwagi na to, że magistrale pamięci muszą pracować z dużą częstotliwością (zwłaszcza pamięć SDRAM), potrzebny jest skomplikowany projekt kilkuwarstwowej płytki PCB, co znacząco podwyższa koszty.

Fotografia 14. Komputer SBC Raspberry Pi 4

W ostatnim czasie na rynku mamy wysyp komputerów SBC (Single Board Computer), bazujących na układach SOC z rdzeniem ARM. Konstruktorzy bardzo chętnie sięgają po nie z uwagi na wydajność i niską cenę. Takie rozwiązanie zwalnia z potrzeby projektowania skomplikowanego systemu, a dostęp do dużej bazy otwartego oprogramowania pozwala szybko opracować bardzo zaawansowane konstrukcje, dzięki czemu w krótkim czasie powstaje gotowe urządzenie. Z tej właśnie przyczyny skupimy się na krótkim przeglądzie najpopularniejszych gotowych rozwiązań SBC, które możemy stosować w krótkich seriach prototypowych. Przegląd rozpoczniemy od doskonale znanego komputera Raspberry Pi (fotografia 14), który doczekał się już czwartej odsłony. Ma 4 GB pamięci operacyjnej, wyposażony jest w 4-rdzeniowy 64-bitowy procesor z wysoko wydajnymi rdzeniami Cortex-A72. Jako kanał komunikacyjny może posłużyć moduł Wi-Fi 2,4 GHz/5 GHz lub port Ethernet. Komputer dysponuje układem graficznym wspomagającym obsługę 3D czy dekodowanie strumieni wideo. Największą zaletą tego rozwiązania jest bardzo bogata dokumentacja techniczna oraz całe mnóstwo oprogramowania przy doskonałym wsparciu społeczności. Komputer znajduje zastosowanie przy opracowaniu prototypów oraz do realizacji hobbystycznych projektów. Z uwagi na umiejscowienie złączy oraz użyte komponenty nie jest przeznaczony do zastosowań przemysłowych. Dostępnych jest wiele akcesoriów oraz dodatkowych modułów przeznaczonych do współpracy z RPi, takich jak obudowy czy dodatkowe płytki dołączane do 40-pinowego złącza, które już pełni funkcję pewnego standardu.

Fotografia 15. Komputer SBC Raspberry Pi Zero

Przy omawianiu Raspberry Pi warto wspomnieć również o skromniejszej wersji - Raspberry Pi Zero (fotografia 15). Ma jednordzeniowy układ SoC z nieco już leciwym rdzeniem ARM11. Wyposażony jest w 512 MB pamięci operacyjnej oraz procesor graficzny umożliwiający wyświetlanie obrazu. Jego największą zaletą jest cena wynosząca ok. 29 złotych brutto, co stawia pod znakiem zapytania sensowność używania większych mikrokontrolerów jednoukładowych, które niejednokrotnie kosztują więcej niż cały komputer. Należy jednak pamiętać, że do działania potrzebuje karty SD, co podnosi koszt rozwiązania. Istotną zaletą jest również złącze, które umożliwia wykorzystanie tego komputera jako bazy dla własnego urządzenia. Konstrukcja nie jest przeznaczona do zastosowania w warunkach przemysłowych.

Fotografia 16. Komputer SBC BeagleBone Black

Jeśli myślimy o poważniejszych zastosowaniach, dużo lepszym rozwiązaniem jest użycie płytki, która ma zintegrowaną pamięć NAND Flash lub eMMC. Dobrym wyborem wydają się płytki wyposażone w mikrokontroler Sitara AM335x firmy Texas Instruments. Przykładem jest komputer BeagleBone Black (fotografia 16) taktowany z częstotliwością 1 GHz, wyposażony w 512 MB pamięci RAM oraz 4 GB pamięci Flash. Komputer ma szereg interfejsów komunikacyjnych: USB, HDMI oraz Ethernet. Ciekawym rozwiązaniem w cenie około 100 zł jest płytka NanoPI Neo 2, dysponująca 64-bitowym 4-rdzeniowym procesorem Cortex-A53 taktowanym zegarem 1 GHz. Zawiera ona 512 MB pamięci operacyjnej DDR3, pamięć eMMC o wielkości 8 GB (na której możemy zainstalować system operacyjny oraz oprogramowanie) oraz gigabitowe złącze Ethernet i dwa porty USB. Jeżeli potrzebujemy bardzo dużej mocy obliczeniowej, np. do przetwarzania obrazów czy wirtualnej rzeczywistości, bardzo ciekawą propozycją jest płytka Nvidia Jetson Nano (fotografia 17) wyposażona w 4-rdzeniową jednostkę centralną z rdzeniami Cortex-A53 oraz procesor graficzny Nvidia Maxell ze 120 rdzeniami CUDA. Całość taktowana jest z częstotliwością 1,43 GHz.

Fotografia 17. Komputer SBC Nvidia Jetson Nano

Ostatnim rozwiązaniem zwykle stosowanym w najbardziej wymagających systemach wbudowanych jest zastosowanie układu programowalnego FPGA oraz zaimplementowanie procesora w postaci "SoftCore". Jest to rozwiązanie stosunkowo rzadko stosowane w systemach profesjonalnych, głównie z uwagi na znaczne koszty, ponadto do pracy wymagają dodatkowego osprzętu takiego jak konfiguratory oraz sekwensery zasilania. Sytuacja ulega zmianie dzięki takim układom jak Lattice MachO2 czy MAX10 firmy Altera, które integrują gotowy do działania układ FPGA w pojedynczej strukturze, co stanowi odpowiednik mikrokontrolera jednoukładowego w świecie układów programowalnych.

Układ SoC czy mikrokontroler jednoukładowy?

Konstruktor urządzeń wbudowanych dysponuje zarówno układami opartymi o mikrokontrolery jednoukładowe, jak i zaawansowanymi układami SoC. Dobór odpowiedniego rozwiązania przy szerokiej ofercie rynkowej nie jest łatwy.
Najprostsze urządzenia wbudowane, takie jak sterowniki pieca CO, sterowniki klimatyzacji czy termostaty zwykle budowane są w oparciu o mikrokontrolery, głównie ze względu na koszty oraz z uwagi na to, że do sterowania takimi procesami wystarczą nawet najprostsze rozwiązania. W bardziej zaawansowanych urządzeniach, gdzie potrzebna jest obsługa sieci, kolorowych wyświetlaczy LCD itp., wybór nie jest już taki oczywisty. Cena bardziej zaawansowanych mikrokontrolerów, potrafiących podołać wyżej podanym zadaniom, może nawet przewyższać ceny prostszych rozwiązań opartych o układy SoC. Dodatkową zaletą zastosowania układu SoC jest możliwość użycia systemu operacyjnego Linux i jego bogatych zasobów. W przypadku mikrokontrolera mamy do dyspozycji systemy operacyjne czasu rzeczywistego a większość oprogramowania musimy przygotować samodzielnie.

Innym kryterium wyboru jest określenie, czy urządzenie musi sterować procesami czasu rzeczywistego. W przypadku gdy nie ma takich wymagań lub wymagany jest jedynie system typu "soft realtime", możemy zastosować jądro Linux z dodatkowymi łatkami czasu rzeczywistego. Natomiast gdy mamy do czynienia z systemem typu "hard realtime", zmuszeni jesteśmy do zastosowania mikrokontrolera jednoukładowego oraz systemu czasu rzeczywistego. W przypadku bardziej zaawansowanych urządzeń czasu rzeczywistego możemy rozważyć scenariusz z dwoma układami, gdzie mikrokontroler steruje procesem czasu rzeczywistego, natomiast inne zadania, jak obsługa wyświetlacza graficznego, obsługa użytkownika czy komunikacja z systemem zdalnym, oddelegowana jest do układu SoC.

Oprogramowanie

Istotną kwestią związaną z systemami wbudowanymi jest oprogramowanie, zarówno to działające w systemie docelowym, jak i wspomagające tworzenie oprogramowania dla tych systemów, takie jak: kompilatory, debuggery, zintegrowane środowiska programistyczne itp. W pierwszej kolejności omówimy oprogramowanie stosowane w systemach budowanych w oparciu o mikrokontrolery. W drugiej części omówimy oprogramowanie stosowane w rozwiązaniach z układami SOC, po czym w ostatniej części zajmiemy się oprogramowaniem wspomagającym.

Oprogramowanie dedykowane mikrokontrolerom jednoukładowym

Oprogramowanie dla mikrokontrolerów obecnie tworzymy głównie w językach ANSI - C oraz C++. W przypadku oprogramowania dla najmniejszych mikrokontrolerów 8-/16-bitowych z uwagi na mocno ograniczone zasoby stosowany jest niemal wyłącznie ANSI - C. Popularny niegdyś assembler znajduje zastosowanie głównie w postaci wstawek np. przy pisaniu systemów operacyjnych oraz w najmniejszych mikrokontrolerach, pozbawionych stosu sprzętowego lub dysponującego kilkudziesięcioma bajtami pamięci operacyjnej.

Jeśli system wbudowany zawiera bardziej zaawansowany mikrokontroler 32-bitowy z rdzeniem ARM, dysponujący większą pamięcią flash i RAM, możemy użyć języka C++ w najnowszym standardzie ISO/IEC 14882:2017 i jest to rozwiązanie zalecane. Umiejętne stosowanie języka C++ zwiększa bezpieczeństwo kodu oraz skraca czas przygotowania oprogramowania, nie podnosząc znacząco wymagań sprzętowych.

Oprogramowanie na najmniejsze systemy wbudowane tworzone jest bez użycia systemu operacyjnego. W takim przypadku program działa w trybie foreground-backgroud, bazując na maszynach stanów oraz pętlach obsługi zdarzeń. Duża część kodu również wykonuje się bezpośrednio w procedurach obsługi przerwań. O ile napisanie tego typu oprogramowania dla najprostszych projektów nie nastręcza wielu problemów, tak w przypadku bardziej zaawansowanych systemów brak mechanizmów wielozadaniowości staje się problematyczny. W takich okolicznościach z pomocą przychodzą nam systemy operacyjne czasu rzeczywistego uruchamiane na małych mikrokontrolerach. Wymagania stawiane przed współczesnymi systemami, takie jak obsługa sieci, kolorowe wyświetlacze dotykowe, nowoczesne i skomplikowane w obsłudze magistrale typu USB, powodują, że napisanie tego typu oprogramowania bez systemu jest niewykonalne w rozsądnym przedziale czasowym. Najnowsze 32-bitowe układy wyposażone są w takie ilości pamięci operacyjnej, że uruchomienie systemu na układzie kosztującym kilka złotych nie stanowi wielkiego wyzwania. Na rynku istnieje wiele systemów operacyjnych przeznaczonych dla małych mikrokontrolerów, przy czym znakomitą większość stanowią rozwiązania otwartoźródłowe. W znakomitej większości przypadków systemy dla mikrokontrolerów nie są systemami operacyjnymi w pełnym tego słowa znaczeniu, zawierającymi mechanizmy sterowników urządzeń, zarządzanie pamięcią, zarządzanie użytkownikami czy obsługę sieci. Należy je traktować bardziej jako bibliotekę zapewniającą obsługę wątków niż pełnoprawny system operacyjny. Typowe API takich systemów pokazano na rysunku 18. Serce stanowi mechanizm obsługi wątków/zadań oraz mechanizm komunikacji i synchronizacji międzyprocesowej. Dodatkowe funkcje, jakie może pełnić system, to zarządzanie pamięcią czy sterowniki urządzeń, jednak nie są to elementy obowiązkowe.

Rysunek 18. API typowego systemu operacyjnego dla małych mikrokontrolerów

W przypadku układów pozbawionych jednostki MMU, sterowniki urządzeń nie są konieczne, ponieważ system oraz aplikacje pracują we wspólnej przestrzeni i mają dostęp do wszystkich zasobów, w tym do systemu przerwań oraz przestrzeni adresowej urządzeń peryferyjnych.

Jednym z najstarszych oraz powszechnie używanych systemów jest FreeRTOS, który możemy uruchomić na całej gamie mikrokontrolerów, począwszy od 8-bitowych AVR, skończywszy na 32-bitowych ARM. Został napisany w języku C i jest nastawiony głównie na prostotę oraz możliwość szybkiego przenoszenia na różne architektury. Obecnie może działać na ponad 30 różnych architekturach sprzętowych i w swojej podstawowej postaci zawiera obsługę mechanizmów takich jak tworzenie zadań, elementy synchronizacji i komunikacji międzyprocesowej oraz podstawową obsługę zarządzania pamięcią dynamiczną. Jego główną wadą jest to, że jest napisany w języku ANSI C z dużą liczbą makr i różnych współcześnie niezalecanych technik programistycznych.

Interesującym systemem jest ChibiOS dostępny na licencji GPL, przeznaczony dla mikrokontrolerów 8-/16-/32-bitowych. Jest dostępny na mniejszą liczbę architektur sprzętowych i w jego podstawowej wersji może zajmować od 1,2 kB do 5,5 kB pamięci FLASH. Zawiera bardziej rozbudowane mechanizmy synchronizacyjne a kod źródłowy jest dużo nowocześniejszy od wspomnianego wcześniej systemu. Oprócz samej biblioteki obsługi wątków jego opcjonalną część może stanowić mechanizm sterowników urządzeń oraz biblioteka graficzna uGFX przeznaczona do obsługi różnych wyświetlaczy graficznych.

Innym systemem doskonale znanym czytelnikom jest ISIX zapewniający podobną funkcjonalność do przedstawionego wcześniej systemu ChibiOS. Jego jądro zostało napisane w języku ANSI-C, natomiast pozostała część powstała z użyciem standardu C++17.

Jeżeli do dyspozycji mamy większą ilość pamięci, możemy użyć systemu czasu rzeczywistego NuttX przeznaczonego dla 8- i 32-bitowych mikrokontrolerów. Jego głównym założeniem jest zgodność ze standardem ANSI oraz POSIX, co umożliwia skompilowanie oraz uruchomienie dużej części bibliotek przeznaczonych np. dla systemu operacyjnego Linux. Jest jednym z najbardziej rozbudowanych systemów tego typu, ponieważ oprócz API standardu POSIX, zawiera podsystem sterowników urządzeń, wirtualny system plików i obsługę sieci. Obsługuje również Wi-Fi, NTFS, karty sieciowe, USB zarówno host, jak i device. Niestety duża zgodność ze wspomnianymi systemami pociąga za sobą stosunkowo duże jak na mikrokontrolery wymagania pamięciowe oraz sprzętowe.

Duża popularność technologii IoT spowodowała, że systemami dla mikrokontrolerów zainteresowały się duże koncerny, przykładem czego jest system LiteOS, którego autorem jest firma Huawei. Dostępny jest na liberalnej licencji BSD i możemy go uruchomić na najważniejszych architekturach sprzętowych: ARM, x86, RISC-V. Charakteryzuje się bardzo lekką budową w podstawowej wersji zajmując mniej niż 10 kB pamięci FLASH. Jego głównym przeznaczeniem jest Internet Rzeczy, zatem szczególny nacisk położono na systemy łączności, krótki czas odpowiedzi oraz niski pobór energii. Obsługuje takie systemy łączności jak: Wi-Fi, Ethernet, BLE, ZigBee oraz wiele innych popularnych protokołów IoT. Systemem o podobnym przeznaczeniu jest ZephyrOS rozwijany pod skrzydłami Linux Foundation a w jego rozwój włączeni są m.in. Intel, NXP, Linaro czy Texas Instruments.

Kilkanaście lat temu jedynym oprogramowaniem dostarczanym przez producentów mikrokontrolerów jednoukładowych były zazwyczaj pliki nagłówkowe z opisem rejestrów. Reszta spoczywała na barkach programisty. Obecnie sytuacja wygląda inaczej, producenci prześcigają się nawzajem, dostarczając kompletne biblioteki, które za pomocą wywołania kilku funkcji umożliwiają wygodne uruchomienie układów peryferyjnych znajdujących się w mikrokontrolerze. Co prawda w starszych mikrokontrolerach układy peryferyjne były znacznie prostsze, wystarczyło zazwyczaj przestawić kilka bitów w rejestrach i układ był gotowy do pracy. Współczesne układy peryferyjne są coraz bardziej złożone i dysponują szeregiem zaawansowanych opcji, a ich obsługa jest dużo bardziej skomplikowana. Czas, jaki konstruktorzy mają na opracowanie urządzenia, uległ znacznemu skróceniu. W związku z tym koniecznością stało się dostarczanie bibliotek układów peryferyjnych, aby uprościć i przyspieszyć czas przygotowania projektu. Spójrzmy np. jak wygląda obsługa diody LED dołączonej do portu GPIO w przypadku 89C2051 i STM32F103 bezpośrednio z wykorzystaniem rejestrów (listing 1). Jak łatwo możemy zauważyć, kod sterujący diodą LED w przypadku mikrokontrolera 8051 jest bardzo prosty i wymaga w zasadzie jedynie zmiany stanu odpowiedniej linii portu. W przypadku mikrokontrolera STM32 uruchomienie portu GPIO w kierunku wyjścia wymaga wykonania szeregu czynności, takich jak włączenie sygnałów zegarowych oraz konfiguracji portu w kierunku wyjścia. Ponadto w kodzie pominięto inicjalizację i konfigurację początkową, której wymaga STM32, a w przypadku 8051 po włączeniu zasilania układ jest od razu gotowy do pracy. Oczywiście im bardziej wyszukaną funkcję pełni układ peryferyjny, tym jego obsługa jest bardziej skomplikowana. Dziś na szczęście większość znaczących producentów układów dostarcza gotowe biblioteki, tak więc nie będziemy musieli operować na rejestrach jak jeszcze kilkanaście lat temu. W przypadku mikrokontrolerów STM32 jest to biblioteka STM32Cube integrująca się z oprogramowaniem narzędziowym dostarczonym przez producenta. Zupełnie oddzielną kwestią jest jakość kodów źródłowych dostarczanych przez producentów, która często bywają kiepska.

Listing 1. Program do sterowania diodą LED dołączoną do portu GPIO w przypadku 89C2051 i STM32F103
//89c2051 (SDCC)
int main(void){
	for(;;) {
		P2_1 = 1;
		for(int i=0; i<10000; ++i);
		P2_1 = 0;
		for(int i=0; i<10000; ++i);
	}
}

//STM3F103 (GCC)
#include <stm32f30x.h>
int main(void){
	RCC -> AHBENR |= 1 << 21;
	GPIOE -> MODER |= 1 << 26;
	GPIOE -> ODR |= 1 << 13;
	while(1){
		for (int i = 0; i < 1000000; ++i) ;
		GPIOE -> ODR |= 1 << 13;
		for (int i = 0; i < 1000000; ++i) ;
		GPIOE -> ODR &= ~(1 << 13);
	}
}

Oprogramowanie przeznaczone dla rozwiązań SoC/SBC

Układy SoC dzięki zastosowaniu jednostki centralnej, będącej pełnoprawnym mikroprocesorem z obsługą pamięci wirtualnej, otwierają dostęp do bogatej bazy oprogramowania przeznaczonego dla standardowych systemów komputerowych. Możliwość uruchomienia pełnoprawnego systemu operacyjnego ogólnego przeznaczenia daje dostęp do bogatej bazy oprogramowania oraz bibliotek, które rozwinęły się w ciągu ostatnich 10 lat. W dobie coraz większych wymagań stawianych systemom wbudowanym, takich jak obsługa wyświetlacza graficznego czy skomplikowanego interfejsu, jedyną możliwością przygotowania oprogramowania w krótkim czasie jest użycie pełnoprawnego systemu wraz z szeroką bazą oprogramowania. Wtedy możliwe jest skupienie się na przygotowaniu głównej funkcjonalności urządzenia bez potrzeby wnikania w niskopoziomowe szczegóły. Znacząco obniżamy koszt opracowania oraz skracamy czas dostarczenia urządzenia do klienta. Szczególną popularność zyskują tutaj systemy operacyjne rozwijane w oparciu o model otwartego oprogramowania. Rozwijane są przez społeczność (głównie duże firmy), co zapewnia znaczące oszczędności zarówno czasu, jak i pieniędzy potrzebnych do rozwoju systemu.

Najpopularniejszym systemem operacyjnym mającym zastosowanie w systemach wbudowanych jest Linux, który działa praktycznie na wszystkich obecnie istniejących układach SoC. Jądro Linux jest w świecie urządzeń wbudowanych standardem, więc każdy znaczący producent układu zapewnia wsparcie dla tego systemu. Używając słowa Linux, mamy na myśli samo jądro systemu operacyjnego. Natomiast cały system oprócz samego jądra stanowią biblioteki oraz aplikacje. Budowę typowego stosu programowego systemu Linux pokazano na rysunku 19. Na najniższym poziomie mamy jądro systemu, które jest odpowiedzialne za zarządzanie pamięcią, zarządzanie procesami oraz obsługę urządzeń wejścia/wyjścia za pośrednictwem sterowników urządzeń. Jądro dostarcza standardowe API wywołań systemowych, jednak najczęściej aplikacje użytkownika nie odwołują się bezpośrednio do niego, tylko korzystają z bibliotek pośrednich. Najważniejszą biblioteką zapewniającą podstawowe API dla aplikacji jest libc. Biblioteka zapewnia aplikacjom standardowy interfejs wymagany przez aplikacje pisane w języku C oraz dostęp do najbardziej niskopoziomowych funkcji związanych z systemem, takich jak: zarządzanie procesami, zarządzanie pamięcią, komunikacja międzyprocesowa itp. Kolejnymi istotnymi bibliotekami systemowymi są: libDRM, odpowiedzialna za obsługę systemu graficznego, libasound odpowiedzialna za obsługę dźwięku oraz libevdev, odpowiedzialna za obsługę urządzeń wejściowych, takich jak myszki, klawiatury, ekrany dotykowe itp. Oczywiście takich bibliotek jest znaczenie więcej. Z punktu widzenia systemów wbudowanych istotną biblioteką jest libusb, umożliwiająca tworzenie oprogramowania dla urządzeń USB w postaci aplikacji, bez konieczności bezpośredniego tworzenia sterowników pracujących w przestrzeni jądra.

Rysunek 19. Linux architektura systemu

Istnieje wiele odmian systemu Linux zwanych dystrybucjami przygotowanymi przez różnych producentów oraz społeczność. O ile przygotowanie systemu dla komputerów PC nie stanowi problemu, z uwagi na to, że architektura PC jest ściśle określona, o tyle w przypadku systemów wbudowanych, które występują w wielu odmianach, przygotowanie dystrybucji jest znacznie bardziej skomplikowane. Po pierwsze mamy wiele architektur i konfiguracji sprzętowych, a po drugie przed systemami wbudowanymi stawiamy zupełnie różne wymagania. Oprogramowanie routera będzie wymagać zupełnie innego zestawu bibliotek i aplikacji niż oprogramowanie służące do sterowania maszyną CNC. Istnieją co prawda generyczne dystrybucje przeznaczone dla systemów wbudowanych, jednak najczęściej używane są jedynie na etapie prototypowania lub amatorskich projektów. Największym problemem z gotowymi dystrybucjami jest to, że zawierają one całe mnóstwo oprogramowania niekoniecznie dostosowanego i zoptymalizowanego dla danego rozwiązania. Tworząc własną dystrybucję od podstaw, możemy uszyć system na miarę dostosowany do specyfiki projektu. Najpopularniejszymi gotowymi dystrybucjami przeznaczonymi dla architektury ARM są: Debian, Ubuntu for the Internet of Things, Arch Linux ARM. Są to dystrybucje zbliżone do tych znanych z komputerów PC, przy czym musimy pamiętać, że instalacja na komputerku SBC bywa bardziej skomplikowana. Korzystając z gotowej dystrybucji, musimy się liczyć z tym, że często będzie konieczne dostarczenie własnego bootloadera czy jądra z opcjami specyficznymi dla własnej płytki SBC.

Jeśli planujemy dostarczyć urządzenie, które nie jest prototypem, dużo lepszym rozwiązaniem jest zbudowanie systemu Linux ze źródeł dostosowanych do naszego urządzenia. Przygotowując obraz dysku w ten sposób, mamy kontrolę nad całym procesem kompilacji włącznie z dedykowaną optymalizacją dla naszej architektury oraz dostosowaniem poszczególnych komponentów dokładnie do naszych wymagań. Takie podejście pozwala na zbudowanie bezpiecznego i wysoce zoptymalizowanego systemu przeznaczonego do konkretnych potrzeb.

Kompilacja całego systemu krok po kroku jest zadaniem dosyć skomplikowanym i czasochłonnym, co już dawno temu zostało dostrzeżone przez społeczność, która przygotowała odpowiednie narzędzia automatyzujące przygotowanie własnej dystrybucji. Obecnie istnieją dwa konkurencyjne rozwiązania, prostszy Buildroot oraz dużo bardziej wyszukany Yocto Project.

Buildroot jest zbiorem zestawu plików Makefile oraz zestawem łatek automatyzujących proces przygotowania obrazu systemu Linux. Umożliwia wybór poszczególnych komponentów systemu oraz opcji konfiguracyjnych za pomocą menu KCONFIG. Do prawidłowego działania potrzebujemy jedynie narzędzia make oraz kompilatora potrafiącego zbudować kod dla architektury hostującej. Pozostałe elementy włącznie z kompilatorem skrośnym na docelową architekturę są kompilowane bezpośrednio z kodów źródłowych. Buildroot wspiera wiele architektur sprzętowych: x86, ARM, MIPS, PPC. Pozwala również na wybór jednej z kilku dostępnych bibliotek języka C. Umożliwia przygotowanie obrazu do wgrania na płytkę docelową oraz budowanie poszczególnych komponentów z wykorzystaniem managera pakietów co może być przydatne, jeśli planujemy zapewnić możliwość zdalnej aktualizacji oprogramowania.

Obecnie najbardziej zalecanym narzędziem do budowy własnych dystrybucji jest Yocto Project wspierane i rozwijane przez Linux Foundation. Przeznaczone jest do tworzenia własnych dystrybucji Linuksa dla urządzeń IoT i wspiera ponad 22 różne architektury sprzętowe. Bazuje na podejściu obiektowym oraz warstwach oprogramowania, które są na siebie nakładane. Jego największą zaletą jest całe mnóstwo plików konfiguracyjnych opisujących sposób budowy oraz kompilacji poszczególnych bibliotek, programów czy innych komponentów. Pozwala to na przygotowanie własnej dystrybucji Linuksa dostosowanej do urządzenia i zawierającej jedynie niezbędne pakiety.

Oprócz systemu Linux, który króluje w systemach wbudowanych (podobnie jak Windows na desktopach), pewną popularność mają również inne systemy operacyjne przeznaczone do tego celu. Jednym z takich systemów jest Android Things zaprezentowany przez firmę Google w 2015 roku. Jego głównym przeznaczeniem są urządzenia wbudowane o niewielkiej ilości pamięci RAM rzędu 32 MB. System wspiera komunikację z wykorzystaniem protokołów BT oraz Wi-Fi. Podobnie jak w klasycznym Androidzie zawiera on maszynę wirtualną JVM, a więc możemy go wygodnie programować w języku Java. Do dyspozycji mamy bogaty zestaw bibliotek w tym obsługę tak niskopoziomowych interfejsów jak: I2C, SPI czy obsługę portów GPIO bezpośrednio z poziomu Javy.

Innym systemem mającym pewną popularność, jest Windows 10 IoT firmy Microsoft. Jest systemem płatnym z zamkniętym kodem źródłowym przeznaczonym dla urządzeń dysponujących relatywnie niewielką ilością pamięci RAM. Bazuje na jądrze NT i umożliwia pisanie aplikacji w technologii .NET. Umożliwia również korzystanie z API Win32 znanego z pełnej wersji systemu Windows. Może pracować zarówno na procesorach o architekturze x86, jak i ARM, jednak z uwagi na to, że konieczne jest wnoszenie opłat licencyjnych, nie jest on zbyt popularny.

Oprogramowanie wspomagające tworzenie oprogramowania dla systemów wbudowanych

Odpowiednie narzędzia programistyczne pozwalają znacząco przyspieszyć projektowanie oprogramowania oraz skrócić czas wprowadzenia produktu na rynek. Na początku rozwoju systemów wbudowanych, gdy królowały układy 8-bitowe, oprogramowanie najczęściej było pisane w asemblerze. W późniejszym czasie na rynku zaczęły pojawiać się kompilatory języka C, pozwalające znacznie ułatwić przygotowanie oprogramowania.

W międzyczasie okazało się, że wczesne architektury, takie jak 8051 czy PIC, nie do końca były dostosowane do wykonywania kodu wygenerowanego za pomocą kompilatora, chociażby z powodu braku adresowalnego stosu (PIC) czy różnych sposobów adresowania pamięci (8051). Producenci mikrokontrolerów dostrzegli ten problem i zaczęli opracowywać lub modyfikować rdzenie mikrokontrolerów, tak aby lepiej współpracowały z kompilatorami języka C. W tym samym czasie na rynku pojawiły się zintegrowane środowiska programistyczne, które oprócz samego kompilatora zawierały zintegrowany edytor, debugger czy kreator projektów umożliwiający przygotowanie bazy nowego projektu za pomocą kilku kliknięć. Na początku były to rozwiązania komercyjne, które kosztowały bardzo duże pieniądze i były poza zasięgiem małych firm. Z czasem społeczność zaczęła rozwijać kompilator GCC dla architektury AVR oraz ARM, równolegle dla architektury PIC i 8051 rozwijany był kompilator SDCC. Wczesnym wersjom kompilatorów z tamtych czasów dużo brakowało do rozwiązań komercyjnych zwłaszcza pod kątem wielkości i szybkości otrzymywanego kodu maszynowego. Producenci dostrzegli, że narzędzia oferowane po wysokich cenach nie są dobrym rozwiązaniem, ponieważ znacząco zmniejszają sprzedaż. W związku z powyższym firmy zaczęły się interesować rozwojem kompilatorów na darmowych licencjach. W międzyczasie na rynku pojawiło się również środowisko Eclipse umożliwiające tworzenie własnego IDE. Bazując na Eclipse oraz GCC, producenci zaczęli dostarczać darmowe zintegrowane środowiska programistyczne. Obecnie każdy szanujący się producent mikrokontrolerów dostarcza tego typu środowiska nieodpłatnie. Pomimo istnienia środowisk IDE, zdaniem autora nadal lepszym rozwiązaniem jest przygotowanie i tworzenie oprogramowania dla mikrokontrolerów, bazując bezpośrednio na narzędziach takich jak make czy waf, ponieważ do prawidłowego zbudowania projektu potrzebny będzie jedynie kompilator oraz mamy możliwość zbudowania całego projektu za pomocą automatycznych skryptów. Istotnym aspektem jest to, że mamy pełną kontrolę nad projektem i jesteśmy zupełnie niezależni od producenta.

Jeśli budujemy aplikację dla mikrokontrolerów jednoukładowych, jedyną potrzebną rzeczą do napisania i uruchamiania oprogramowania będzie zintegrowane środowisko IDE oraz ewentualnie interfejs JTAG. W przypadku JTAG sytuacja wyglada podobnie jak w przypadku kompilatorów i środowisk IDE, gdzie kiedyś narzędzia te kosztowały tysiące dolarów a obecnie możemy je nabyć w cenie nieprzekraczającej kilkudziesięciu złotych.

W przypadku oprogramowania dla systemów opartych o układy SoC z systemem Linux sytuacja wygląda inaczej i zależy od tego, czy będziemy zajmować się oprogramowaniem bootloadera, sterownikami dla jądra systemu Linux, czy pisaniem oprogramowania w przestrzeni użytkownika. Przy tym w tekście skupimy się głównie na ostatnim przypadku, ponieważ większość układów SoC ma wsparcie dla bootloadera oraz jądra systemu Linux dostarczone przez producenta. Zestaw oprogramowania potrzebny do tworzenia aplikacji dla przestrzeni użytkownika niczym nie różni się od zestawu przeznaczonego do tworzenia oprogramowania dla komputera PC.

Potrzebować będziemy: kompilatora skrośnego, debuggera gdb pracującego na komputerze PC oraz gdb serwera pracującego na płytce docelowej. Wszystkie te narzędzia są automatycznie budowane, jeśli do tworzenia własnej dystrybucji wykorzystujemy narzędzia Yocto lub Buildroot. Dodatkowo, jeśli jesteśmy przyzwyczajeni do zintegrowanych środowisk graficznych, możemy skorzystać z darmowych edytorów, takich jak "Eclipse" lub "Visual Studio Code". Pisanie oprogramowania w Linuksie jest zdecydowanie bardziej wygodne niż tworzenie aplikacji dla mikrokontrolerów, ponieważ do dyspozycji mamy dużo więcej pamięci oraz jednostkę MMU, która w przypadku rażących błędów aplikacji zapobiega awarii całego systemu.

Podsumowanie

Na przestrzeni lat zmieniały się systemy wbudowane oraz ich zastosowania. Dawniej można było spotkać je jedynie w najbardziej zaawansowanych urządzeniach a narzędzia przeznaczone do tworzenia oprogramowania były bardzo drogie. Z czasem stawały się coraz bardziej dostępne a ich cena spadła poniżej kosztu rozwiązań realizowanych bez udziału mikroprocesorów. Do wzrostu popularności przyczyniła się również dostępność darmowych narzędzi. Aktualnie wraz z rozwojem interfejsów graficznych coraz bardziej zauważalny jest trend odchodzenia od mikrokontrolerów jednoukładowych na rzecz systemów zbudowanych w oparciu o układy SoC. Myślę, że z czasem, jeśli producentom uda się zintegrować w jednym układzie SoC pamięć SDRAM, rozwiązania tego typu będą wypierały mikrokontrolery jednoukładowe.

Lucjan Bryndza, EP
Lucjan.bryndza@boff.pl

Artykuł ukazał się w
Elektronika Praktyczna
listopad 2019

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik kwiecień 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio marzec - kwiecień 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje kwiecień 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich maj 2024

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów