Witam wszystkich bardzo serdecznie
Jako, że jest to mój pierwszy poradnik tego typu, z góry przepraszam za ewentualne nieścisłości lub niedociągnięcia oraz niekonsekwentne używanie form osobowych i nieosobowych - jeszcze się nie wprawiłem w pisaniu takich poradników
.
Wszystkie kwestie formatowane
kursywą są moimi spostrzeżeniami, które można pominąć
.
Postaram się ten post często aktualizować, aby go ukończyć.
Moim celem jest przekonanie forumowiczów i nie tylko, do przerzucenia się na język C++ dla AVR. Przy tym chciałbym zaznaczyć, że nie mówię tutaj absolutnie o wyższości tego języka programowania nad innymi, a jedynie przedstawić nowatorskie rozwiązania znacznie umilające pracę programisty.
Osoby, które nie miały wcześniej styczności z programowaniem, lub nieużywające wcześniej języka C/C++ mogą nie w pełni wszystko zrozumieć, mimo iż będę się starał wykonywać wszystkie działania krok po kroku, dodając za każdym razem komentarze do kodu. Gdy coś będzie nie jasne bardzo proszę pisać najlepiej tutaj na forum, a nie na priv, gdyż ktoś może mieć podobny problem, a razem na pewno uda nam się go rozwiązać
Dodatkowo zachęcam do wcześniejszego zapoznania się z językiem C, najlepiej z książki „Mikrokontrolery AVR Język C - Podstawy programowania”.
Na wstępie przystąpię do omówienia zalet i wad programowania zorientowanego obiektowo (C++ dla AVR):+ hermetyzacja (enkapsulacja) - pełna kontrola nad zakresem widoczności pól (składowych klasy) oraz metod
+ możliwość podziału algorytmów na mniejsze składniki w pełni odzwierciedlające rzeczywistość oraz świat wirtualny (klasy, dziedziczenie)
+ pełne wsparcie dla programistów opierających swoje projekty o biblioteki innych osób (identyczne nazwy zmiennych i funkcji bez użycia mechanizmów zasięgu zmiennych oraz przeciążania funkcji - przestrzenie nazw)
+ nowe typy danych (m.in. bool - wartości logiczne: true, false; _Complex i _Imaginary - liczby zespolone: część rzeczywista i urojona)
+ nowe słowa kluczowe rozszerzające możliwości języka
+ prostsza alokacja pamięci (new, delete)
+ deklaracja zmiennych w dowolnym miejscu programu (nie ma konieczności deklarowania zmiennych na początku instrukcji blokowej)
+ szablony funkcji i klas - możliwe wywołanie funkcji z wartościami różnych typów (bez potrzeby przeciążania funkcji, czy też stosowania typu pustego void*)
+ przeciążanie operatorów - nadawanie nowych znaczeń dla istniejących operatorów
+ referencja, czyli alias dla zmiennej, funkcji…
+ oraz wiele, wiele więcej
- trudniejsza składnia w stosunku do ANSI C
- brak obsługi wyjątków (try, catch, throw)
- nie wszystkie mechanizmy języka ujęte w standardzie działają w przypadku kompilatorów C++ dla AVR
- na chwilę obecną bardziej znaczących uchybień nie wychwyciłem, mam nadzieję, że ta lista wad będzie się z czasem skracać
Więcej szczegółów dotyczących różnic (nie koniecznie działających w kompilatorach C++ dla AVR) znajduje się
tutaj.
Pierwszy projektZe względu na to, iż używam środowiska AtmelStudio, to w nim prezentował będę pierwsze kroki przy tworzeniu nowego projektu w C++.
Nie używałem programu Eclipse, dlatego nie jestem w stanie umieścić zrzutów ekranowych z realizacji tych projektów. Jeśli zajdzie taka potrzeba bardzo proszę osoby bardziej biegłe ode mnie w programie Eclipse, do podzielenia się tą wiedzą.
Z menu „
File->New” wybieramy „
Project…” lub używamy skrótu „Ctrl+Shift+N”.
W tym momencie należy wybrać zakładkę „
Installed Templates” oraz opcję „
C/C++”, póxniej trzeba zaznaczyć pole „
GCC C++ Executable Project”. Następnie
nadajemy nazwę dla nowo tworzonego projektu oraz
nazwę solucji, która domyślnie jest taka sama jak nazwa projektu. Można zrezygnować z tworzenia folderu dla nowej grupy projektów, odznaczając pole „Create directory for solution”. Jeśli do końca nie wiesz, co to oznacza to pozostaw to pole zaznaczone, dzięki czemu będzie możliwość dodania programów dla wielu procesorów w ramach jednego zbiorczego projektu, przy jednoczesnym utrzymaniu porządku w domyślnym folderze „Atmel Studio”.
Po zaakceptowaniu wprowadzonych zmian pojawia się kolejne okno do wyboru mikroprocesora, dla którego będziemy pisać program. Używając listę wyboru „rodziny urządzenia” mamy możliwość wstępnego przefiltrowania wyników tak dużej bazy mikrokontrolerów. Ja jednak użyję bardzo pomocnego pola wyszukiwarki „
Search for device”, dzięki któremu wyszukam przykładowy procesor ATMega1284P, wpisując jedynie fragment nazwy:
Po zaznaczeniu odpowiedniego procesora i kliknięciu „OK” możemy wreszcie przystąpić do pisania kodu. Omówię w nim podstawowe zasady obiektowości oraz praktyczny przykład wykorzystania takiego „potworka”
Pierwszy program w C++Wbrew pozorom nie będzie to słynny „Hello World”, a coś bardziej praktycznego. Oczywiście nie z tego względu, że jest to zbyt błahy algorytm, a wręcz przeciwnie w przypadku mikroprocesorów nie jest tak łatwo, gdyż trzeba zaadaptować jakiś wyświetlacz. W tym celu najlepiej byłoby na początek użyć sposobu opisanego przez
Mirka, lecz nawet to podejście wymaga podstawowej wiedzy na temat klas w języku C++, chcąc w pełni wykorzystać jego możliwości. Może nie do końca jasno się wyraziłem - naszym celem będzie utworzenie szeregu bibliotek w C++, tak aby w prosty sposób można było zarządzać różnymi urządzeniami I/O (wejścia/wyjścia).
Zajmiemy się tematem
sterowania silników DC (możliwe również AC - wszystko zależy od modułów wykonawczych), tzn. zmiany kierunku oraz prędkości. Temat ten sukcesywnie będziemy rozszerzać o nowe możliwości tj. pomiar przebytej drogi, regulację położenia (PID) itd..
Nasz nowo utworzony program przedstawia się następująco:
język cpp
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Delikatnie go przerobię używając własnego stylu programistycznego, który oczywiście z pewnością nie jest tym najlepszym, ale dla mnie wygodniejszy i dużo bardziej przejrzysty.język cpp
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Tą kosmetyczną zmianą jest jedynie usunięcie chwilowo zbędnych komentarzy, gdyż nie ma sensu ich za każdym razem przytaczać. Bardziej istotną modyfikacją jest zamiana stałej dosłownej na odpowiadającą jej wartość
true. Może jestem pedantyczny i czepiam się szczegółów, ale w kodzie lubię mieć porządek i szczerze radzę takie podejście początkującym programistom, ponieważ jeśli nie dla Was to dla osób interpretujących taki kod będzie on łatwiejszy do zrozumienia.
Już w tym miejscu należy się zatrzymać i zastanowić, co to jest tak naprawdę
true. Jest to nowe słowo kluczowe języka C++, które w raz z
false i
bool tworzą nowy typ danych, jakim jest typ logiczny.
Z wartościami logicznymi można było się spotkać również w języku C, ale tam były one widoczne w sposób niejawny, tj. wyrażenia logiczne w warunkach, pętlach, czy też obliczeniach z operatorami logicznymi. Pamiętając zasady związanie z operacjami logicznymi można powiedzieć, że każda wartość różna od zera jest interpretowana jako
true, natomiast wartości równe zero to
false. Tak więc zapis:
bool a = true; oznacza to samo co
uint8_t a = 1;, a
bool b = false; to
uint8_t b = 0;, bo faktycznie zmienne typu logicznego są utożsamiane z jednobajtową zmienną bez znaku. Ktoś może w takim razie powiedzieć: „Po co wprowadzać nowy typ danych, skoro jest to ten sam typ co używany dotychczas.”, a ja odpowiem „Dla odseparowania wartości logicznych od liczbowych.”. Podobnie używamy słowa kluczowego typedef dla przedefiniowywania typów, ale tego zagadnienia nie śmiem ponownie opisywać, przy tak wyczerpującym temat
artykule mistrza .
Ale się rozpisałem na tak można powiedzieć prozaicznie jasny temat. Jeśli w ten dogłębny sposób będę opisywał każde zagadnienie to chyba nikt nie będzie w stanie doczytać do końca .Kontynuujemy więc nasze rozważania i od razu bez zbędnego owijania w bawełnę przejdziemy do kodu:
język cpp
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Użyte zostało tutaj słowo kluczowe
class definiujące klasę. Bez obaw nie jest to taka trudna kwestia, a już na pewno nie dla osób znających ideę struktur. Tak naprawdę klasę można formalnie zastąpić strukturą w języku C++, jednak nie jest to zalecany zabieg, gdy mamy do czynienia z bardziej złożonymi obiektami, które mogą być w bardziej czytelny sposób opisane za pomocą klas.
Najbardziej znaczącą różnicą między klasą, a strukturą jest domyślny operator widoczności (dla klasy jest to private, natomiast dla struktury public), o którym opowiem w dalszej części artykułu. Może być pewnie zaskoczeniem taka pełna zgodność wsteczna, ale zapewniam, że istnieje możliwość deklarowania funkcji tzw. metod w strukturze! O tym również dalej…
Dzięki klasie tworzymy pewien szablon (w tym przypadku silnika), który istnieje w rzeczywistości. Nie chodzi tutaj o jakiś konkretny silnik, ale ogólnie mówimy kompilatorowi, że istnieje coś takiego jak silnik. Podobnie jest w przypadku wszystkich typów danych -
uint8_t jest pewnym wzorcem definiującym pewien zakres liczb, ale nie jest to jakaś konkretna zmienna tego typu, przechowująca konkretną wartość w danej chwili czasowej.
Teraz pewnie padnie pytanie, dlaczego taką dziwną nazwę nadałem naszej klasie. Jest to spowodowane tym, że w swojej pracy używam również języka Delphi (Object Pascal), w którym standardem stało się definiowanie nazw klas od literki „T”. Nie wiem do końca jak sprawa się ma w przypadku języka C++ (wiele razy czytałem, że tak samo, ale nie chcę tutaj bzdur pisać, jeśli to nie prawda), ale domyślam się, że tak samo. Oczywiście nazwa klasy nie podlega jakimś ścisłym regułom (oprócz tycz dotyczących nadawania nazw zmiennych - nie będę tutaj wszystkiego, aż tak dokładnie opisywał). Musi jednak być intuicyjna i od razu rozpoznawalna, dlatego dodałem angielskie słówko „Motor” oznaczające silnik. Jeśli się komuś taki zapis nie podoba i uważa, że Polak musi programować w języku polskim to nie ma problemu, każdy może nadać taką nazwę, jaka mu się podoba.
Ja jednak będę kontynuował wszystkie zapisy w języku angielskim i to nie z tego powodu, aby pokazać jaki to biegły i obeznany jestem w tym języku, bo wręcz przeciwnie, a dlatego że zdecydowanie lepiej pisze mi się przy użyciu prostych wyrażeń angielskich, niż odmian i znaków diakrytycznych (które zdecydowanie odradzam, mimo iż kompilatory jej coraz bardziej wspierają - nie zalicza się jednak do nich AtmelStudio ) w języku polskim. Bardzo ważna jest sama konstrukcja klasy. W tak prostej i schematycznej formie, zapis sprowadza się do słowa kluczowego
class, po który następuje
nazwa klasy oraz
nawiasy klamrowe, koniecznie
zakończone średnikiem. Co prawda nie jest wymagane podanie nazwy klasy (klasa anonimowa) przy jednoczesnej deklaracji zmiennej obiektowej, ale nie jest to zalecane podejście. Chcąc zachować zasady dobrego stylu programowania w przyszłości będziemy dzielić kod na odrębne pliki, co wyklucza takie „laurkowe” sposoby.
Co więcej będziemy tworzyć i posługiwać się wskaźnikami na obiekt danej klasy, gdzie niezbędne jest wywołanie konstruktora, który nosi taką samą nazwę jak klasa (jeśli klasa nie posiada nazwy to nie jest możliwe wywołanie jej konstruktora).
Powyższe zdanie może wywołać u początkujących dreszczyk emocji i zniechęcenie do dalszego czytania, dlatego zdecydowałem się je obarczyć kursywą . Spokojnie wszystko się wyjaśni w dalszej części - gwarantuję, że po przeczytaniu ze zrozumieniem całego artykułu to zdanie okaże się śmiesznie proste .Może nie jest to do końca najlepszy moment, gdyż klasa jest zupełnie pusta tzn. nie zawiera żadnego interfejsu i struktury (pól, atrybutów); ale utworzymy obiekt tej klasy tzw. instancję klasy.
Chciałbym w tym miejscu usystematyzować wiedzę na temat tych dwóch pojęć w sposób obrazowy:
Klasa - np. samochód - wiadomo, że ma cztery koła, kierownicę i jeszcze kilka niezbędnych drobiazgów
)
Obiekt - konkretny pojazd danej marki, modelu oraz wersji
Zasady deklaracji zmiennych strukturalnych i obiektowych w C++ są mniej uciążliwe niż w „czystym” języku C, dlatego też nie jest wymaganie użycie słów kluczowych
struct oraz
class, a jedynie nazwy typu i zmiennej. Nie ma więc potrzeby przedefiniowywania typów (nadawania aliasów), co wcale nie wyklucza sensu stosowania
typedef.
CDN...Postaram się rozbudowywać ten artykuł w miarę możliwości czasowych.
Dyskusję na temat tego poradnika proszę prowadzić w oddzielnym wątku: topic3516.html