Język programowania Arduino – jedyny słuszny wybór
Decyzja dotycząca wyboru języka programowania w świecie Arduino z pewnością nie była łatwa. Od wielu już lat jedynym słusznym wyborem dla systemów mikroprocesorowych jest język C, który dzięki wysokiej efektywności i elastyczności wyparł asembler z ogromnej większości projektów. Tylko, że język C ma opinię trudnego do nauczenia (bzdura!), a przecież z założeń Arduino wynika, że praca ma być łatwa i przyjemna. Jednak również w tym przypadku autorzy Arduino stanęli na wysokości zadania. Otóż w świecie Arduino programy piszemy w zasadzie w języku C. To „w zasadzie” wynika z tego, że twórcy Arduino wzięli z najpopularniejszego języka programowania to co uznali za potrzebne i jednocześnie postarali się (niestety z różnym skutkiem), żeby język ten był stosunkowo łatwy do opanowania przez amatorów.
GPIO7->DR[0x3fc]=(1<<3);
Oczywiście można tak robić i z pewnością ta instrukcja była czytelna i zrozumiała dla programisty wyspecjalizowanego w pisaniu aplikacji dla konkretnego mikrokontrolera. Warto jednak zadać sobie pytanie czy za kilka lat, gdy konieczne będzie zmodyfikowanie programu, analiza jego działania nie pochłonie zbyt dużo coraz cenniejszego czasu. A przecież można ten sam efekt (dla tego samego mikrokontrolera) uzyskać wywołaniem funkcji:
GPIO_WriteBit(GPIO_LED, LED3, Bit_SET);
Prawda, że jest to czytelniejsze? Z pewnością nawet za kilka lat taki kod będzie zrozumiały nie tylko dla autora programu.
To nie język C powoduje, że niektóre kody źródłowe programów w nim napisanych mogłyby startować w konkursach kryptograficznych! To powodują niedouczeni programiści, a fakt, że w wielu miejscach można łatwo znaleźć przykłady kalamburów programistycznych napisanych właśnie w języku C wynika tylko i wyłącznie z tego, że jest to najpopularniejszy język programowania, a nie z powodu stopnia skomplikowania tego języka. |
Tak więc z możliwości zagmatwania kodu programu (co przecież jest łatwe w każdym języku programowania, a nie tylko w C) nie wynika konieczność tego czynienia! Z popularnej wśród programistów anegdoty mówiącej, że program trudno było napisać, więc również trudno powinno się go czytać, można się pośmiać, ale w praktyce warto zadbać o czytelność i przejrzystość pisanego programu.
Fot. 1. Dołączenie diody LED do zestawu Arduino Uno
Wiemy już, że język, którym będziemy posługiwać się pisząc programy w środowisku Arduino wywodzi się z języka C. Porównajmy więc dwa elementarne programy powodujące miganie diodą LED dołączoną do linii 13 zestawu (czyli linii PB5 mikrokontrolera) napisane w tych językach. Na fotografii 1 pokazano sposób dołączenia diody do zestawu Arduino Uno. Na listingu 1 znajduje się program napisany w języku C. Do jego skompilowania został wykorzystany kompilator AVR GCC z biblioteką avr-libc. Na listingu 2 znajduje się analogiczny program przygotowany w środowisku Arduino. Podobieństwa w formalnym zapisie używanym w obu przypadkach są widoczne na pierwszy rzut oka – tak samo używa się nawiasów zwykłych u klamrowych, podobnie wyglądają definicje i wywołania funkcji itd. Natomiast budowa programów jest już różna. Program w języku C rozpoczyna się od deklaracji używanych bibliotek, ewentualnie zmiennych, stałych itp. a działanie programu rozpoczyna się od funkcji int main(), która jak widać zwraca wartość całkowitą do programu wywołującego. Oczywiście w świecie małych mikrokontrolerów taka deklaracja funkcji main() nie ma sensu, gdyż w olbrzymiej większości przypadków tego typu programy nie są wywoływane z nadrzędnego systemu, pracują natomiast samodzielnie i ich zakończenie nigdy nie powinno nastąpić. Ciągłość pracy przygotowywanego programu zapewnia nieskończona pętla while(1), więc wykonanie instrukcji return 1 nigdy nie nastąpi, ale tego typu zapis jest konieczny ze względu na wymogi kompilatora języka C.
Natomiast program dla Arduino jest zbudowany z dwóch bloków, czyli funkcji setup(), wywoływanej po uruchomieniu/wyzerowaniu mikrokontrolera, oraz funkcji loop(), do której jest przekazywane sterowanie po wykonaniu funkcji setup(). Nazwy tych funkcji w zasadzie same się komentują. W ciele funkcji setup() należy zawrzeć wszelkie instrukcje konfigurujące nasz system do pracy, natomiast funkcja loop() to wspomniana wcześniej pętla nieskończona. Przed wspomnianymi funkcjami oraz w ich „wnętrzu” można deklarować zmienne i stałe o zasięgu zgodnym z zasadami obowiązującymi w języku C – przykład takich deklaracji można zobaczyć w listingu 2.
Listing 1. Program w języku C powodujący miganie diodą LED
#define F_CPU 16000000UL // częstotliwość rezonatora kwarcowego #define LED 0x20 // linia, do której dołączono diodę LED // = linia 13 w Arduino Uno // = linia 5 portu mikrokontrolera #include #include int main(void) { DDRB = LED; // ustawienie linii PB5 jako wyjściowej while (1) // pętla nieskończona { PORTB = LED; // ustawienie bitu 5 w porcie PB _delay_ms(1000); // opóźnienie 1s (1000 ms) PORTB = 0x00; // wyzerowanie bitów w porcie PB _delay_ms(1000); // opóźnienie 1s (1000 ms) } return 1; // sztuczne zakończenie programu, gdyż // ze względu na prototyp funkcji main // musi ona zwracać wartość } // koniec funkcji main
Listing 2. Program migania diodą LED w wersji Arduino
const int LED = 13; // dioda LED jest dołączona do linii 13 zestawu void setup() // funkcja konfigurująca { pinMode(LED, OUTPUT); // ustawienie linii LED jako wyjściowej } void loop() // pętla nieskończona { digitalWrite(LED, HIGH); // ustawienie bitu delay(1000); // opóźnienie 1 s (1000 ms) digitalWrite(LED, LOW); // wyzerowanie bitu delay(1000); // opóźnienie 1 s (1000 ms) }
Zależność pomiędzy programem Arduino i programem w języku C (pamiętajmy, że „finalnym” kompilatorem Arduino jest AVR GCC, czyli kompilator języka C) pokazano na listingu 3.
List. 3. Zależność pomiędzy programem Arduino i programem w języku C
int main(void) { init(); // inicjalizacja zestawu narzucona przez // środowisko Arduino (np. typ zestawu, // częstotliwość pracy mikrokontrolera) setup(); // inicjalizacja zestawu przez użytkownika // (np. incjalizacja sposobu pracy interfejsu // szeregowego, konfiguracja linii we/wy) while (1) // pętla nieskończona { loop(); // właściwy program } return 0; }
Nawet w przypadku tak prostych programów, jak pokazane na listingach 1 i 2, widoczna jest różnica w ich czytelności, chociaż tak naprawdę oba programy są napisane w języku C. Co zatem powoduje, że program przygotowany w Arduino jest bardziej przyjazny? Otóż jest on napisany z wykorzystaniem jednej z wielu bibliotek standardowo zawartych w Arduino. I tak naprawdę to obecność bibliotek do obsługi najpopularniejszych układów peryferyjnych jest jednym z mocniejszych argumentów przemawiających za tym środowiskiem. Co więcej, standardowe biblioteki są przygotowane dla różnych platform sprzętowych, więc zamiast pokazanego na fotografii 2 zestawu Arduino Uno z mikrokontrolerem ATmega328 (rodzina AVR firmy Atmel), można użyć pokazanego na fotografii 3 zestawu ChipKit Uno32 z mikrokontrolerem PIC32MX320F128 (rodzina PIC32 firmy Microchip) czy też zestawu Itead Maple (fotografia 4) z mikrokontrolerem STM32F103RBT6 (rodzina STM32 firmy STMicroelectronics). Daje to projektantowi możliwość dobrania sprzętu odpowiedniego do danego zastosowania.
Fot. 3. Zestaw ChipKit Uno32 z mikrokontrolerem z rodziny PIC32
Fot. 4. Zestaw Itead Maple z mikrokontrolerem z rodziny STM32
Warto zwrócić jeszcze uwagę na kilka zalet wynikających ze stosowania dobrze przygotowanych bibliotek. W programie z listingu 1 instrukcja:
PORTB = LED;
powoduje zmianę stanu wszystkich linii portu B mikrokontrolera, a nie tylko na linii, do której jest dołączona dioda LED. Wynika to z braku instrukcji modyfikujących stan pojedynczej linii. Oczywiście bez problemu można znaleźć przygotowane przez kogoś biblioteki z odpowiednimi funkcjami, można takie biblioteki również przygotować samemu lub uzyskać oczekiwany efekt za pomocą operacji logicznych, np. ustawienie linii 5 portu B można osiągnąć przez instrukcję sumy logicznej:
PORTB |= 0x20;
Jednak czy zapis:
digitalWrite(LED, HIGH);
nie jest czytelniejszy?
Autoryzowanym dystrybutorem zestawów Arduino w Polsce jest KAMAMI.pl. |