[079] Układamy wiedzę - cz. 5
Przykłady w poprzednich artykułach zawierały tajemnicze wyrażenia włączające świecenie diod i sprawdzanie pstryczków. Mówiłem, że do tego powrócę i teraz będzie czas na przyjrzenie się temu krok po kroku. Każde Arduino ma pewną liczbę pinów, z których możemy tworzyć funkcjonalne porty: do włączania światła, analizy stanu przełącznika, pomiaru napięcia i wielu innych rzeczy. Jeśli przeanalizujemy wszystkie zadania z tym związane, okaże się, że wcale aż tak wiele tego nie ma.
Po pierwsze: każdy port może być wyjściowy, czyli będzie oferował jakieś napięcia albo wejściowy – w tym wypadku napięcia będą musiały być doprowadzone. Po włączeniu prądu czy po resecie możemy wszystkie piny traktować jak nieokreślone i te, z których mamy zamiar korzystać, należy koniecznie zdefiniować. Teoretycznie może to być uczynione kiedykolwiek, byle przed pierwszym użyciem pinu, ale przyjęło się to robić na początku programu, w bloku setup. Piny możemy zdefiniować na trzy sposoby. Pierwszym jest wyjście.
pinMode(diodaSwiecaca, OUTPUT); // Zadeklaruj port diody jako wyjście.
Odtąd pin, który nazwałem diodaSwiecaca zamienia się w port wyjściowy, który może wystawiać napięcie. Ile prądu możemy pociągnąć z takiego portu? Zależy od wersji Arduino. Klasyczne Uno na przykład pozwala aż na 40 mA, choć zaleca się nie przekraczać połowy tej wartości. Trzeba też pamiętać, że równocześnie nie można ze wszystkich pinów ciągnąć więcej niż 200 mA w sumie. Nowe płytki, zwłaszcza pracujące na 32-bitowych mikrokontrolerach, mają znacznie mniejsze możliwości i wszystko to należy sprawdzić w dokumentacji.
Po drugie: każdy port może być wejściowy, więc będzie on takim niby woltomierzem, mierzącym napięcie dołączone do pinu.
pinMode(pstryczek, INPUT); // Zadeklaruj port pstryczka jako wejście.
Możemy tam podłączać źródła napięć, na przykład inne układy, które wysyłają jakieś przebiegi. Zasada generalna brzmi: napięcie podłączane nigdy nie może być wyższe niż napięcie zasilania. Czyli 5 woltów dla Uno, a 3,3 wolty dla niskonapięciowych płytek. Nie wolno także podrzucać tam napięć ujemnych. Jeśli byłoby takie ryzyko, trzeba użyć diod Zenera i rezystorów, tworząc obwód zabezpieczający.
Ale to nie wszystko. Wiele badanych elementów same z siebie nie generują napięcia: na przykład przełącznik albo potencjometr. Żeby móc analizować stan takich elementów, należy podać im napięcie. Mikrokontrolery mają taką możliwość, wystawiając na porty napięcie zasilania o niewielkiej wydajności. Mówi się o takim porcie, że jest „podciągnięty”, w domyśle do plusa zasilania. Jeśli teraz podłączymy doń pstryczek zwierający port do masy, będziemy mogli w prosty sposób stwierdzić czy jest on wciśnięty – na wejściu otrzymamy zero albo nie – na wejściu otrzymamy jedynkę, czyli napięcie bliskie zasilanemu. By wymusić takie zachowanie pinu, należy użyć trzeciego typu jego deklaracji.
pinMode(pstryczek, INPUT_PULLUP); // Zadeklaruj port pstryczka jako wejście.
Dotąd wspominałem o deklarowaniu sposoby pracy pinu. Skoro to zrobimy, przyjrzyjmy się jak korzystać z owego dobra w programie. Zacznijmy od sytuacji, w której pin jest portem wyjściowym. Znowu mamy tutaj dwie sytuacje: prostą - dwustanową i udającą źródło napięcia analogowego.
digitalWrite(diodaSwiecaca, HIGH); // Włącz diodę.
W pierwszym przypadku użyjemy funkcji digitalWrite, po czym nastąpi nawias, a w nim adres pinu oraz stan: niski bądź wysoki. Jak mówiłem, możemy użyć zera i jedynki, false i true albo – co jest najbardziej logiczne – pary LOW i HIGH. Adresy pinów znajdziemy w dokumentacji każdego Arduino. W przypadku Uno mamy 14 pinów: od 0 do 13, przy czym dwa pierwsze są zajęte podczas programowania układu, a na trzynastym wstawiono diodę świecącą. Warunkowo możemy także skorzystać z dodatkowych sześciu pinów oznaczonych literą A, które służą mierzeniu napięcia, co daje nam baterię 20 wyjść. Dlatego wszelkie testy z diodą możemy przeprowadzać bez podłączania czegokolwiek, bo diodę tam już mamy. Dociekliwym dodam, że nie tak wprost podpiętą nieładnie, a sprzężoną wzmacniaczem operacyjnym, co w praktyce znaczy, że w zasadzie nie obciąża ona tego portu i nie wpływa na inne obwody tam dołączane. Acz w nieoryginalnych Arduino wcale tak nie musi być.
Innym zachowaniem jest udawanie, że port jest analogowy i może wystawiać dowolne napięcie z przedziału od zera do napięcia zasilania. I tak – to jest udawanie tylko. Tak naprawdę to wciąż tam się pojawia pełne napięcie albo jego brak, tylko bardzo szybko ten stan się zmienia, a średnia energia – tak to nazwijmy – na porcie związana jest ze stosunkiem pełnego napięcia do jego braku, bo na to właśnie możemy wpływać. Dioda zasilana takim napięciem o zmieniającym się wypełnieniu (PWM) – bo tak się formalnie nazywa zmiana stosunku napięć do jego braków – będzie świecić z jasnością proporcjonalną do wartości owego wypełnienia. Wskazówka miernika także będzie się wychylać proporcjonalnie, a silnik – będzie się proporcjonalnie obracał. Ale gdyby jakieś urządzenie nie akceptowało takiego poszatkowanego napięcia, musimy go scałkować (wygładzić) filtrem dolnoprzepustowym albo podkręcić częstotliwość wyżej, w rejony, które nie będą już przeszkadzać odbiornikowi. Domyślna częstotliwość wynosi niecałe 500 Hz, ale możemy ją zmienić: od 30 Hz do 62 i pół kHz w kilku krokach za pomocą nieco bardziej zaawansowanych instrukcji przestawiających dane w rejestrach odpowiedzialnych za te częstotliwości.
analogWrite(diodaSwiecaca, jasnosc); // Wystaw na port diody przebieg o wypełnieniu określonym zmienną jasnosc.
Oczywiście instrukcja ma teraz inne brzmienie, już nie digital, a analogWrite, a w nawiasie adres i wartość, zwykle ośmiobitowa, określająca wypełnienie. No i jeszcze jedna ważna rzecz: sporo płytek Arduino umożliwia stworzenie analogowego wyjścia tylko z niektórych pinów. Uno pozwala na takie sztuki z sześcioma jedynie i w dokumentacji należy poszukać szczegółów.
Czas przeanalizować pracę w drugą stronę, czyli jak realizować pomiar napięć. Najpierw zajmijmy się mierzeniem stanów, czyli zwracaniem zera lub jedynki. Przy czym zero, LOW czy false są zwracane, gdy napięcie rzeczywiste jest bliskie zeru – ale nie musi to być dokładnie zero, a może wynieść maksymalnie osiem dziesiątych wolta (dla urządzeń pięciowoltowych). Dobrym zwyczajem jest jednak nie podchodzić aż tak wysoko. Jedynka, HIGH albo true są zwracane, gdy napięcie osiągnie co najmniej połowę napięcia zasilania. I znowu nie warto siedzieć tak nisko, starając się raczej dobijać do pięciu woltów dla układów pięciowoltowych i odpowiednio mniej dla tych, które zasilane są niższym napięciem.
if (digitalRead(pstryczek) == HIGH) {...
Realizacja jest równie prosta jak poprzednio: pytamy o digitalRead, a w nawiasie podajemy adres. I jak poprzednio, każdy pin Arduino może być przepytywany, także te spoza adresów od zera do trzynastki, czyli analogowe. Jedyny wyjątek znajdziemy w wersji Nano i Mini. Tam na zasadzie gratisu dołożono jeszcze dwa piny dla pracy analogowej: A6 i A7, które tej mocy nie posiadają.
if (analogRead(potencjometr) > 511) {...
W końcu mamy ostatni przypadek: czytamy napięcie analogowe. Tym razem pin będzie pracował jak cyfrowy woltomierz o rozdzielczości zależnej od modelu Arduino. Uno i pochodne dysponują najniższą rozdzielczością, ale i tak niezłą, bo dziesięciobitową. AnalogRead z numerem portu w nawiasie zwraca poziom zmierzonego napięcia, ale nie w woltach, a w jednostkach relatywnych względem… no właśnie. Domyślnie poziom odniesienia stanowi masa i zasilanie. Więc dla zera woltów dostaniemy zero, a dla pięciu – 1023, bo to jest największa dziesięciobitowa liczba. Działa to nieźle, ale z dokładnością różnie jest, bo wiadomo, że napięcie zasilania to nie jest szczyt stabilności. Dlatego za pomocą polecenia analogReference możemy narzucić inny punkt odniesienia dla najwyższego mierzonego napięcia. Do wyboru są: INTERNAL – bardzo dokładne źródło napięcia wbudowane w układ – ale jego wartość zależy od typu układu albo EXTERNAL – źródło dołączane do specjalnego wejścia, które może być już tak dokładne, jakie zbudujemy.
Natomiast owe wirtualne woltomierze znajdują się na innych pinach niż poprzednio omawiane, oznaczane są literą A i cyferkami. Uno ma ich sześć, Nano i Mikro 8, inne układy mogą mieć takich wejść kilkanaście. Jak mówiłem, w razie potrzeby mogą one być wypożyczane do zestawu portów cyfrowych, tracąc swoje analogowe właściwości.