Arduino UNO
prototypovacia doska Arduino UNO, cyklus
Sense-Think-Act, mapa pamäte mikrokontroléra
ATmega328P, fragmentácia pamäte, skrytá funkcia
main()
Záznam z prednášky
Introduction
(slide) Pred pár rokmi som si kúpil túto knižku. Je to taká veselá knižka pre najmenších (vekovú kategóriu som splnil už dávno) o robotoch, pomocou ktorej si môžete postaviť vlastných robotov (rozlične inteligentných a z rozličných komponentov). Kúpil som si ju z viacerých dôvodov, ale dva z nich boli: terminológia a ilustrácie, resp. príklady. A v pár veciach mi bola užitočnou aj pri príprave tejto prednášky.
(slide) Tá knižka je vyznavačom tzv. DIY princípu, čo je skratka od Do It Yourself, teda urob si sám. Čiže je pre takých domácich kutilov a hobbystov.
(slide) Táto oblasť sa označuje aj podobným termínom Make a teda každý, kto sa takýmto činnostiam venuje je Maker (makač :-)).
(slide) A makači majú tiež svoje vlastné stretnutia, tzv. Maker Faire-s, Maker Day-s alebo u nás Namakaný deň.
(slide) V tej knižke sa spomína tzv. sense-think-act cycle (robotiMartinus?), na základe ktorého pracuje každý robot. Jednotlivé fázy tohto cyklu sú:
- sense - reprezentuje robotovo vnímanie, kedy robot prijíma informácie o dianí okolo seba,
- think - reprezentuje robotovo myslenie, kedy na základe prijatých informácií z okolia sa robot rozhodne, čo urobí, a
- act - predstavuje robotovu činnosť, ktorou ovplyvní vonkajší svet.
Aby sme boli konkrétni, všetky tieto fázy cyklu je možné reprezentovať konkrétnymi prvkami:
(slide) Fáza sense je reprezentovaná prvkami, ktoré sa nazývajú senzory. Senzor je súčiastka, ktorá zisťuje, čo sa deje okolo a prevádza inú formu energie na elektrickú (napr. fotočleny, mikrofón, tlačidlo, …)
(slide) Fáza think je reprezentovaná počítačom alebo mikrokontrolérom, ktorý je možné programovať. V programe je následne zadefinované správanie robota.
(slide) Fáza act je reprezentovaná tzv. akčným členom (v inej literatúre sa môžete stretnúť s termínmi aktér, efektor, vykonávateľ). Akčný člen je súčiastka, ktorá prevádza elektrickú energiu na inú formu energie (napr. dióda, reproduktor, motor, …)
Airplane Example
(slide) Tento cyklus sa však netýka výlučne len robotov, ale je možné sa s ním stretnúť aj v iných oblastiach. My si princíp fungovania tohto cyklu ukážeme na príklade letiaceho lietadla. Lietadlo síce nevyzerá ako robot, ale je to tiež systém pracujúci v reálnom čase.
Lietadlo obsahuje počítač, ktorý vyhodnocuje údaje z prebiehajúceho letu. Na to ich však potrebuje zbierať. Napr. potrebuje vedieť, ako vysoko letí, ako rýchlo letí, kde sa práve nachádza. Všetky tieto informácie lietadlo potrebuje na to, aby sa vedelo rozhodnúť, čo urobí, resp. kde by sa malo nachádzať v najbližšom momente. Na základe zozbieraných údajov teda upraví výšku pomocou krídiel, spomalí alebo zrýchli pomocou motorov.
Ak by sme teda mali vztiahnuť tento systém reálneho času na náš sense-think-act cyklus, tak:
zbieranie informácií o aktuálnom stave a polohe lietadla, patrí do fázy sense,
vyhodnotenie informácií a naplánovanie ďalšieho kroku lietadla, patrí do fázy think, a
zníženie, resp. zvýšenie výkonu motorov lietadla, upravenie polohy krídiel, patrí do fázy act.
Introduction to Microcontrollers
(slide) Na našich najbližších stretnutiach a rovnako aj vaše posledné zadanie bude venované programovaniu mikrokontrolérov, ktoré sú hlavou sense-think-act cyklu. Mikrokontroléry sú totiž jednoduché počítače v jednom púzdre (v jednom čipe) a je možné ich programovať.
A microcontroller is a special purpose computer on a single chip.
(slide) Ak by sme mali mikrokontrolér s niečim porovnať, tak s mikroprocesorom. Najmä kôli prevedeniu, nakoľko mikroprocesor a mikrokontrolér vyzerajú veľmi podobne - sú v jednom púzdre. Dokonca aj jeden typ mikrokontroléra môžete kúpiť v rozličných púzdrach (viď obrázok).
Tie najdôležitejšie rozdiely medzi mikrokontrolérom a mikroprocesorom zhrňuje nasledujúca tabuľka:
mikroprocesor mikrokontrolér je zariadenie na všeobecné použitie špecializované zariadenie označuje sa ako jednočipový počítač neobsahuje I/O porty, pamäť, časovače a pod. obsahuje RAM, ROM, sériové a pralelné rozhranie, časovače, … všetko na jednom čipe používajú sa ako CPU v mikropočítačoch používajú sa v jednoduchých/jednoúčelových zariadeniach jeho dizajn/návrh je komplexný jeho dizajn/návrh je jednoduchý je drahý (desiatky/stovky Eur) je lacný (jednotky Eur) energeticky náročný (desiatky watov) low power zariadenia (jednotky Watov) majú Von Neumannovskú architektúru majú Harvardskú architektúru vysoká rýchlosť (GHz) malá rýchlosť (MHz) Raspberry Pi Arduino, ESP32, micro:bit, Rasbperry Pi Pico
Arduino UNO
(slide) My sa budeme učiť programovať mikrokontrolér Arduino UNO. Tento mikrokontrolér bol navrhnutý a vyvinutý pre potreby vzdelávania, takže je ideálny kandidát na úvod do tejto problematiky.
Poznámka
Ak by sme sa chytali za slovíčka, tak Arduino UNO nie je mikrokontrolér, ale prototypovacia doska. Mikrokontrolér je na tejto doske osadený a je jej srdcom. V prípade Arduino UNO je to mikrokontrolér ATmega328P.
Termín Arduino však zľudovel natoľko, že sa ním zvyknú označovať ako dosky z rodiny Arduín, tak aj samotný mikrokontrolér ATmega328P.
Pozrime sa bližšie na to, čo všetko sa na doske nachádza:
- napájací konektor - Arduino je možné napájať z externého zdroja v rozsahu 7-12V
- stabilizátory napätia
- USB port - používa sa na napájanie, nahrávanie vášho programu do Arduina (programovanie) a na komunikáciu pomocou sériového portu
- reset tlačidlo - resetuje mikrokontrolér (reštartne program, ktorý je v ňom nahratý)
- TX a RX LED diódy - tieto LED-ky indikujú komunikáciu medzi Arduinom a počítačom, čo je možné vidieť napr. pri nahrávaní programu do Arduina alebo pri komunikovaní po sériovej linke
- mikrokontrolér - samotný mikrokontrolér, ktorý je srdcom Arduina (ATmega)
- LED-ka napájania - indikuje, že Arduino je napájané
- digitálne piny - ku ktorým je možné pripojiť senzory a akčné členy, používa sa 5V logika
- analógové piny - ku ktorým je možné pripojiť senzory a akčné členy
- špeciálne piny - napr. GND a 5V, PWM a pod
- LED-ka pripojená k pin-u č. 13 - programovateľná LED-ka pripojená k pin-u č. 13
- externý oscilátor - 16MHz
Poznámka
S príchodom novej elektroniky sa začína používať 3.3V logika. Ak použijete takýto senzor/zariadenie pre Arduino, je otázne, či toto zariadenie bude tzv. 5V tolerantné (či bude vedieť pracovať s 5V). Ak totiž nebude, tak si viete dané zariadenie odpáliť. Napr. Rapsberry Pi používa 3.3V logiku, ale nie je 5V tolerantné.
Aby všetko fungovalo ako má, potrebujete použiť tzv. prevodníky logických úrovní medzi 3.3V a 5V (z angl. level shifter).
Arduino Programming
(slide) Na písanie programov budeme používať nástroj Arduino IDE, ktoré je možné stiahnuť z domovskej stránky www.arduino.cc.
Poznámka
Pokiaľ používate linuxový OS, môžete Arduino IDE nainštalovať aj priamo z balíčkov distribúcie. Odporúčam vám však nainštalovať si tento nástroj priamo pomocou inštalačiek zo stránky www.arduino.cc, nakoľko verzia dostupná v balíčkoch distribúcie môže dosť stará oproti najnovšej verzii dostupnej zo stránok projektu.
Poznámka
Pomaly je vyvíjané aj nové prostredie Arduino IDE 2.0, ktoré je postavené na prostredí Eclipse. Toto prostredie je však stále vo vývoji a nebudeme ho používať.
Samozrejme existuje mnoho iných editorov a prostredí, ktoré je možné použiť na vývoj programov pre Arduino. Najčastejšie sa jedná o rozšírenia existujúceho editora alebo vývojového prostredia o možnosť vyvíjať programy aj pre Arduino, ako Atom, Eclipse, CLion alebo Visual Studio Code. Tým je možné využiť práve silu existujúcich IDE.
A samozrejme je možné vyvíjať aj z príkazového riadku alebo rozličnými spôsobmi aj pomocou editora Vim.
(slide) Za samostatnú zmienku určite stojí PlatformIO. Obecne sa jedná o rozšírenie pre rozličné editory, ale ponúka výrazne viac možností ako je štandardné Arduino IDE. PlatformIO je ideálny nástroj, ak to myslíte s vývojom pre embedded zariadenia naozaj vážne.
(slide) Programy pre Arduino sa píšu v jazyku C++, ktorý je rozšírením jazyka C. A keďže je Arduino IDE založené na platforme Wiring, programy v ňom písané sa volajú sketch-e.
Setup your IDE
Pozrime sa teda na prostredie, ktoré budeme používať na písanie programov pre Arduino trošku bližšie.
Najpodstatnejšia vec, ktorú musíte po spustení urobiť alebo aspoň skontrolovať je, pre akú dosku, resp. pre aký mikrokontrolér je vaše IDE nastavené a ku akému portu je táto doska (mikrokontrolér) pripojený. To si môžete všimnúť vpravo dole v rozhraní IDE.
Ak však chcete tieto nastavenia zmeniť, tak:
choďte do menu
Tools > Board
, ak chcete zmeniť prednastavenú dosku, alebochoďte do menu
Tools > Port
, ak chcete zmeniť prednastavený port.
Ak by ste totiž mali vybratý nesprávny port, k nahratiu programu na dosku nedôjde, ale proces nahrávania skončí s chybou. Ak by ste však mali nesprávne vybratú dosku, tak síce k nahratiu dôjde, ale program sa nemusí (ale môže) spustiť (samotné dosky/mikrokontroléry sa navzájom líšia).
Blink Example
(slide) Začneme s programom Blink, ktorý je akýmsi Hello world-om v hardvérovom svete.
(slide) Projekt Blink rozbliká LED diódu, ktorá sa nachádza priamo na doske, takže netreba nič zapájať navyše.
Projekt Blink sa nachádza v ukážkových príkladoch priamo v Arduino IDE. Nájdete ho v menu
File > Examples
. Nahráme ho do Arduina. Po nahratí sa program automaticky spustí a LED dióda na doske začne blikať LED dióda.Poznámka
Tí, ktorí s Arduinom už majú skúsenosť, vedia, že táto LED dióda je vyvedená na digitálny pin č. 13. To znamená, že makro
LED_BUILTIN
má pre dosku Arduino UNO v skutočnosti hodnotu13
.Ak stlačíme tlačidlo RESET, program sa spustí znova od začiatku.
Structure of the Sketch
Pozrime sa teraz bližšie na samotný kód:
// the setup function runs once when you press reset // or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. (LED_BUILTIN, OUTPUT); pinMode} // the loop function runs over and over again forever void loop() { // turn the LED on (HIGH is the voltage level) (LED_BUILTIN, HIGH); digitalWrite// wait for a second (1000); delay// turn the LED off by making the voltage LOW (LED_BUILTIN, LOW); digitalWrite// wait for a second (1000); delay}
(slide) Spomínal som, že sketch-e sú vlastne programy napísané v jazyku C++. To, čo však v sketch-i chýba, je hlavná funkcia
main()
. Miesto toho má každý program pre Arduino nasledovnú štruktúru:void setup(){ // initialization function // runs once } void loop(){ // main loop // runs continuously }
kde význam týchto funkcií je nasledovný:
setup()
- funkcia slúži na inicializáciu pinov a spúšťa sa iba raz (na začiatku)loop()
- funkcia predstavuje hlavnú aplikačnú slučku (tzv. superloop) a po inicializácii funkciousetup()
sa táto funkcia spúšťa neustále
(slide) Ak by sme sa teda pokúsili prepísať správanie programu napísaného pre Arduino pomocou uvedených dvoch funkcií priamo v jazyku C, program by mohol vyzerať nasledovne:
int main(){ // runs once (); setup // runs continuosly for(;;){ (); loop} }
Samozrejme sa nami vytvorený sketch len pripojí k výslednému programu, ktorý funkciu
main()
obsahuje. Tá vyzerá v skutočnosti trošku ináč, ale nie veľmi výrazne. Ak si teda necháte vo svojej inštalácii Arduina vyhľadať súbormain.cpp
:$ cd arduino-x.y.z $ find . -name main.cpp ./hardware/arduino/avr/cores/arduino/main.cpp
uvidíte toto:
int main(void) { (); init (); initVariant #if defined(USBCON) .attach(); USBDevice#endif (); setup for (;;) { (); loopif (serialEventRun) serialEventRun(); } return 0; }
Takto organizovaný kód však núti programátora používať globálne premenné, o ktorých sme hovorili, že ich nadmerné používanie vedie k zlým návykom. V prípade mikrokontrolérov je situácia ešte horšia, nakoľko mikrokontroléry sú výrazne obmedzené veľkosťou pamäte. Pozrime sa teda, ako je to s pamäťou v prípade mikrokontroléra ATmega328P.
Arduino and Memory
(slide) Keď sa program nahrá do mikrokontroléra, v spodnej časti IDE sa zobrazia informácie o spotrebe pamäte aktuálnym sketch-om:
Sketch uses 924 bytes (2%) of program storage space. Maximum is 32256 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.
Všimnite si, koľko výsledný kód zaberá - 924B z 32256B. A všimnite si koľko pamäte máme k dispozícii - 9B z 2047B. Prečo sú tam dve maximá? Nemali by sme mať k dispozícii len jednu pamäť pre program a dáta?
Poznámka
Za tento výpis môže nástroj
avr-size
, ktorý je AVR alternatívou nástrojasize
. Nachádza sa v inštalácii Arduina (v priečinkuhardware/tools/avr/bin/
) alebo v linuxových distribúciách v príslušnom balíčku (napr. vo Fedore je to balíkavr-binutils
).Ak chcete nástroj spustiť, musíte vo
Vlastnostiach
zapnúťViac informácií počas prekladu
a vo výpise pri preklade nájsť riadok, ktorý obsahuje cestu vedúcu k.elf
súboru. Ten následne použite ako parameter príkazuavr-size
:$ avr-size Blink.ino.elf text data bss dec hex filename 924 0 9 933 3a5 Blink.ino.elf
(slide) Väčšina moderných mikrokontrolérov je založených na Harvadskej architektúre. To znamená, že časť pre program (označovaná ako Flash) je oddelená od pamäte pre dáta (označovaná ako SRAM). Pamäť mikrokontrolérov je obyčajne malá a ľahko sa môže stať, že aj malý program dokáže naplniť pamäť dát veľmi rýchlo.
(slide) Pozrime sa teda na to, ako je na tom s pamäťou mikrokontrolér ATmega328P. Tento mikrokontrolér má tri typy pamäte:
Flash - Pamäť pre uloženíe preloženého programu. Má veľkosť 32k a používa 16b adresnú zbernicu. Je rozdelená na dve časti:
- Boot Loader Section, ktorá zaberá 512B, a
- Application Program Section, kde sa nachádza samotný program
Adresná zbernica Flash pamäte je iná ako pamäť SRAM (má iný adresný priestor). Jej životnosť predstavuje minimálne 10k cyklov.
SRAM (Static Random Access Memory) - Dočasná pamäť, ktorá bude vymazaná, pri reštartovaní mikrokontroléra. Okrem samotnej pamäte pre bežiaci program o veľkosti 2k obsahuje aj všeobecné a špeciálne registre.
EEPROM - Elektricky prepisovateľná trvalá pamäť, ktorej údaje sú dostupné aj po reštartovaní mikrokontroléra. Môžu byť do nej zapisované len 8b hodnoty. Jej veľkosť je 1k. Jej životnosť je minimálne 100k cyklov.
Používa sa pomocou “štandardnej” knižnice
EEPROM.h
alebo pomocou externej knižniceEEPROMEx
, pomocou ktorej je možné zapisovať aj iné dátové typy ako byty.
(slide) Pre náš bežiaci program máme teda k dispozícii 2k dát v adresnom rozsahu od adresy
0x100
až po adresu0x8ff
. Aby to nebolo všetko, aj táto pamäť má konkrétnu štruktúru:.data - Inicializovaný dátový segment, nazývaný aj jednoducho Dátový segment. Obsahuje inicializované globálne a statické premenné v kóde.
.bss - Neinicializované globálne a statické premenné v programe.
heap - Časť pamäte, kde sa ukladajú dynamicky alokované údaje pomocou funkcií ako
malloc()
alebocalloc()
a je ich možné uvolniť pomocou volania funkciefree()
.stack - Časť pamäte známa ako zásobník. Ukladá sa do nej aktuálny stav registrov pri volaní funkcií, aby procesor vedel, do akého stavu sa vrátiť, keď sa funkcia ukončí. Rovnako sa tu nachádzajú lokálne premenné funkcií.
Obecný problém je, že pamäť heap a stack sa podľa potreby a v rámci veľkosti dynamicky zväčšujú a zmenšujú. To, čo sa však dostane do pamätí .data a .bss, je k dispozícii po celý čas behu programu. Je teda veľmi jednoduché zapratať pamäť napr. dostatočne veľkým poľom prvkov, ktoré bude existovať ako globálna premenná.
Code Example
Použitie zásobníku a hromady je viacmenej jasné. Zaujímavé je ale to, kedy sa využívajú segmenty
.data
a.bss
. Modifikujem teda ukážku Blink, aby sme ho videli:// uninitialized (global = 0) global variable, 2B int global; // initialized global variable, 2B int counter = -1; void setup() { (LED_BUILTIN, OUTPUT); pinMode= 4; counter } void loop() { for(global = 0; global < counter; global++){ (LED_BUILTIN, HIGH); digitalWrite(1000); delay(LED_BUILTIN, LOW); digitalWrite(1000); delay} }
Obsadené miesto v príslušnom segmente je možné vidieť postupne pomocou príkazu
avr-size
:$ avr-size /path/to/Blink.ino.elf text data bss dec hex filename 1002 2 11 1015 3f7 /path/to/Blink.ino.elf
Upozornenie
Pozor pri priebežnom spúšťaní príkazu
avr-size
. Prekladač má totiž zapnuté rozličné optimalizácie a ak len vytvoríte premennú, ktorú nikde nepoužijete, tak ju prekladač ani len nezaradí do výsledného programu.Poznámka
Nástroj PlatformIO má mocný doplnok s názvom Inspect. Ten ponúkne výrazne silnejšiu analýzu vytvoreného kódu, kedy môžete vidieť aj všetky premenné, ktoré sa v danom segmente nachádzajú. Rovnako tak uvidíte aj všetky funkcie, ktoré sa dostatnú do výsledného programu.
Memory Issues
(slide) Aj keď sme sa v rámci predmetu (a hlavne zadaní) dostatočne venovali dynamickej alokácii pamäte, jej použitie v prípade embeded zariadení nemusí byť ideálne. Veľmi ľahko sa totiž môže stať, že dôjde k fragmentácii pamäte.
(slide) Čo je to fragmentácia pamäte? Jednoducho sa dá fragmentácia opísať ako diery v pamäti, ktoré vznikli uvoľnením už nepotrebných blokov, ale ktoré sa už nedajú vďaka svojej veľkosti znova použiť.
Poznámka
Problém fragmentácie sa netýka len pamätí. Viete sa s ním stretnúť na ktoromkoľvek úložnom zariadení, ako sú napr. disky. S operáciou defragmentácia disku v OS Windows na súborovom systéme FAT ste sa už určite stretli.
(slides) Je dobré si uvedomiť, že k dispozícii sú len 2k pamäte, ktorú je možné využiť. Aby sme využili pamäť čo najefektívnejšie a predišli tak problémom s pamäťou, resp. s fragmentáciou pamäte, uvediem niekoľko odporúčaní:
ak je to možné, vždy použite zásobník miesto hromady - pamäť je súvislá, nedochádza k fragmentácii, vždy je pravidelne uvoľňovaná, keď program vyskočí z funkcie, jej správa nás nič nestojí.
vyhnite sa používaniu globálnych a statických premenných - údaje uložené v
.bss
a.data
nie sú nikdy uvoľňované počas životného cyklu programusnažte sa, aby reťazce boli čo najkratšie - každý znak v pamäti zaberá 1B, čo znamená, že dokopy viete napísať reťazec o dĺžke max. 2048 znakov
snažte sa udržiavať minimálnu veľkosť polí - ak neskôr zistíte, že potrebujete zmeniť veľkosť, proste ju upravte a znovu preložte program
používajte vhodné údajové typy - pri programovaní často zvykneme používať priveľké údajové typy pre hodnoty, ktoré potrebujeme uchovávať; použitím menších typov šetríme pamäť
Poznámka
Napríklad aj v horeuvedenej úprave programu Blink by sme vedeli ešte ušetriť
2B
tak, že premennéglobal
acounter
by sme pretypovali na typbyte
.(slide) Štepán Bechynský, jeden z popularizátorov a autorov projektu Arduino 101, na jednom zo svojich workshopov povedal, že “Programovanie pre Arduino môže byť aj celkom dobrým cvičením písania efektívneho kódu pre skúsených programátorov.” A má pravdu ;)
Sketch - The Different Way
Pri programovaní mikrokontrolérov sa sám častokrát stretávam s ospravedlňovaním, že “Tak to robia všetci.”, ktorým ľudia ospravedlňujú porušenie niektorých z vyššie uvedených odporúčaní (najčastejšie to o globálnych premenných). Nedajte sa strhnúť davom a najskôr pouvažujte - dobré princípy vám môžu v budúcnosti zachrániť život ;)
Aby sme sa týmto problémom vyhli v čo najväčšej miere a aspoň z časti sa im vyhli, budeme v ďalších prednáškach písať sketch-e s pomocou hlavnej funkcie
main()
a teda nie len pomocou predpísaných funkciísetup()
aloop()
. Týmto prístupom znížime cenu výsledného programu z pohľadu spotrebovanej pamäte.Do tejto podoby môžeme hneď prepísať aj príklad Blink. Prepíšem teda ten, ktorý zdražel pomocou dvoch globálnych premenných na cenu
13B
:int main(){ // needed from original main() function (); init // initialization, runs once int global; int counter = -1; (LED_BUILTIN, OUTPUT); pinMode= 4; counter // main loop, runs continuously for(;;){ for(global = 0; global < counter; global++){ (LED_BUILTIN, HIGH); digitalWrite(1000); delay(LED_BUILTIN, LOW); digitalWrite(1000); delay} } }
Ak tento sketch preložíme, uvidíme, že cena poklesla z
13B
na pôvodných9B
, čo je cena zrejme Arduino SDK:Sketch uses 920 bytes (2%) of program storage space. Maximum is 32256 bytes. Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.
.kkrieger
(slide) Na záver sa už len pozrieme na to, čo všetko sa dá vopchať do necelých 95kB kódu - pozrieme sa na projekt .kkrieger.
.kkrieger (from Krieger, German for warrior) is a first-person shooter video game created by German demogroup .theprodukkt (a former subdivision of Farbrausch) which won first place in the 96k game competition at Breakpoint in April 2004. The game remains a beta version as of 2012
stiahnutie na stránke archívu webu (direct download)