Przyciski i debouncing

W tym samouczku zajmiemy się problemem poprawnego odczytywania stanu przycisków. Wbrew pozorom samo digitalRead(PIN) może okazać się niewystarczające...

Czym jest przycisk

Przyciski (buttony, pushbuttony, tactswitche) są powszechnie stosowanym elementem z Arduino. Jednak czasem dla początkujących irytujące jest właściwe ich podłączenie – część nóżek jest zwarta ze sobą. Wsadzenie przycisku w złej orientacji powoduje, że nie działa (nie dotyczy to wszystkich przycisków). Do czego można takie przyciski wykorzystać? Można z nich zrobić układ prostej klawiatury, dzięki której obsłużymy nasz interfejs, możemy je wykorzystać przy budowie np. prostego termostatu do ustawiania temperatury. Możliwości jest wiele, a jedynym ograniczeniem jest wasza wyobraźnia.

Różnego rodzaju przycisku

Jak podłączyć przycisk

Popatrzmy sobie na przyciski które zostały wykorzystane w powyższym zdjęciu. Na pierwszy ogień najbardziej znany microswitch, który można spotkać w klawiaturach w większości projektów. W sprzedaży dostępne są różne średnice oraz różne wysokości przycisku, jednak zasada działania i podłączenia nadal pozostaje ta sama.

Przycisk microswitch

Każdy z przycisków posiada 4 nóżki, które połączone są w pary. Kiedy naciskamy przycisk następuje zwarcie ze sobą tych nóżek. Jeśli spojrzycie na poniższe zdjęcia to zobaczycie, że bardzo ważna jest orientacja przycisku na płytce PCB. Jeśli przez pomyłkę taki przycisk przekręcicie o 90 stopni, to spowoduje to wieczne zwarcie w układzie (układ będzie myślał, że przycisk jest cały czas wciśnięty), a co za tym idzie nie będzie spełniał swojej roli.

Kolejny przycisk to przełącznik dźwigniowy, który posiada 3 nóżki. Środkowa nóżka jest nóżką wspólna, natomiast skrajne posiadają stan otwarty i zamknięty. Przełączając dźwignie zmieniamy stany skrajnych nóżek na przeciwne. W internecie możecie się spotkać z takimi przełącznikami, które będą miały dwie lub trzy pozycje przełączania.

Przełącznik MTS-1

Ostatni największy na zdjęciu przycisk, to nic innego jak przycisk microswitch zintegrowany z dużą obudową i podświetleniem. Zasada działania jest taka sama jak w przypadku pierwszego opisywanego przycisku.

Przycisk Big Red Button

Skoro wiemy już na jakiej zasadzie działają przyciski to czas podłączyć i sprawdzić jak obsłużyć taki przycisk z użyciem Arduino.

Wykorzystanie wbudowanych rezystorów podciągających (pull-up)

W celu poprawnego podłączenia przycisku należy użyć rezystorów, które będą pełnić rolę odszumiania. Oznacza to, że jeśli przycisk będzie naciśnięty na porcie pojawi się stan wysoki, jeśli puścimy przycisk to będzie stan niski. Gdybyśmy nie użyli rezystorów, mogłaby się zdarzyć sytuacja że po puszczeniu przycisku na porcie pojawi się stan nieokreślony bądź jakieś szumy czy zakłócenia.

Atmega328, która jest w Arduino posiada takie rezystory już w sobie. W związku z tym możemy odpuścić montaż zewnętrznych rezystorów. Jeśli bedziemy wykorzystywali wewnętrzny pull-up (tak się nazywa użycie wewnętrznych rezystorów podciągających) nasz układ będzie wyglądać bardzo prosto.

Układ na płytce testowej

Schemat układu

Do testów możemy użyć przykładowego kodu ze strony arduino.cc, którego zadaniem jest zapalenie wbudowanej diody podłączonej do pinu 13 kiedy przycisk zostanie naciśnięty, a zgaszenie jej kiedy przycisk puścimy.

void setup() {
  //rozpoczęcie połączenia serial
  Serial.begin(9600);
  //konfiguracja pinu2 jako wejście i aktywowanie wbudowanego rezystora podciągającego
  pinMode(2, INPUT_PULLUP);
  //konfiguracja pinu13 jako wyjście
  pinMode(13, OUTPUT);

}

void loop() {
  //odczytanie stanu przycisku i przypisanie go do zmiennej sensorVal
  int sensorVal = digitalRead(2);
  //przesłanie odczytanej wartości zmiennej sensorVal do portu szeregowego
  Serial.println(sensorVal);

  // Należy pamiętać, że po aktywacji pull-up logika przycisku jest odwrócona
  // Jeśli przycisk jest nie naciśnięty to jego stan jest wysoki (HIGH),
  // a stan niski (LOW) kiedy jest naciśnięty. Dioda będzie zapalona kiedy przycisk
  // zostanie naciśnięty:
  if (sensorVal == HIGH) {
    digitalWrite(13, LOW);
  } else {
    digitalWrite(13, HIGH);
  }
}

Podłączenie bez wykorzystania pull-up

Jeśli nie chcecie korzystać z pull-up, to musicie do każdego przycisku dołożyć rezystor (może być, a nawet powinien mieć wartość 10K). Układ będzie wyglądał tak jak na schemacie poniżej.

Układ na płytce testowej

Schemat układu

W tym przykładzie wykorzystamy również kod znajdujący się na stronie arduino.cc. Tak jak pisaliśmy powyżej, w tym przykładzie nie będziemy korzystali z wbudowanego rezystora podciągjącego.

const int buttonPin = 2;     // przypisanie stałej do pinu przycisku (stałe się nie zmieniają) - pin 2
const int ledPin =  13;      // przypisanie stałej do pinu LED - pin 13

// variables will change:
int buttonState = 0;         // przypisanie zmiennej do odczytywania stanu przycisku

void setup() {
  // konfiguracja pinu LED jako wyjścia:
  pinMode(ledPin, OUTPUT);
  // konfiguracja pinu przycisku jako wejścia:
  pinMode(buttonPin, INPUT);
}

void loop(){
  // odczyt stanu przycisku:
  buttonState = digitalRead(buttonPin);

  // sprawdzenie czy przycisk jest naciśnięty
  // jeśli jest to ustawienie zmiennej buttonState w stan wysoki (HIGH):
  if (buttonState == HIGH) {
    // jeśli buttonState będzie mieć stan wysoki to dioda LED zostanie zapalona:
    digitalWrite(ledPin, HIGH);
  }
  else {
    // w innym wypadku dioda LED nie będzie się świecić:
    digitalWrite(ledPin, LOW);
  }
}

Drgania zestyków

Wyszstkie przyciski, przekaźniki mechaniczne, kontaktrony posiadają problem związany z drganiem styków. Ma to związek głównie z tym, iż styki wykonane są najczęściej z metali sprężystych. Kiedy przycisk zostaje naciśnięty powoduje to odkształcenie się styków, co prowadzi do kilku drgań zanim styki się ze sobą zewrą. W rezultacie otrzymujemy krótkie pulsy zamiast jednej czystej zmiany z jednego stanu do drugiego. Problem jest poważny w systemach, gdzie układy są w stanie wyłapać drgania myląc je z prawidłowym naciśnięciem przycisku. Np. jeśli zrobilibyśmy układ liczący naciśnięcia przycisku, może się okazać, że zliczy on więcej naciśnięć niż faktycznie fizycznie wykonaliśmy. Drgania styków mogą trwać od kilkudziesięciu mikrosekund do kilkudziesięciu milisekund niezależnie czy zwieramy czy rozwieramy styki. Niektóre noty katalogowe przycisków podają maksymalny czas drgania styków, dzięki czemu użytkownik może je wyeliminować np. programowo.

Drgania styków widziane na oscyloskopie

Jak zapobiegać

Jeśli wiemy, że drgania mogą wynosić np.20ms możemy programowo dopisać instrukcje, a by program odczekał po wykryciu naciśnięcia przycisku 20ms i ponownie sprawdził jego stan. Jeśli przycisk będzie nadal naciśnięty to wtedy program może wykonać wcześniej zdefiniowaną instrukcję. Jest to jedna z prostszych metod. Jeśli nie wiemy jak długo mogą trwać drgania wartość czasu będziemy musieli dobrać testowo.

Kolejną możliwością jest wykorzystanie biblioteki Bounce2, która posiada algorytmy wykrywające drgania i eliminujące je.

#include <Bounce2.h>

#define BUTTON_PIN 2
#define LED_PIN 13

// Inicjalizacja obiektu Bounce
Bounce debouncer = Bounce(); 

void setup() {

  // Konfiguracja pinu przycisku i aktywowanie rezystora podciągającego:
  pinMode(BUTTON_PIN,INPUT_PULLUP);

  // Po konfiguracji przycisku, ustawienie działania funkcji Bounce :
  debouncer.attach(BUTTON_PIN);
  debouncer.interval(5); // interwał w ms

  //Konfiguracja pinu diody LED :
  pinMode(LED_PIN,OUTPUT);

}

void loop() {
  // Aktualizacja funkcji Bounce :
  debouncer.update();

  // Odczyt zaktualizowanej wartości (value) :
  int value = debouncer.read();

  // W zależności od stanu włączenie lub wyłączenie diody LED :
  if ( value == LOW ) {
    digitalWrite(LED_PIN, HIGH );
  } 
  else {
    digitalWrite(LED_PIN, LOW );
  }

}

Jak widać w powyższym kodzie użytkownik może sobie zdefiniować czas trwania drgania styków (debouncer.interval()), a później odzytywać już stan przycisku. Biblioteka ta jest bardzo przydatna, bowiem na samym początku definiujemy parametry odczytu (czas trwania drgań), a później wystarczy tylko odczytać stan przycisku. Gdybyśmy nie korzystali z biblioteki, do każdej funkcji odczytującej stan przycisku należałoby dodać opóźnienie sprawdzające czy po określonym czasie przycisk nadal jest naciśnięty. Dzięki tej bibliotece ułatwiamy sobie nie tylko prace, ale poprawiamy również przejrzystość naszego kodu, który będzie łatwiejszy do sprawdzenia w przypadku gdyby coś nie chodziło.

Jeśli chcielibyśmy się fizycznie pozbyć drgań styków, należałoby pomyśleć o zastosowaniu np. przycisków pojemnościowych, czy przekaźników półprzewodnikowych.

Czego się nauczyliśmy