Kurs Arduino – #3 – UART (komunikacja z PC), zmienne

Kurs Arduino – #3 – UART (komunikacja z PC), zmienne

Omawiane Arduino pozwala na wykorzystanie licznych interfejsów komunikacyjnych. Dzięki nim tworzone konstrukcje mogą porozumiewać się z innymi układami, czujnikami lub komputerem.

W tej części kursu zajmiemy się UARTem. Jest to prosty i bardzo popularny interfejs szeregowy. Szczególnie podczas komunikacji z komputerem.

Jak działa UART? - Odrobina teorii

Jego zasada działania opiera się na szeregowym wysłaniu ciągu bitów, które następnie składane są w informację. Pojedyncza ramka (w uproszczeniu bajt) danych jest nadawany w poniższej postaci:

Przykładowy przebieg UART

Przykładowa ramka UART

Transmisja rozpoczyna się od bitu startu, zaznaczonego na rysunku jako BS. Zawsze jest to bit będący logicznym zerem. Następnie, zależnie od konfiguracji, następuje po sobie 7, 8 lub 9 bitów danych (tutaj zaznaczone jako B0-B7), które są wysyłaną informacją. Bit stopu (zaznaczony tutaj jako bit BK) to bit będący logiczną jedynką - mówi o końcu transmisji.

Wykorzystując UART w Arduino interesują nas dwa piny:

  • Tx do wysyłania danych (pin 1 w Arduino),
  • Rx do odbierania danych (pin 0 w Arduino).

Aby transmisja przebiegała prawidłowo, w obu układach musi być ustawiona ta sama prędkość przesyłu danych - zwana jako baud-rate. Określa ilość transmitowanych bitów na sekundę. Często spotykane wartości to: 9600 oraz 115200.

Komputer, z którym chcemy nawiązać łączność, musi być również wyposażony w odpowiedni interfejs. Niestety producenci PCtów zaprzestali wyprowadzania na zewnątrz portu szeregowego w standardzie RS-232, który jeszcze kilka lat temu był wyposażeniem większości komputerów.

Pozostaje nam komunikacja przez USB. Niestety zadanie to jest dość trudne. Dlatego najczęściej hobbyści stosują konwertery UART <-> USB, które znacznie ułatwiają sprawę. Korzystając z Arduino nie trzeba się o to martwić. Konwerter taki został już wbudowany w nasza płytkę.

Łączność dzięki USB.

Łączność dzięki USB.

W związku z tym nie musimy nawet podłączać nigdzie pinów 0 oraz 1. Wystarczy połączenie komputera z Arduino poprzez kabel USB (ten sam, który używany jest podczas programowania).

Przejdźmy do przykładów praktycznych. Pierwsze dwa programy wymagają TYLKO podłączenia Arduino przez USB z komputerem. Dopiero później dodamy do naszego układu peryferia.

Zestaw elementów do kursu

Gwarancja pomocy na forum Błyskawiczna wysyłka

Teraz możesz kupić zestaw ponad 70 elementów niezbędnych do przeprowadzenia ćwiczeń z kursu u naszych dystrybutorów!

Kup w Botland.com.pl

Pierwszy program w Arduino

Zadaniem poniższego programu jest proste, regularne wysyłanie tekstu do komputera:

Po wgraniu powyższego programu pozornie nic nie będzie się działo. Aby zaobserwować jego działanie, musimy z menu Arduino wybrać: Narzędzia->Monitor Portu Szeregowego. Dzięki temu otworzy się nowe okno. Będzie to potocznie zwany terminal. W tym miejscu możemy obserwować, to co jest przesyłane do/z Arduino przez port COM, czyli nasz UART.

Aktualizacja, styczeń 2016 - od aktualizacji środowiska Arduino IDE, funkcja omawiana tutaj jako Monitor portu szeregowo znajduje się pod opcją Szeregowy monitor. Jeśli w swoim programie widzisz obie opcje wybieraj teraz Szeregowy monitor, druga z opcji została omówiona w części 10.

Działanie program w praktyce:

Tekst przesyłany do terminala.

Tekst przesyłany do terminala.

Przejdźmy do analizy programu. Pierwszą rzeczą, którą należy wykonać jest ustawienie prędkości transmisji. Służy do tego funkcja Serial.begin(speed), gdzie speed określa prędkość transmisji. W tym przypadku jest to 9600 baud/sec. Natomiast do wysyłania ciągu znaków służy funkcja - Serial.println(val), gdzie val to ciąg znaków lub liczba.

Tekst "Witaj na Forbocie!" zostaje wyświetlony tylko raz, ponieważ umieściliśmy go w funkcji setup, a jak powinieneś pamiętać z poprzedniej części kursu - instrukcje tam zawarte wykonywane są tylko po uruchomieniu programu.

Zadanie domowe nr 2.1

Sprawdź co stanie się, gdy w oknie terminala będziesz wybierał prędkości inne od ustawionych w Arduino. Kiedy pojawiają się błędy? W jaki sposób się objawiają? Jak sam zobaczysz błędy są dość charakterystyczne, warto o tym pamiętać.

Interakcja z programem

Oczywiście informacje nie muszą być wysyłane przez UART cały czas, nadawanie i odbieranie może również przebiegać jednorazowo w wybranej przez nas chwili. Jest to bardzo użyteczne, chociażby do diagnozowania pracy układu lub sygnalizacji różnych zdarzeń.

Wyobraźmy sobie sytuację, w której Arduino byłoby zamontowane w roli sygnalizatora otwarcia okna, do którego ramy zamontowano specjalny przycisk/czujnik. Cały mechanizm działa w taki sposób, że gdy okno jest zamknięte przycisk zwierany jest do masy, w przeciwnym wypadku obwód jest przerwany.

Korzystając z poprzednio zdobytej wiedzy mógłbyś napisać program, który włącza kolorową diodę, gdy okno jest otwarte. Spróbujmy jednak rozbudować nasz program. Wyposażmy Arduino w przycisk (imitujący sensor w ramie okna) oraz dwie diody (zieloną i czerwoną).

arduinoKurs_2_1

Gdy okno jest zamknięte (przycisk wciśnięty) świeci się zielona dioda. Gdy przerwiemy obwód (przestaniemy wciskać przycisk) powinna włączyć się czerwona dioda, a w terminalu powinniśmy odczytać komunikat "Uwaga! Alarm! Okno nie jest zamknięte!".

Sprawdźmy jak działa program! Niestety nie najlepiej, gdy okno nie jest zamknięte informacja o alarmie nadawana jest cały czas. Wolelibyśmy jednak, aby wysyłana była tylko raz. Masz pomysł jak to zmienić? Oczywiście korzystając z pętli while, którą wykorzystywaliśmy poprzednio.

Czy możemy poprawić w powyższym kodzie coś jeszcze? Poprawić nie, ale oczywiście możemy go uczynić go bardziej eleganckim.

Instrukcja #define

Z czasem nasze programy będą się znacznie rozrastały. Co w przypadku, gdy konieczna będzie zmiana fizycznego podłączenia np.: diody lub przycisku?  Zmiana numeru pinu w każdym miejscu programu byłaby uciążliwa, prawda?

Z pomocą przychodzi nam dyrektywa #define. Pozwala ona na zdefiniowane symbolu, który będzie podmieniany przed kompilacją w każdym miejscu programu.  Przykładowo:

Umieszczając na początku programu linijkę:  #define ledPin 8 sprawimy, że przed kompilacją każde wystąpienie frazy ledPin zostanie zamienione na 8. Oczywiście zamieniana nazwa może być inna, ważne aby była unikalna i pomagała Ci podczas pisania długich programów.

Poniżej ostateczna, poprawiona wersja naszego okiennego alarmu:

Od teraz fizyczna zmiana pinu, pod który podłączymy diodę lub czujnik, będzie wymagała zmiany tylko w jednym miejscu programu.

Zmienne, czyli "uniwersalne szufladki"

Przed przejściem do kolejnych programów (w tym wysyłania informacji do Arduino przez UART) musimy zająć się zmiennymi. Jak zostało to określone w tytule tej sekcji, zmienne są uniwersalnymi szufladkami, do których możemy odkładać pewne wartości.

Konkretnie, w skrócie, mogą być to znaki, słowa lub liczby. Najczęściej spotkamy się właśnie, ze zmiennymi liczbowymi. Kiedy zmienne będą potrzebne? Gdy będziemy chcieli przechować jakąś wartość oraz wykonywać na niej operacje różnego typu. Zmienna, tak samo jak funkcja - może mieć swój specyficzny typ mówiący o tym, jakie dane może przechowywać.

Oto lista najważniejszych typów danych:

Na początku najczęściej będziesz wykorzystywał zmienne typu:

  • boolean
  • int
  • string

Boolean, tak jak już zostało wspomniane służy do przechowywania wartości prawda lub fałsz. Ten typ najczęściej będzie wykorzystywany do sygnalizacji zdarzeń lub sterowania warunkami.

Int, to najczęściej używana zmienna do przechowywania liczb całkowitych. Można w niej zapisać informacje takie jak ilość wciśnięć przycisku, ile razy wywołano dane zdarzenie lub odczyt z czujnika odległości (tym zajmiemy się pod koniec kursu). Na zmiennych można oczywiście wykonywać operacje matematyczne - więcej w praktycznych przykładach.

String, to ciąg znaków, czyli w najprostszym ujęciu w zmiennych tego typu będziemy mogli przechowywać napisy. Słowa, zdania, komunikaty.

Zmienne w praktyce

W celu użycia zmiennej musimy ją zadeklarować, czyli poinformować kompilator o jej typie oraz nazwie. Na szczęście nie musimy martwić się o przydzielenie pod nią konkretnego miejsca w pamięci mikrokontrolera. Jeśli jesteś jednak zainteresowany jak to wszystko działa w środku to zapoznaj się koniecznie z artykułem: Mikrokontroler – wszystko o jego działaniu.

Wracając do meritum, nazwę każdej zmiennej rozpoczynamy od litery. Cyfry nie mogą znajdować się na jej początku. Każdą deklarację należy dokonywać w następujący sposób:

Powyższe dwie metody powinny dać identyczny wynik. Pierwsza jest jednak bezpieczniejsza, ponieważ natychmiast po utworzeniu zmiennej przypisuje jej wartość początkową. Druga metoda w teorii powinna również utworzyć zmienną o wartości początkowej równej zero. W 99,99% przypadkach tak właśnie będzie. Jednak dobrym nawykiem jest ręczne ustawianie wartości początkowych zaraz przy deklaracjach.

Zmienne mają cechę zwaną zasięgiem. Jeżeli dana zmienna jest umieszczona w jednej funkcji, procedurze lub podprogramie, to nie będzie widoczna (nie będziemy mogli z niej korzystać) w innym podprogramie. Kilka przykładów:

Teraz może być to mało jasne, jednak spokojnie - z czasem wszystko poznasz w praktyce. Na początku dla ułatwienia będziemy jednak wykorzystywać tylko zmienne globalne. Doświadczeni programiści mogą tutaj wytykać niepoprawność - jednak do wszystkiego dojdziemy, powoli.

Jest jeszcze jedna bardzo ważna sprawa - nazewnictwo zmiennych. Pamiętaj, aby zmiennym nadawać nazwy, które będą określały ich przeznaczenie. Czyli przykładowo zamiast:

należy stworzyć zmienną:

Problem może pojawić się, gdy przeznaczenie zmiennych będzie bardziej skomplikowane. Nie bój się jednak tworzyć takich nazw jak:

Najważniejsze, aby program był czytelny.

Wykorzystanie zmiennych - licznik

Teoria za nami - teraz czas na praktykę. Zacznijmy od czegoś bardzo prostego. Na początku niech nasz program w każdym obiegu pętli wypisuje wartość zmiennej, którą będziemy zwiększać.

Oczywiście na samym początku umieszczona została deklaracja zmiennej globalnej. Dzięki czemu dostęp do niej będziemy mieli z każdego miejsca programu. Następnie standardowo uruchamiana jest transmisja. Nasza główna pętla loop(), wykonuje 3 czynności.

  1. Sięgamy do miejsca w pamięci, które zadeklarowaliśmy jako zmienną licznik, a następnie wysyłamy znalezioną tam wartość przez UART.
  2. Zwiększamy aktualną wartość zmiennej licznik o 1.
  3. Czekamy 100ms (dla lepszego efektu) i wracamy na początek pętli.

Wyjaśnienia może wymagać punkt 2, czyli zwiększanie zmiennej. Użyty została zapis:

Z punktu widzenia matematycznego, gdzie znak "=" oznacza równość powyższa linijka nie powinna działać. Jednak w programowaniu znak "=" oznacza przypisanie. W praktyce powyższy kod należy rozumieć jako następującą operacje:

  1. weź wartość zmiennej licznik,
  2. dodaj do niej 1,
  3. tak otrzymaną wartość zapisz do zmiennej licznik.

Załaduj program do Arduino, aby przekonać się, że działa on poprawnie. Oczywiście, aby zobaczyć efekty należy uruchomić monitor portu szeregowego (terminal).

Efekt działania pierwszego programu ze zmienną.

Efekt działania pierwszego programu ze zmienną.

Zadanie domowe nr 2.2

Co stanie się, gdy deklarację zmiennej licznik przeniesiesz do pętli loop()?

Zadanie domowe nr 2.3

Jak zostało wspomniane, zmienne mają swoje ograniczenia. Zmień deklarację zmiennej licznik z int na byte, która może pomieścić liczby z zakresu 0-255. Co dzieje się, gdy wartość ta zostanie przekroczona? Obserwuj uważnie wyświetlane wyniki!

Transmisja dwukierunkowa z Arduino

Oczywiście komunikacja, aby była użyteczna, powinna odbywać się w dwóch kierunkach. Do tej pory to Arduino wysyłało informacje do nas. Pora abyśmy mogli mu "odpowiadać".

Zadaniem pierwszego programu będzie "nasłuchiwanie" naszego imienia. Gdy je prześlemy, Arduino powinno nas przywitać komunikatem "Witaj Imie!", gdzie oczywiście wstawione zostanie przesłane wcześniej imię.

Na początku deklarujemy zmienną odebraneDane, do których kopiowany będzie ciąg odebranych znaków. Poznajemy nową funkcję: Serial.available(). Funkcja ta zwraca ilość bajtów, które zostały odebrane i czekają na obsługę przez Arduino.

W wypadku kiedy dane będą dostępne (będzie ich więcej niż 0) - rozpoczyna się ich zapisywanie do zmiennej odebraneDane. Dzieje się to za pomocą funkcji Serial.readStringUntil(terminator), która kopiuje dane z bufora do momentu napotkania znaku terminatora (w tym przypadku "\n" - czyli znak nowej linii).

Interakcja z układem - sterowanie diodami przez UART

Wykorzystamy możliwość wysyłania tekstu do Arduino, aby sterować diodami LED. W tym celu należy podłączyć dwie diody zgodnie z poniższym rysunkiem (diody na pinach 8 i 9).

Ard_2_6

Zadanie naszego programu, to włączanie zielonej lub czerwonej diody na 1 sekundę, gdy do Arduino zostanie wysłana odpowiednia komenda. Gotowy program prezentuje się następująco:

Przeanalizujmy teraz działanie programu. Na początku definiowane są nr pinów z diodami oraz deklarowana jest zmienna, do której kopiowane są odebrane dane. Następnie w pętli sprawdzamy czy Arduino odebrało dane. Jeśli tak to sprawdzamy, czy dane te pasują do jednego z kolorów. Na tej podstawie włączane są odpowiednie diody.

Zadanie domowe nr 2.4

Przerób powyższy program  w taki sposób, aby w przypadku błędnego koloru wysyłał odpowiedni komunikat przez terminal. Swoim rozwiązaniem podziel się w komentarzu!

Zadanie domowe nr 2.5*

Zadanie trudniejsze, z gwiazdką . Napisz program, który po wysłaniu koloru diody zmieni jej stan na przeciwny. Jeśli dioda jest włączona, to zostanie wyłączona i na odwrót. Podpowiedź: koniecznie użyj dodatkowych zmiennych typu bool, które będą pamiętały aktualny stan diod.

Podsumowanie

Pierwotnie, ta część kursu miała być dłuższa. Uciąłem jednak specjalnie część materiału, aby nie wprowadzać zbyt dużo nowości jak na jeden raz. Ta "wyjęta część" materiału ukaże się w formie dodatkowej części kursu.

Po przeczytaniu tego artykułu każdy powinien umieć napisać program komunikujący się w prosty sposób z komputerem. Lekcja była stosunkowa prosta, ale poświęć jej odpowiednią ilość czasu. Zrób wszystkie zadania domowe - KONIECZNIE! Na Wasze pytania czekam w komentarzach!

Pamiętaj, że komplet elementów niezbędnych do przeprowadzenia wszystkich ćwiczeń jest dostępny w Botlandzie. Zakup zestawów wspiera kolejne publikacje na Forbocie!

Zmienne, to element programowania, który będzie pojawiał się już zawsze! Z kolei UART będziemy wykorzystywać często do wyświetlania zebranych informacji, zmiany ustawień naszego programu. Będzie on również bardzo przydatny podczas wyszukiwania błędów w naszych kodach!

Nawigacja kursu

Autorzy: Sławomir Kozok
Damian (Treker) Szymański

P.S. Nie chcesz przeoczyć kolejnych części naszego darmowego kursu programowania Arduino? Skorzystaj z poniższego formularza i zapisz się na powiadomienia o nowych publikacjach!

arduino, kursArduino, uart, USART, zmienna