Cyfrowe wejście/wyjście

Cyfrowe piny ARDUINO mogą być wykorzystane jako wejścia lub jako wyjścia. Niezbędne są więc funkcje, które umożliwią ustawienie (skonfigurowanie) pinów oraz zapis lub odczyt danych.

pinMode()

Polecenie pinMode() umożliwia określenie pracy poszczególnych pinów. Jego składnia jest następująca:

pinMode(pin, tryb_pracy);

pin to określenie numeru pinu układu ARDUINO, tryb_pracy to jedna z predefiniowanych stałych: INPUT, OUTPUT, INPUT_PULLUP. Stałe zostały opisane w innym artykule kursu. Przykładowe deklaracje trybu pracy pinów:

pinMode(13,OUTPUT);
pinMode(8,INPUT);

#define ledPin 10
pinMode(ledPin,OUTPUT);

const byte przycisk=4;
pinMode(przycisk,INPUT_PULLUP);

int przycisk2=5;
pinMode(przycisk2,INPUT_PULLUP);

Jak widać deklaracja sposobu funkcjonowania pinu jest prosta. Warto zwrócić uwagę na fakt, że ten sam efekt można osiągnąć na kilka sposobów. Pierwsze dwa przykłady bazują na bezpośrednim podaniu numeru pinu i określeniu trybu jego działania. Jest to deklaracja prawidłowa, jednak nie zaleca się jej stosowania. Warto skorzystać z jednego z kolejnych sposobów. Bezpośrednie odwoływanie się do numerów pinów może prowadzić do zaciemnienia kodu i pomyłek. Przez cały czas pisania i testowania programu należy pamiętać co jest podłączone do konkretnego pinu ARDUINO. Lepszym rozwiązaniem jest zastosowanie definicji, stałej lub zmiennej. Definiując nazwę dla konkretnego pinu określamy (poprzez nazwę) jego zastosowanie. Zdecydowanie łatwiej zapamiętać, że ledPin to pin, do którego podłączono diodę LED w projekcie niż, że LED podłączono do pinu 10. Warto pamiętać, że deklarując odpowiednie nazwy będziemy się do nich odwoływać w programie, nie tylko podczas definicji trybu pracy. Definicja nazwy za pomocą dyrektywy define, lub const jest tożsama z praktycznego punktu widzenia. Kompilator zastąpi podaną nazwę przez przypisaną do niej wartość. Programiści ArduinoIDE zalecają stosowanie const. Ja w odniesieniu do pinów układu zalecam stosowanie define jako jednoznacznego wyróżniającego się przyporządkowania pinu do nazwy. Dodatkową zaletą stosowania define jest zwyczajowe jego umieszczanie na początku kodu, co ułatwia modyfikację układu elektronicznego w przyszłości. Zmieniając przyporządkowanie poszczególnych elementów układu zmieniamy wyłącznie numery pinów w definicji i cały program działa prawidłowo.

W ostatnim przykładzie pokazano definicję zmiennej jako nazwy do pinu układu. Taka definicja występuje na oficjalnej stronie dokumentacji ARDUINO. Moim zdaniem nie jest to najlepszy przykład, ponieważ zmienna zabiera cenne miejsce w pamięci. Jedynym uzasadnionym przykładem stosowania zmiennej może być sytuacja, gdy chcemy w pętli wykonywać operacje na wielu pinach. Są to jednak sytuacje szczególne. Pokazany schemat połączeń umożliwia przetestowanie opisanych w artykule funkcji cyfrowych. Podłączenie przycisku do masy układu zakłada wykorzystanie wewnętrznego rezystora pullup.

Czasami nie wykorzystujemy pinów analogowych układu, a chcielibyśmy wykorzystać je jako wejście lub wyjście cyfrowe. W takim przypadku za pomocą funkcji pinMode() ustawiamy odpowiedni tryb dla pinów A0 - A5..

#define zasilanie A0

void setup()
   {
     pinMode(zasilanie,OUTPUT);
   }

Wykorzystanie jednego z pinów A0 - A5 jako cyfrowych nie powoduje braku możliwości wykorzystania innych jako wejścia analogowe. Możliwe jest korzystanie np. z 2 wejść analogowych i 4 wyjść cyfrowych w ramach pinów A0 - A5.


    digitalWrite()

Funkcja digitalWrite() umożliwia ustawienie stanu pinu zdefiniowanego jako cyfrowe wyjście. Jej składnia jest zbliżona do pinMode(). Wymaga dwóch parametrów. pierwszym jest numer pinu, drugim wartość logiczna LOW lub HIGH. Ustawienie wartości LOW spowoduje wystawienie na pinie napięcia 0V (zwarcie do masy układu), wartość HIGH to w zależności od wersji układu 3,3V lub 5V (zależne od napięcia zasilania układu ARDUINO). Poniżej przykład zastosowania funkcji digitalWrite():

#define ledPin 13
void setup()
   {
     pinMode(ledPin,OUTPUT);
   }

void loop()
   {
    digitalWrite(ledPin,HIGH);          //zapal LED
    delay(500);                         //poczekaj 500ms (0,5s)
    digitalWrite(ledPin,LOW);           //zgaś LED
    delay(500);                         //poczekaj 500ms (0,5s)
   }

Jak widać na przykładzie możliwe jest ustawienie odpowiedniego stanu na wyjściu. Zastosowana w przykładzie funkcja delay() umożliwia wykonanie opóźnienia i będzie omówiona w dalszej części artykułu.

Wykorzystanie funkcji digitalWrite() w przypadku, gdy pin jest ustawiony jako wejście spowoduje włączenie lub wyłączenie trybu PULLUP czyli podanie zasilania przez rezystor podciągający do napięcia zasilania. W nowszych wersjach ArduinoIDE zdecydowanie lepiej stosować pinMode() z parametrem INPUT_PULLUP;

    digitalRead()

Funkcja digitalRead() umożliwia odczytanie stanu wejścia z pinu zdefiniowanego jako wejście cyfrowe. Funkcja ta zwraca wartość LOW lub HIGH. Należy podać numer pinu z którego chcemy odczytać stan.

digitalRead(pin);

Jeżeli pin, z którego czytamy nie jest podłączony w danej chwili do masy ani do zasilania funkcja może zwrócić losowo wartość LOW lub HIGH. Należy o tym pamiętać podczas projektowania układu elektronicznego. Często stosuje się rezystor podciągający wewnętrzny lub zewnętrzny, żeby zapobiec odczytywaniu przypadkowych informacji. Poniżej przykłady odczytu wartości cyfrowej z pinu wejściowego:

#define ledPin  13         // dioda LED podłączona do pinu 13
#define inPin   7           // przycisk podłączony do pinu 7
int val = 0;                // zmienna przechowująca odczytany stan wejścia

void setup()
{
  pinMode(ledPin, OUTPUT);  // pin 13 jako wyjście
  pinMode(inPin, INPUT_PULLUP);    // pin 7 jako wejście
}

void loop()
{
  val = digitalRead(inPin); // odczyta stanu z pinu 7
  digitalWrite(ledPin, val);// ustawienie stanu diody zależne od stanu wejścia
}

Powyższy przykład pokazuje jak odczytać stan wejścia i wykorzystać tą informację do sterowania wyjściem. Zmienna val przechowuje wartość LOW lub HIGH w zależności od stanu wejścia. Funkcja digitalWrite() ustawia stan wyjścia na podstawie zmiennej val. Często uzależniamy wykonanie jakiejś operacji bezpośrednio od stanu pinu. Wtedy możemy napisać:

#define ledPin  13         // dioda LED podłączona do pinu 13
#define inPin   7           // przycisk podłączony do pinu 7

void setup()
{
  pinMode(ledPin, OUTPUT);  // pin 13 jako wyjściet
  pinMode(inPin, INPUT_PULLUP);    // pin 7 jako wejście
}

void loop()
{
  if(digitalRead(inPin)) //bardziej jawny sposób: if(digitalRead(inPin)==HIGH)
      digitalWrite(ledPin, HIGH);
  else
      digitalWrite(ledPin, LOW);
}

Powyższy przykładowy program również zapala i gasi diodę w zależności od stanu podłączonego przycisku. Działanie to samo, składnia programu trochę inna. Często można zastosować zamiennie powyższe dwa rozwiązania, w pewnych przypadkach łatwiej jest zastosować pierwszy przykład, w innych drugi.