Po zapoznaniu z podstawowymi elementami zestawów ARDUINO oraz napisaniu programu "Hello World!" pora na zapoznanie z językiem programowania układów. Struktura języka oparta została głównie na C/C++, stąd osoby, które wcześniej programowały w tych językach nie będą miały żadnych problemów z opanowaniem programowania ARDUINO. Pozostali muszą przebrnąć przez podstawowe informacje o instrukcjach sterujących, typach danych, czy funkcjach. Większość zawartych tutaj informacji będzie zgodna z dowolnym kursem C/C++ z uwzględnieniem różnic w typach danych oraz kilku specyficznych instrukcji dotyczących programowania portów we/wy.

UWAGA!: Zaleca się zapoznanie (chociaż pobieżne) z kursem C++ znajdującym się na naszej stronie, lub podstawami systemów liczbowych, algorytmami, zapisem kodu...

Podstawy podstaw..

Kilka spraw formalnych, czyli takich, które wszyscy wiedzą, ale czasem zapominają... W ArduinoIDE tak jak w C/C++ należy pamiętać o wielkości znaków. Słowa kluczowe takie jak if, for, while zawsze piszemy małymi literami. Każdą instrukcję kończymy znakiem ";". Średnik jest informacją dla kompilatora jaki fragment należy zinterpretować jako instrukcję. Klamry { .. } służą do oznaczania bloków programu. Używamy ich do ograniczenia ciała funkcji (patrz poniżej) oraz bloków dotyczących pętli, czy instrukcji warunkowych. Dobrym zwyczajem jest dodawanie komentarzy do treści programu, żeby można było łatwo zrozumieć kod. Komentarze jedno-liniowe rozpoczynamy znakiem // (podwójny slash). Komentarze wieloliniowe rozpoczynamy znakiem /* a kończymy */.

Jeżeli chcemy dołączyć do naszego programu jakąś bibliotekę to korzystamy z polecenia include. Poniżej przykładowe załączenie bibliotek:

#include <SPI.h>                  // biblioteka standardowa
#include "własna_biblioteka.h"    // biblioteka w katalogu projektu

 

Zaczynamy od końca, czyli od funkcji...

Funkcja (podprogram) to wydzielona część programu wykonująca jakieś operacje. Funkcje stosuje się, aby uprościć program główny i zwiększyć czytelność kodu. Warto stosować funkcje, ponieważ bardzo łatwo możemy je wykorzystać w wielu projektach. Typowy kurs programowania zawiera informacje o funkcjach dopiero w dalszej swojej części. W przypadku ARDUINO funkcje zostaną omówione na wstępie, ponieważ każdy nawet najprostszy program musi zawierać dwie funkcje specjalne. Była już o nich mowa w poprzednich częściach kursu, tu jednak usystematyzujemy te informacje.

Schemat deklaracji funkcji jest następujący:

typ nazwa_funkcji( [ lista-parametrów ] )
 {
    // instrukcje do wykonania (ciało funkcji)
    return (/* zwracane-wyrażenie */);
 }

Typ to nazwa dowolnego dostępnego typu danych w danym języku programowania. Lista typów dostępnych podczas programowania ARDUINO znajduje się w odrębnym artykule. Funkcja po wykonaniu zwróci wartość zadeklarowanego typu. W przypadku, gdy funkcja z założenia nie będzie zwracała żadnej wartości wykorzystuje się tzw. typ nieważny "void". Nazwa funkcji umożliwia jej jednoznaczną jej identyfikację. W celu wywołania (uruchomienia) funkcji podajemy jej nazwę. W nawiasach okrągłych podaje się parametry wywołania funkcji. Parametry nie są konieczne, ale często przydatne. W przypadku, gdy piszemy funkcję, która nie ma argumentów pozostawiamy puste nawiasy okrągłe. W nawiasach "{ ...}" znajduje się właściwe ciało funkcji czyli instrukcje, które chcemy wykonać. Opis konkretnych instrukcji pojawi się w dalszej części kursu. Wszystkie funkcje, które mają zwrócić jakąś wartość kończą się instrukcją "return", po której podajemy zwracaną wartość. Tylko funkcje zadeklarowane z typem nieważnym ("void") nie zawierają instrukcji "return". Warto wiedzieć, że instrukcja "return" kończy wykonywanie funkcji niezależnie od umiejscowienia. Poniżej zamieszczono kilka przykładowych deklaracji funkcji.

void f1()
  {
    //ciało funkcji
  }

int minus()
  {
    //ciało funkcji
    return(0);
  }

int dodaj(int a, int b)
  {
   return(a+b);
  }

 Jak widać na przykładach deklaracja funkcji może przybrać różną postać w zależności od potrzeb. Zachęcam do poznania i stosowania funkcji podczas pisania własnych programów. Z czasem każdy programista dorobi się biblioteki własnych funkcji "na każdą okazję", co przyspieszy i ułatwi pisanie nowych aplikacji. Skoro już wiemy jak napisać własną funkcję, to warto wiedzieć jak z niej skorzystać. Wszystkie funkcje piszemy w jednym pliku/programie. Istnieje bardziej eleganckie rozwiązanie, ale nim zajmiemy się w kolejnych częściach kursu. Mając zadeklarowane funkcje możemy z nich skorzystać w innych funkcjach podając odpowiednią nazwę oraz ewentualne wymagane parametry. Poniżej przykłady wywołania funkcji, które przed chwilą napisaliśmy.

f1();
x=minus();
dodaj(2,2);
y=dodaj(1,5);

Jak widać na przykładach wywołanie funkcji następuje poprzez podanie jej nazwy oraz wymaganej liczby parametrów. Istotne jest, żeby wywołanie funkcji było zawsze zgodnie z jej deklaracją. Jeżeli funkcja f1() została zadeklarowana bez parametrów to nie można w jej wywołaniu podać żadnych parametrów. Tak więc wywołanie f1(0); będzie nieprawidłowe. Funkcja dodaj(int a, int b) wymaga podania dokładnie dwóch parametrów, więc wywołanie jej z jednym, lub trzema parametrami jest niemożliwe. Dwa z podanych przykładów obrazują przekazanie przez funkcję wartości do zmiennej. Wywołanie y=dodaj(1,5); spowoduje wykonanie funkcji "dodaj" z parametrami "1" i "5" i zapisanie zwróconej wartości do zmiennej "y".

Dysponując wiedzą na temat deklaracji i wywołania funkcji możemy przejść do funkcji systemowych ARDUINO: setup() i loop(). W ArduinoIDE wymagana jest deklaracja powyższych funkcji. Funkcja setup() to funkcja, która wywoływana jest automatycznie po włączeniu zasilania lub naciśnięciu przycisku RESET. Zgodnie ze swoją nazwą wykorzystywana jest do ustawienia wartości początkowych zmiennych, deklaracji wejść i wyjść układu, czyli ogólnie ustawiane są w niej parametry początkowe budowanego systemu. Ze względu na swoją specyfikę funkcja ta nie zwraca wartości, nie przekazuje się do niej również żadnych parametrów. Prawidłowa deklaracja funkcji setup() została przedstawiona poniżej:

void setup()
   {
    //ciało funkcji - inicjalizacja systemu
   }

Funkcja loop() wywoływana jest w nieskończonej pętli. Ta funkcja również nie zwraca żadnej wartości i nie jest wywoływana z parametrami. Poniżej przedstawiono prawidłową deklarację funkcji loop():

void loop()
   {
    //ciało funkcji - kod programu
   }

Jak widać deklaracja funkcji loop() jest identyczna jak deklaracja funkcji setup(). Różnica polega na wykonaniu powyższych funkcji przez mikrokontroler. Przeanalizujemy teraz poniższy pseudokod (tego kodu w rzeczywistości nie uruchomisz...):

void setup()
   {
     zapal_diodę1();
     zgaś_diodę1();
   }
void loop()
   {
     zapal_diodę2();
     zgaś_diodę2();
   }

W funkcji setup() umieszczono dwie instrukcje: pierwsza z nich zapala diodę1 podłączoną do układu (np. pin13), druga gasi tą diodę. W funkcji loop() umieszczono identyczne instrukcje umożliwiające zapalenie i zgaszenie diody2 podłączonej do układu (np. pin12). W efekcie uruchomienia programu dioda1 zapali się i zgasi raz, natomiast dioda2 będzie zapalać się i gasić tak długo, jak długo będzie podłączone zasilanie do ARDUINO. Naciśnięcie przycisku RESET spowoduje, że raz zapali się dioda1 i ponownie zacznie mrugać dioda2.

Podsumowując:

  • funkcje setup() i loop() to funkcje systemowe, które muszą być zdefiniowane w każdym projekcie. Nawet w sytuacji, gdy w jednej z nich nie umieścimy żadnego kodu muszą być zadeklarowane obie,
  • funkcja setup() wykonywana jest raz, funkcja loop() wykonuje się stale (po zakończeniu funkcji uruchamiana jest od nowa),
  • własne funkcje tworzymy w tym samym pliku; możemy je wywołać zarówno z poziomu funkcji setup(), loop() jak i innej funkcji,
  • własne funkcje mogą być wywoływane z parametrami oraz zwracać wartość,
  • wywołanie funkcji musi być zgodnie z jej deklaracją.