ARM_minimal.
Nebojme se ARM.

Fenoménem dnešní doby je v oblasti vestavných systémů přechod z 8.bitových platforem na 32.bitové, především ARM a to zejména Cortex-M. I když hlavním důvodem je asi především cena, protože poměrně dobře vybavený Cortex-M0 je možné pořídit v kusovém množství už za 1$, podívejme se i na důvody jiné.

Proč nemusí být přechod na novou platformu tak bolestivý.

Důvodů je hned několik. V dalším textu budeme důsledně používat volně dostupné nástroje z rodiny open source i při vědomí, že existují komfortní komerční programy, které práci na projektech velmi usnadňují. Je to ovšem otázka k diskusi, vycházíme z předpokladu (možná mylného), že je lépe vidět trochu lépe pod povrch věcí aby byly naše programy poněkud kvalitnější. Předem připravené knihovny, které lze ovšem používat i v open source nás odstiňují od podstaty, která bývá většinou dost prostá. Nakonec i některé ty komerční programy používají v jádře open source překladače, jen je to prostě dobře nastaveno a připraveno k okamžitému použití. Zásadním problémem platformy ARM není její velká složitost na níž se podepsala léta usilovného vývoje, ale spíš bídná dokumentace. Tedy ne že by nebyla, je jí však taková spousta, že vykopat z jejích hlubin ta zrnka podstatného vyžaduje spoustu úsilí. A právě to mnohé odradí. Ale není to zase tak zlé. Co budeme potřebovat (software) :

  1. Binutils. To je kolekce programů jež umožní překlad z jazyka symbolických adres (assembleru), sestavení výsledného obrazu kódu a jeho převedení na formát vhodný pro upload do flash cílového procesoru. Zarytý assemblerista nic víc nepotřebuje.
  2. Pokud tedy nejsme masochisty, budeme se snažit použít nějaký trochu lepší jazyk pro popis problému. Sice se rozmáhá java, javascript, python, ale to není zrovna nejvhodnější pro takhle omezené systémy. Pak jsou vhodnější, ale poměrně obskurní jazyky jako forth nebo esl - viz Příklad 3 - Blikáme pomocí ESL. Obvykle se však používá jazyk C, i když vůči němu mají mnozí velké výhrady. Ale protože se v průběhu vývoje hodně rozšířil, je dostupný ve volné verzi na řadu různých platforem a pokud se v něm píše trochu umravněně, jednou napsaný kód lze přenést na jinou platformu s mnohem menšími potížemi než např. v čistém assembleru. Zde máme na výběr
    • GCC Ten je použit v příkladu, je dost propracovaný, stále se vyvíjí, dostupný pro velkou škálu platforem včetně např. AVR, PIC32 (MIPS) aj. Zmíníme ještě, že je možné pomocí něj překládat i z jiných jazyků, velmi dobře použitelný je i pro vestavné systémy C++. To zvětšuje i efektivitu práce, aniž by se to nějak negativně podepsalo na rychlosti nebo délce kódu.
    • clang + LLVM Nový univerzální překladač se zajímavou architekturou, který zde zmiňuji především proto, že jediný nástroj umožňuje generovat kód pro různé platformy. GCC musí být pro každou platformu zvlášť přeložen. Dynamicky se vyvíjí, sleduje nové trendy (je napsán a určen především pro C++), ale zatím to vypadá, že GCC umí přece jen trochu lépe optimalizovat. Uvidíme, co ukáže budoucnost, ale obávám se, že už nyní je to obrovský moloch a bude stále růst.
  3. Nějakou C-čkovou knihovnu. Obvykle se používá Newlib z dílny Redhatu, ale lze použít i jiné nebo (jako zde) se lze obejít bez ní.

Pro úplnost je třeba dodat, že jak binutils, tak překladače C, C++ jsou tzv. křížové nástroje, tedy spouštějí se na jiné architektuře (zpravidla běžné PC) než pro jakou generují kód - ARM thumb, thumb2. Tyto nástroje jsou distribuovány jako zdrojové kódy a potřebné programy pak musíme zkompilovat. To je zdlouhavá procedura, která nemusí vždy vést k žádoucímu cíli. A protože to už někdo udělal za nás, využijeme toho a hotové binárky si stáhneme třeba zde. Je zde ještě jeden dobrý důvod proč použít předkompilovaný balík - software se postupně vyvíjí, opravují se v něm chyby a přidávají nové vlastnosti. Proto je dobré používat co nejnovější. Jenže po určité době dojde k vydání nové tzv. major verze, která obsahuje především různá vylepšení, ale zase nemusí být úplně dobře odladěna. Předkompilovaný balík je určitým kompromisem - zpravidla obsahuje software, který je dost nový na práci, ale zase dost starý na to, aby v něm bylo co nejméně chyb.

Sice to není nezbytné, ale je dobré použít pro řízení překladu nějaký make systém. Je to použito i v tomto jednoduchém příkladu, v komentářích Makefile. je vidět parametry použité při překladu a sestavení. Ve Windows postačí MinGW, nemusí být ani MSYS. V Linuxu to většinou není třeba vůbec řešit, většina distribucí make obsahuje.

A jaký je tedy rozdíl mezi 8-bit platformou např. oblíbenou 8051 a ARM Cortex-M0 z hlediska aplikačního programátora ? Ne zase tak obrovský, ten 32.bit je z mnohého hlediska i prostší.

  1. Především svět ARM je většinově třívoltový, pětivoltové jsou jen výjimky. Tedy napájení je max. 3.3V, spíše však méně. I když IO piny mohou být 5V tolerantní, je lépe celý návrh přizpůsobit logice 3V. Což nemusí být problém.
  2. 8051 měla několik adresních prostorů (data, pdata, xdata, code, sfr, bit, sbit), které bylo nutno dost důsledně rozlišovat už při psaní kódu i když jsme si to ne vždy plně uvědomovali, protože překladač mnohé zařídil (byť ne vždy efektivně) za nás. Kdo pamatuje starší verze avr-gcc, ví jak složité bylo pracovat s konstantami, uloženými ve flash. Pojmenované adresní prostory to trochu zjednodušily, ale i tak to bylo třeba trochu hlídat. ARM má jediný 32-bitový adresní prostor a ten je (zjednodušeně řečeno) dostatečně velký, aby se tam všechno vešlo. I zde musíme ale systému říct, co má kam zapsat.
    • Tedy že kód má být ve flash, konstanty také, ale data v RAM. To zařídí tzv. Linker skript. To je proti 8051 něco nového a je dobré se naučit, jak do toho vstoupit. Kdo se setkal s AVR a gcc možná něco podobného viděl, jenže tato platforma má už připravené skripty pro jednotlivé procesory, takže se o to celkem není třeba starat. Tady to moc nejde, protože typů procesorů je opravdu moc. I když jak plyne z komentářů zase tak moc se to neliší - nejvíce v definici paměťových regionů, případně názvu startovací procedury ENTRY. Zde uvedený skript je velmi zjednodušen, u větších projektů by bylo nutné dodefinovat i různé symboly pro startovací kód, který mimo jiné musí zajistit vynulování sekce bss a naplnění sekce data z flash. To je ale už nad rámec tohoto jednoduchého příkladu.
    • Registry periferií. Sice by to šlo také zařídit pomocí tohoto skriptu, ale to by bylo poměrně těžkopádné, takže se používají (v C, příp. C++) ukazatele na pevné místo v paměti. Obvykle je to vylepšeno tím, že je určitá sada registrů se společnou funkcí deklarována jako struktura viz např. GPIO_Type. Ani to není dramatický rozdíl, jde o zvyk.
    • Zmíníme ještě přístup k jednotlivým bitům. 8051 na to měla specifickou oblast RAM (ale i SFR), kde šlo zapisovat a číst bity atomicky. U Cortex-M3,4 je něco podobného zachováno, je však nutné podotknout, že u některých typů i když je tato funkce v jádře zachována, chybí potřebný kus RAM, takže to nefunguje. Zatím jsem ale nenarazil na příklad, kde by se to dalo smysluplně využít.
  3. Složitější periferie. To je nejčastější námitka. Když se do toho ale obujeme pořádně, zjistíme, že základní funkce periferií je možné aktivovat zapsáním několika málo bitů do registrů. Nad složitějším chováním periferie je nutné se poněkud zamyslet a nastudovat referenční manuál. Výrobce sice většinou poskytuje periferní knihovny, ale pokud tyto mají postihnout všechny složitosti, jsou pak rovněž komplikované a požadované jednoduché chování se v tom úplně ztratí. Nehledě na to, že z dokumentace k periferním knihovnám stejně většinou nezjistíme, jak přesně zajistit požadovanou funkci. Je to dáno především tím, že knihovny výrobce tvoří jakoby "odspodu", tedy z hlediska hardware, nikoli podle potřeb toho, kdo to pak bude používat. Druhý extrém jak vyřešit problém je styl "Arduino". Zde zase máme sadu funkcí, které lze jednoduše používat, ale pokud chceme trochu odlišné chování, je nutné zasáhnout přímo do knihovny. Což by ani nebylo na závadu, ale tento styl je bohužel doveden ad absurdum (např. předdefinováním statických tříd pro periferie, ale je tam spousta dalších podivností), takže i tento systém má řadu odpůrců. Čili dokonalý systém nechme budhistům a jiným hledačům absolutní pravdy.
    V našem příkladě je jediné co je nutno udělat navíc - pustit přes RCC_Type do periferie hodinové pulsy, aby vůbec chodila. To platí pro všechny periferie, při jejich velkém množství je skutečně lépe je "odstavit", pokud nejsou používány. Na druhou stranu bývají nové procesory vybaveny periferiemi, které ve starších řadách nebyly a bylo nutné připojit externí řadič. S výhodou se dá použít např. USB nebo Ethernet. Knihovny pro obsluhu těchto složitých periferií bývají k dispozici na stránkách výrobce, případně zašity přímo do ROM procesoru.
  4. Přerušovací systém, který zde téměř nepoužíváme (jen start programu, reset) je u Cortex-M vlastně jednodušší. Vektory, tedy vlastně adresy obslužných rutin, jsou (zjednodušeně) také na pevných adresách (opět Linker skript.), obslužné rutiny mohou však být jednoduché C funkce typu void Handler (void), hardware je tomuto přizpůsobeno. Procesory Cortex-M byly navrženy tak, aby bylo možné napsat celý kód v jazyce C a překladač nemusel používat různé obezličky pro uschování registrů, když program vstoupí do přerušení. Sice to poněkud zdržuje, ale funguje to. Detaily zde nebudeme probírat, nejsou pro nás příliš důležité.
  5. Instrukční sada je samosebou jiná. Zajímavé je, že u 32.bitového procesoru může být použita 16.bitová instrukční sada. Šířka slova instrukcí tedy se šířkou slova, s níž pracuje aritmeticko-logická jednotka (ALU) nesouvisí. Označení o kolika bitový procesor se jedná se vztahuje právě na ALU, tedy na výpočty. Zase tak moc divné to není, třeba AVR, označované jako 8.bitové mají instrukce také 16.bitové. U Cortex-M je pouze 16.bitová thumb, resp.thumb2 sada instrukcí, pravý 32.bit ARM to neumí. Šetří se tak místo ve flash. Ale protože je to RISC, moc se toho neušetří, takže výsledný kód je delší. Obvykle mají tyto procesory zase větší flash. V neposlední řadě je třeba uvažovat 32.bitovou aritmetiku - ta samosebou značně zkrátí a urychlí výpočty s větší šířkou slova než 8 bitů. A těch je většina.
  6. A nakonec jak dostat program do cílového procesoru. Jak postupně nahradily pevné paměti (E)EPROM paměti typu FLASH, i 8-bitové procesory začaly používat sériové rozhraní pro programování. U ARM (a jiných) je tato myšlenka dovedena poněkud dále. Standardem se stalo rozhraní JTAG (4 dráty a zem), přičemž vnitřní struktura umožňuje různé ladící funkce - od otestování vodivého spojení pinů (boundary scan) po úplné ovládání interního programu včetně breakpointů a watchpointů. Kromě toho pro zavedení programu do paměti bývá v čipu i kousek pevné paměti ROM, obsahující zavaděč využívající různé dostupné periferie (přes USART po USB) podobně jako to uměly už ty 8.bity. A aby to nebylo zase tak jednoduché, začalo se šetřit vývody a místo JTAG se u Cortex-M používá SWD - 2 dráty a zem (minimálně, může obsahovat i reset, není však nutný). JTAG bývá osazen také, sdílí piny se SWD, ale ne vždy a ne vždy plnohodnotný. U některých typů bývá JTAG použit jen pro boundary scan. Pro SWD už nelze použít nějaký "hrdzavý klinec", je potřeba dost sofistikovaný hardware i software. Vývojové kity takový hardware obsahují - např. zde použitý Discovery kit má tzv. ST-Link. Co se týče software je i zde několik možností. Z vyzkoušených uvedeme:
    • OpenOCD Podporuje širokou paletu cílových procesorů, tak JTAG/SWD hardware jako je ST-Link. Software funguje jako server na který se lze připojit telnetem nebo pomocí gdb, což je debuger, ovládaný z příkazové řádky. Umožňuje to poměrně komfortní ladění, což bylo třeba na 8051 možné jen pomocí dost drahého emulátoru. Gdb bývá součástí toolchainu pro ARM, v balíku z launchpad.net je obsaženo.
    • Black Magic probe Tohle je už pro zkušenější uživatele. Je v tom finta - gdb server běží přímo v procesoru sondy. Lze to flashnout i do toho ST-Linku, ale operace je nevratná. Znamená to o vrstvu software méně, ale není podporováno tolik typů procesorů. Z funkčního hlediska je to možná o něco lepší než OpenOCD.
    • pro úplnost ještě můžeme zmínit, že některé procesory např. NXP mají vcelku šikovný bootloader, který po připojení jako USB device v počítači vytvoří virtuální disk, na který binární obraz systému prostě nakopírujeme. Nepotřebujeme tedy vůbec žádný hardware. Na tomto systému je založen klon blackmagic, který lze stáhnout
      svn checkout svn://svn.code.sf.net/p/kizarmprobe/code/ kizarmprobe
      Nebo lze použít odkaz na této stránce, kde můžete najít i stručný popis. Gdb server pak může běžet v levném čipu LPC11U24 (34) a tímto systémem můžeme v jiném čipu (nemusí být nutně NXP) ladit programy. Je to tedy jakýsi podomácku vyrobitelný bootstrap pro začátek práce s Cortex-Mx.

Programů pro debug a upload firmware do cílového procesoru je samosebou více, na webu lze najít mnoho možností. JTAG, příp. SWD znamená použít dost složitý řetězec hardware a software, což je náročné na nastavení. Na druhou stranu získáme dobrou kontrolu nad cílovým procesorem pomocí několika málo pinů. Zde je dobré ještě podotknout, že pokud chceme např. SWD používat, nelze pak už použité piny využít v aplikaci k ničemu jinému. Možnost ladění přímo v cílovém procesoru pomocí relativně levného hardware je snad největší přínos těchto nových typů procesorů a to nejen ARM. To je proti 8051 neskutečný komfort, i když standardní techniky ladění za běhu pomocí výpisů na sériový (ale i jiný) port nebo nastavováním IO pinu se samosebou používají i zde. Jak je zmíněno dále v textu, program může běžet i v RAM. Což většinou 8-bitové uP neumožňovaly. Pro ladění je to docela dobrá možnost, výhodou je, že je to rychlejší a "neošoupáte" flashku, nevýhodou je že RAMka je menší, tedy se toho tam moc nevejde a potřebuje to speciální nastavení bootovacího režimu, které se u jednotlivých typů liší, u některých je poměrně obtížné.

Příklad 1 - Naprosto minimální blikání LEDkou na STM32F0 nebo STM32F4 Discovery.

Viz Celkový kód v C nebo C++. Základní hodiny jádra ani FLASH se nenastavují, zůstávají na default hodnotách. Prostě se využije toho, že nějaké hodiny se do jádra zavést musí automaticky aby se to vůbec rozběhlo. Není to ale dobrá praktika, hodiny je lépe přesně specifikovat a správně nastavit FLASH také není od věci. Zde je to jen pro maximální zkrácení kódu vynecháno. C-čkový zdroják je asi zbytečně ukecaný aby bylo vidět, že dobře funguje optimalizace.

Příklad 2 - Blikáme pomocí systémového časovače.

Viz C-čkový kód pro blikání pomocí systémového časovače. Tento příklad umožňuje přesněji nastavit čas blikání. Úplně přesně to není, stále používáme default hodiny a ty jsou HSI, což je interní RC oscilátor procesoru. Nicméně to ve většině případů stačí, nepřesnost by neměla přesáhnout asi jedno procento. HSI má na F0 frekvenci 8MHz, na F4 16HMz, tedy hodně pod mezí jádra a není potřeba externí krystal. Zde už použijeme CMSIS pro nastavení systémového časovače. Kód tím poněkud naroste - musíme použít větší tabulku vektorů (minimálně až po SysTick_Handler), inicializace časovače také něco stojí. CMSIS je knihovna určená pro ovládání jádra (nikoli periferií) Cortex M procesorů. Byla vytvořena přímo ARM Company (resp. Keil), je celkem dobře a úsporně napsána a je k dispozici jako zdrojové texty v jazyce C. Jinak je kód stejně jednoduchý jako v předchozím případě. Protože vlastní bliknutí probíhá v přerušení, lze procesor v hlavní smyčce uspat, čímž se sníží spotřeba. Pro ARM je celkem běžná praktika, že celá činnost zařízení (pokud je to možné) probíhá v přerušení a hlavní smyčka bývá prázdná. Něco málo k tomuto viz Softwarové přerušení

Pozn.
Program může běžet ve FLASH i RAM, pro běh v RAM nutno nastavit BOOT1 (v option bytes, což není tak jednoduché pro F0, propojkou na F4) a BOOT0 pin na VCC a upravit script.ld (sekce .fixed >RAM ).

Příklad 3 - Blikáme pomocí ESL.

ESL je takový hybrid slepený z C, Pascalu a asi i něčeho jiného. Původně to však mělo být určeno především pro vestavné systémy. Pracuje to nad LLVM, takže kdo používá clang má základ už nainstalovaný. Nainstalovat nad tím ESL by neměl být problém, ale překladač je napsán celý v tomto jazyce, takže je nutné mít nějakou prvotní verzi tohoto překladače (problém vejce a slepice), což nefunguje ve Windows. Snad jedinou výhodou tohoto jazyka je lepší přístup k periferiím, umožňuje např. indexovat bitová pole, což v C neuděláte. Pak je program lépe strukturován a tím i čitelnější. Dále je možné definovat volatilitu pro čtení i zápis, a zřejmě to má i některé další užitečné atributy, které jsem neobjevil. Nevýhody převažují

Nicméně chvíli jsem si s tím hrál, celkem to funguje, takže pro ostatní průkopníky slepých uliček jsem to zařadil bez bližších komentářů sem. Pro ukázku stejné blikátko

/**
* Tohle je velmi zjednodušené blikátko,
* fungující na STM32F0 a STM32F4 Discovery (v RAM, ale po úpravě script.ld patrně i ve FLASH).
* Systém se nijak nenastavuje, hodiny jsou default HSI. V definicích použitých periferií je
* většina registrů a jejich obsahu ignorována, bylo by to moc psaní.
*/
// Připoj package sys
import sys;
// Čekaní odečítáním, není moc efektivní
proc Delay (time: _uint) {
var count: _uint: in, out; // volatile
count = time;
while count > 0 do {count = count - 1;};
}
// Hlavni procedura se musí nějak navázat na Reset_Handler.
// Lze ji tak přímo pojmenovat a je hotovo. Ty vektory by asi
// nějak šly udělat i v tomto jazyce, ale nechme to být.
// V původním pramenu to dělají v assembleru, C-čko je přehlednější.
proc main(): : weak("Reset_Handler") {
sys.Init (); // jen zapne hodiny a nastaví LED jako výstup
while true do {
sys.toggle (); // přepni LED
Delay (sys.times); // počkej
};
}

a jednoduchá obsluha periferií

package sys {
// definice registru
type Mode: ( // enumerace modu zabere pakovana 2 bity
INPUT, OUTPUT, ALTERNATE, ANALOG
);
type GPIORegs: {
// indexovane pole dvoubitovych elementu v C nejde udelat, zde ano
moder: [16]Mode: packed, lsb, out;
otyper: _uint32: out;
ospeed: _uint32: out;
pupdr: _uint32: out;
idr: _uint32: in;
odr: _uint32: in, out;
// odr:[32]boolean: packed, lsb, in, out;
bsrr: _uint32: out;
lckr: _uint32: out;
};
var gpioc: GPIORegs: external(0x4800_0800);
type RCCRegs: {
_ : [5] _uint32; // ignorujeme
// nic jineho neni potreba, napiseme to jako bitove pole
ahbenr: {
_ : [17] boolean; // ignorujeme
gpioaen: boolean;
gpioben: boolean;
gpiocen: boolean;
gpioden: boolean;
// atd., zbytek neni take potreba
_ : [11] boolean; // ignorujeme
}: packed, lsb, out;
apb2enr: _uint32: out;
apb1enr: _uint32: out;
// atd. nebo jako boolean pole
};
var rcc: RCCRegs: external(0x4002_1000);
// procedury
const led = 8; // modra LED na portu C
const times = 100000; // zpozdeni
proc Init () {
rcc.ahbenr.gpiocen = true;
gpioc.moder[led] = OUTPUT; // tohle v C-cku nejde
};
proc toggle () {
gpioc.odr = gpioc.odr ^ (0x1 << led );
// Tenhle zapis funguje take, pokud odr definujeme jako pole boolean, ale neni tak efektivni
// gpioc.odr[led] = ~gpioc.odr[led];
};
}

Příklad 4 - Blikáme pomocí jazyka D.

Jazyk D vznikl jako logické pokračování C++. Podle autorů specifikace by měl odstranit některé chyby v návrhu jazyka C++ ale je těžké posoudit jaké. Jazyk D podporuje nízkoúrovňové programování, má garbage collector podobně jako třeba java, C#, ale překladač produkuje nativní kód a garbage collector se dá vypnout. Průkopnická práce pro STM32 je zde. Je vidět, že po světě jsou různí podivíni. Věnoval jsem tomu trochu času, blikátko máme - pár postřehů:

Zatím to vypadá jako další slepá ulička nebo šílenost pro nadšence, ale rozvíjí se to. Rozhodně je to pro malé kontroléry vhodnější než java. I když ta je tak rozšířená, že nakonec prorazí spíš a to i navzdory té nevhodnosti. Už teď existují jednoduché JVM pro tyto účely.

Příklad 5 - Blikáme pomocí jazyka rust.

Vycházím z tohoto projektu, dodělal jsem do toho minimální blikání na STM32F4 Discovery ve stylu předchozích příkladů. Vyzkoušeno to bylo na Ubuntu 16.04, rust je nainstalován pomocí skriptu příkazem

curl https://sh.rustup.rs -sSf | sh

Dál se jen řídit pokyny README v rozbaleném archivu. Rust stejně jako většina novějších jazyků používá vlastní build systém (zde cargo), který je poměrně složitý (a tedy nepochopitelný), ale není nutné se o to nějak starat, v projektu je to už zahrnuto.

Nechme to uzrát, zkoušel jsem jazyk D (rust mi ale přijde o něco lepší i když se od skupiny jazyků C dost liší, v něčem mi připomíná spíš python), taky to fungovalo a skoro nikdo to nepoužívá, většina lidí je konzervativní a zůstává u čistého C. Tyhle nové jazyky jsou možná dobré pro normální programovaní na PC, na malých procesorech bývají problémy, které musí řešit někdo, kdo ovládá jak ten jazyk (včetně struktury překladače), tak všechna úskalí vestavných systémů. Ten průnik bývá malý. Je zřejmé, že Mozilla do toho investovala dost úsilí, pokud to bude opravdu tak stabilní a efektivní jak slibují, tak se to ujme, pro automotive a vše, kde se klade důraz na bezpečnost by to mohla být dobrá volba. Struktura jazyka hodně brání nebezpečným konstrukcím, a pokud je programátor použije, snadno se v kódu dají dohledat. Skoro by se dalo říct, že ty MISRA pravidla jsou už uvnitř, hlídá je překladač. Ale MISRA nebo podobné restrikce je možné zabudovat i do překladačů C a C++, jsou na to nástroje např. v clang.

Závěr

Ten jednodušší program s optimalizací -Os (C i C++, binárky jsou stejné - alespoň v mé verzi GCC) má jen 56 bytů. V assembleru (ablink.s) 48 bytů. Pro srovnání a zaryté assembleristy viz Celkový kód v Assembleru. Celkem na tom není co ušetřit. Přece jen je to RISC, takže ldr, str se pořád opakuje. Srovnejte si to s překladem z C nebo C++ a disassemblovaným výstupem cblinking.lst (bblinking.lst). Není to tak velký rozdíl - optimalizovaný překlad z C nebo C++ je velmi efektivní. Složitější příklad, pracující se systémovým časovačem je zhruba 3x větší (záleží na procesoru - pro CM4 je o něco menší).

Je zřejmé, že to není až taková věda a kódu také nemusí být plná FLASH. A protože cena Cortex-M0+ začíná být příznivá, nic nebrání tomu přejít na tuto platformu i pro jednoduchá udělátka, kde by se mohlo zdát, že je to dělo na komára. Sice opravdu je, ale je to jen příklad, této funkce by se dalo dosáhnout se dvěma tranzistory a několika málo pasivními prvky.

Samosebou vývoj takhle minimalistické aplikace je spíš jen pro ukázku, že to jde. Normální lidi používají standardní periferní knihovny výrobce procesoru. Je to celkem pohodlné, zkoušel jsem si v Cube naklikat jen to nezbytně nutné pro blikání LEDkou, je fakt, že to trvá pár minut, funguje to a vygenerovanému kódu pak stačí přidat např. takovouto triviální funkci :

#include "stm32f0xx_hal.h"
void HAL_SYSTICK_Callback(void) {
static uint32_t count;
if (++count >= 500) {
count = 0;
HAL_GPIO_TogglePin (GPIOC, 1<<8);
}
}

a je hotovo. Problém je, že délka programu naroste zhruha na 3 KiB i při zapnuté optimalizaci -Os. Možná to někomu nevadí, ale mně to připadá už dost přehnané. Přece jen se nastavuje jen pár bitů v registrech periferií. Sice o něco víc než v předchozích případech, ale chybu bych v tom pak hledat nechtěl. Najít co vlastně máme napsat jako uživatelský kód je sice méně práce než nastudovat referenční manuál, v případě problému ale stejně nic jiného nezbyde a chyba v cizím kódu se hůř hledá. Nehledě na to, že pokud chceme naklikat ty periferie správně, pak stejně musíme mít nějaké základní povědomí z referenčního manuálu jak.

Sílu 32. bitového procesoru oceníme spíš při výpočtech. Tedy digitální filtry, regulátory a jiné typické aplikace. Pak je možné nasadit jednočip i tam, kde se o tom v časech 8.bitů ani nesnilo. Velkou výhodou v dnešní době je i to, že se není třeba tolik trápit s optimalizací kódu. Byly aplikace, kde se počítalo s každou jednotlivou instrukcí a funkce přímo závisela na přesném časování. Zde tento přístup nelze dost dobře uplatnit. Časování jednotlivých instrukcí není přísně vzato deterministické - vstupuje zde do hry např. pipeline, délka operace násobení může záviset na operandech atd. Čili změna přístupu ke kódování je nutná, ale jak jsme se snažili ukázat, není to nic dramatického.

V neposlední řadě jsem se snažil ukázat na velmi jednoduchém příkladě, jak používat open-source nástroje pro řízení projektu s vestavnými systémy. To, co je zde popsáno, principiálně postačuje pro řízení libovolně složitého projektu. To, že přibudou nějaké startup rutiny, pár řádek do linker skriptu a maličko se změní Makefile vlastně není podstatné. Je to ostatně vidět - i zde přechod z F0 na F4 požaduje změnu jen pár řádek v definici periferií. Podstatné je, že takto v principu funguje překlad a sestavení programu pomocí gcc toolchainu de facto pro libovolnou architekturu, pro kterou je určen. Tedy nejen ARM, ale i AVR, MIPS, MSP430 a další. Pokud pochopíme tento princip, není třeba trápit se s nastavováním různých více či méně povedených IDE. Dokumentace použitých nástrojů není sice dokonalá, ale dá se s trochou praxe naučit i dost pokročilá nastavení, na která IDE nemusí vždy pamatovat. Škoda, že se touto zásadou neřídí tvůrci knihoven s otevřeným zdrojovým kódem. Když tak prohlížím příklady z Internetu, najdu projekty pro různorodá IDE, ale aby k tomu autor přibalil obyčejný makefile, to ho obvykle ani nenapadne. A když už se vyskytne čestná výjimka, jsou tam zase patrně pro zmatení nepřítele různě rozesety absolutní cesty k souborům, což sice dobře funguje autorovi, ale už nikomu jinému. Z dlouholeté zkušenosti bych si dovolil tvrdit, že IDE se mění, ale dobře navržený ručně napsaný makefile zůstává. A není pak takový problém přenést projekt z jednoho prostředí do druhého, většina IDE si s tím poradí. Pro malé úpravy pak není ani to IDE potřeba, stačí libovolný textový editor.

Nakonec přibalíme i zdrojové kódy.