[069] Arduino i klawiatura PC - cz. 2

[069] Arduino i klawiatura PC - cz. 2

W poprzednim artykule nauczyliśmy się przechwytywać kody z pecetowej klawiatury, a nawet generować większość znaków, które znajdują się na klawiszach. Biblioteka PS2Keyboard.h potrafi więcej: nadaje zmiennej wartości, którymi możemy manipulować.


#include <PS2Keyboard.h>  // Biblioteka obsługi klawiatury PS/2
const byte PS2data = 8;   // Adres linii DATA klawiatury.
const byte PS2clck = 3;   // Adres linii CLCK klawiatury, dla wersji UNO może przyjmować wartość 2 lub 3
PS2Keyboard klawiatura;   // Powołaj do życia obiekt o nazwie klawiatura.
char wcisnieto;           // Zmienna przechowująca znak wciśniętego klawisza.

void setup() {
  Serial.begin(9600);                  // Inicjuj przesyłanie danych portem szeregowym.
  klawiatura.begin(PS2data, PS2clck);  // Deklaruj wejścia, do których podłączona jest klawiatura.
}
void loop() {
  if (klawiatura.available()) {     // Jeśli klawisz został wciśnięty, to...
    wcisnieto = klawiatura.read();  // zapamiętaj jego kod w zmiennej wcisnieto.

    if (wcisnieto == PS2_TAB) {  // Wyłap znaki specjalne.
      Serial.print("[Tab]");
    } else if (wcisnieto == PS2_ENTER) {
      Serial.println();
    } else if (wcisnieto == PS2_BACKSPACE) {
      Serial.print("[Backspace]");
    } else if (wcisnieto == PS2_ESC) {
      Serial.print("[ESC]");
    } else if (wcisnieto == PS2_DELETE) {
      Serial.print("[Del]");
    } else if (wcisnieto == PS2_PAGEUP) {
      Serial.print("[PgUp]");
    } else if (wcisnieto == PS2_PAGEDOWN) {
      Serial.print("[PgDn]");
    } else if (wcisnieto == PS2_UPARROW) {
      Serial.print("[Up]");
    } else if (wcisnieto == PS2_LEFTARROW) {
      Serial.print("[Left]");
    } else if (wcisnieto == PS2_DOWNARROW) {
      Serial.print("[Down]");
    } else if (wcisnieto == PS2_RIGHTARROW) {
      Serial.print("[Right]");
    } else {
      Serial.print(wcisnieto);  // Wyślij kod klawisza, jeśli nie był to znak specjalny.
    }
  }
}

Jej twórca zaproponował, powiedzmy, zestaw startowy dla części klawiszy niealfanumerycznych. Nazwy są znormalizowane i zaczynają się od liter PS2, podkreślnika i nazwy konkretnego klawisza. Seria instrukcji warunkowych wydobywa kolejne zdarzenia i w naszym wypadku każe wysyłać do monitora opis tego klawisza zamknięty w klamry. Tam oczywiście może znajdować się dowolna instrukcja albo podprogram.

Zestaw startowy jest jednak ubogi, a poza tym mamy tu niekonsekwencję: Backspace i Delete dają tę samą reakcję. Czy da się coś z tym zrobić? Oczywiście. Tym razem musimy wejść w głębiej i odnaleźć plik o nazwie PS2Keyboard.h Ląduje on wraz z całą biblioteką pobraną na początku, zwykle w katalogu dokumentów Arduino. Wewnątrz znajdziemy mapę kodów klawiatury na symbole, których możemy potem używać we własnych programach. Te, przy których znajdują się zera, są niewykorzystane. Klawisze Backspace i Delete używają tego samego kodu równego 127. Proponuję uporządkować tę tablicę, dopisując własne kody. Kolejność w zasadzie może być dowolna, ale pewne standardy wypadałoby zachować. Enter od zawsze kojarzony był z kodem trzynastym, backspace – z ósmym, tabulator z dziewiątym. Co nie miało tradycji, może wejść gdzie bądź. Poniżej zamieszczam istotny fragment omawianego pliku.

#define PS2_PAGEUP			1   // start of heading
#define PS2_HOME			2   // start of text
#define PS2_END				3   // end of text
#define PS2_PAGEDOWN		4   // end of transmission
#define PS2_F1				5   // enquiry
#define PS2_F2				6   // acknowledge
#define PS2_F3				7   // bell
#define PS2_BACKSPACE		8   // backspace
#define PS2_TAB				9   // horizontal tab
#define PS2_F4				10  // line feed
#define PS2_F5				11  // vertical tab
#define PS2_F6				12  // form feed
#define PS2_ENTER			13  // carriage return
#define PS2_F7				14  // shift out
#define PS2_F8				15  // shift In
#define PS2_SCROLL			16  // data link escape
#define PS2_LEFTARROW		17  // device control 1
#define PS2_RIGHTARROW		18  // device control 2
#define PS2_UPARROW			19  // device control 3
#define PS2_DOWNARROW		20  // device control 4
#define PS2_F9				21  // negative acknowledge
#define PS2_F10				22  // synchronous idle
#define PS2_F11				23  // end of transmission block
#define PS2_F12				24  // cancel
#define PS2_INSERT			26  // substitute
#define PS2_ESC				27  // escape
#define PS2_DELETE			127 // delete

I w ten sposób zadeklarowałem prawie wszystko. Prawie, bo by nadać znaczenie kilku pozostałym klawiszom, trzeba by pogrzebać jeszcze głębiej, ale nie sądzę, by było nam to potrzebne.

#include <PS2Keyboard.h>  // Biblioteka obsługi klawiatury PS/2
const byte PS2data = 8;   // Adres linii DATA klawiatury.
const byte PS2clck = 3;   // Adres linii CLCK klawiatury, dla wersji UNO może przyjmować wartość 2 lub 3
PS2Keyboard klawiatura;   // Powołaj do życia obiekt o nazwie klawiatura.
char wcisnieto;           // Zmienna przechowująca znak wciśniętego klawisza.

void setup() {
  Serial.begin(9600);                  // Inicjuj przesyłanie danych portem szeregowym.
  klawiatura.begin(PS2data, PS2clck);  // Deklaruj wejścia, do których podłączona jest klawiatura.
}
void loop() {
  if (klawiatura.available()) {     // Jeśli klawisz został wciśnięty, to...
    wcisnieto = klawiatura.read();  // zapamiętaj jego kod w zmiennej wcisnieto.

    if (wcisnieto == PS2_PAGEUP) {  // Wyłap znaki specjalne.
      Serial.print("[PgUp]");
    } else if (wcisnieto == PS2_HOME) {
      Serial.print("[Home]");
    } else if (wcisnieto == PS2_END) {
      Serial.print("[End]");
    } else if (wcisnieto == PS2_PAGEDOWN) {
      Serial.print("[PgDn]");
    } else if (wcisnieto == PS2_BACKSPACE) {
      Serial.print("[Backspace]");
    } else if (wcisnieto == PS2_TAB) {
      Serial.print("[Tab]");
    } else if (wcisnieto == PS2_ENTER) {
      Serial.println();
    } else if (wcisnieto == PS2_SCROLL) {
      Serial.print("[ScrLk]");
    } else if (wcisnieto == PS2_LEFTARROW) {
      Serial.print("[Left]");
    } else if (wcisnieto == PS2_RIGHTARROW) {
      Serial.print("[Right]");
    } else if (wcisnieto == PS2_UPARROW) {
      Serial.print("[Up]");
    } else if (wcisnieto == PS2_DOWNARROW) {
      Serial.print("[Down]");
    } else if (wcisnieto == PS2_INSERT) {
      Serial.print("[Ins]");
    } else if (wcisnieto == PS2_ESC) {
      Serial.print("[ESC]");
    } else if (wcisnieto == PS2_DELETE) {
      Serial.print("[Del]");
    } else if (wcisnieto == PS2_F1) {
      Serial.print("[F1]");
    } else if (wcisnieto == PS2_F2) {
      Serial.print("[F2]");
    } else if (wcisnieto == PS2_F3) {
      Serial.print("[F3]");
    } else if (wcisnieto == PS2_F4) {
      Serial.print("[F4]");
    } else if (wcisnieto == PS2_F5) {
      Serial.print("[F5]");
    } else if (wcisnieto == PS2_F6) {
      Serial.print("[F6]");
    } else if (wcisnieto == PS2_F7) {
      Serial.print("[F7]");
    } else if (wcisnieto == PS2_F8) {
      Serial.print("[F8]");
    } else if (wcisnieto == PS2_F9) {
      Serial.print("[F9]");
    } else if (wcisnieto == PS2_F10) {
      Serial.print("[F10]");
    } else if (wcisnieto == PS2_F11) {
      Serial.print("[F11]");
    } else if (wcisnieto == PS2_F12) {
      Serial.print("[F12]");
    } else {
      Serial.print(wcisnieto);  // Wyślij kod klawisza, jeśli nie był to znak specjalny.
    }
  }
}

Tak obecnie wygląda szkic, który obsługuje już wszystkie klawisze funkcyjne i nawigacyjne. Ale na koniec zostawiłem sobie coś jeszcze. Standardy przemysłowe narzucają często wysoką niezawodność urządzeniom, ale też ich obsłudze. Dlatego czasem działanie klawiatur powinno być potwierdzane sygnałami świetlnymi albo dźwiękowymi. Ten drugi sposób był zresztą stosowany we wczesnych czasach komputerowych, gdy klawiatury stanowiły kiepskiej jakości konstrukcje i potwierdzenie klikania realizowano właśnie tak.

Płytka edukacyjna TME zawiera zdublowaną diodę, tę która na płytach Arduino podłączona jest do portu 13 oraz generator pisków, dołączony do portu drugiego. Piszczek taki albo buzzer, to urządzenie, które wydaje dźwięki po dołączeniu doń napięcia. Nie potrzeba tutaj żadnych przebiegów, generator znajduje się wewnątrz. Jest to zaleta i wada zarazem: nie trzeba myśleć o tworzeniu napięć zmiennych, ale też nie da się nijak wpłynąć na głośność ani wysokość tonu. Piszczek może tylko piszczeć albo nie. Ogromnym grzechem jest domyślne ustawienie tych pisków na zbyt długi czas. Znane wszystkim sklepowe kasy swoim bekaniem doprowadzają do szaleństwa klientów i obsługę. A wystarczyłoby ten czas skrócić do wartości nadal słyszalnej, ale nie denerwującej tak bardzo.

#include <PS2Keyboard.h>  // Biblioteka obsługi klawiatury PS/2
const byte PS2data = 8;   // Adres linii DATA klawiatury.
const byte PS2clck = 3;   // Adres linii CLCK klawiatury, dla wersji UNO może przyjmować wartość 2 lub 3
PS2Keyboard klawiatura;   // Powołaj do życia obiekt o nazwie klawiatura.
char wcisnieto;           // Zmienna przechowująca znak wciśniętego klawisza.
const byte led = 13;      // Adres diody świecącej
const byte piszczek = 2;  // Adres piszczka.

void setup() {
  Serial.begin(9600);                  // Inicjuj przesyłanie danych portem szeregowym.
  klawiatura.begin(PS2data, PS2clck);  // Deklaruj wejścia, do których podłączona jest klawiatura.
  pinMode(led, OUTPUT);                // Zadeklaruj adres diody świecącej jako wyjście.
  pinMode(piszczek, OUTPUT);           // Zadeklaruj adres piszczka jako wyjście.
}
void loop() {
  if (klawiatura.available()) {     // Jśli klawisz został wciśnięty, to...
    wcisnieto = klawiatura.read();  // zapamiętaj jego kod w zmiennej wcisnieto.

    if (wcisnieto == PS2_PAGEUP) {  // Wyłap znaki specjalne.
      Serial.print("[PgUp]");
    } else if (wcisnieto == PS2_HOME) {
      Serial.print("[Home]");
    } else if (wcisnieto == PS2_END) {
      Serial.print("[End]");
    } else if (wcisnieto == PS2_PAGEDOWN) {
      Serial.print("[PgDn]");
    } else if (wcisnieto == PS2_BACKSPACE) {
      Serial.print("[Backspace]");
    } else if (wcisnieto == PS2_TAB) {
      Serial.print("[Tab]");
    } else if (wcisnieto == PS2_ENTER) {
      Serial.println();
    } else if (wcisnieto == PS2_SCROLL) {
      Serial.print("[ScrLk]");
    } else if (wcisnieto == PS2_LEFTARROW) {
      Serial.print("[Left]");
    } else if (wcisnieto == PS2_RIGHTARROW) {
      Serial.print("[Right]");
    } else if (wcisnieto == PS2_UPARROW) {
      Serial.print("[Up]");
    } else if (wcisnieto == PS2_DOWNARROW) {
      Serial.print("[Down]");
    } else if (wcisnieto == PS2_INSERT) {
      Serial.print("[Ins]");
    } else if (wcisnieto == PS2_ESC) {
      Serial.print("[ESC]");
    } else if (wcisnieto == PS2_DELETE) {
      Serial.print("[Del]");
    } else if (wcisnieto == PS2_F1) {
      Serial.print("[F1]");
    } else if (wcisnieto == PS2_F2) {
      Serial.print("[F2]");
    } else if (wcisnieto == PS2_F3) {
      Serial.print("[F3]");
    } else if (wcisnieto == PS2_F4) {
      Serial.print("[F4]");
    } else if (wcisnieto == PS2_F5) {
      Serial.print("[F5]");
    } else if (wcisnieto == PS2_F6) {
      Serial.print("[F6]");
    } else if (wcisnieto == PS2_F7) {
      Serial.print("[F7]");
    } else if (wcisnieto == PS2_F8) {
      Serial.print("[F8]");
    } else if (wcisnieto == PS2_F9) {
      Serial.print("[F9]");
    } else if (wcisnieto == PS2_F10) {
      Serial.print("[F10]");
    } else if (wcisnieto == PS2_F11) {
      Serial.print("[F11]");
    } else if (wcisnieto == PS2_F12) {
      Serial.print("[F12]");
    } else {
      Serial.print(wcisnieto);  // Wyślij kod klawisza, jeśli nie był to znak specjalny.
    }
    digitalWrite(led, HIGH);       // Włącz diodę świecącą.
    digitalWrite(piszczek, HIGH);  // Włącz piszczek.
    delay(50);                     // Zaczekaj milisekundę.
    digitalWrite(piszczek, LOW);   // Wyłącz piszczek.
    delay(1);                      // Zaczekaj 20 milisekund.
    digitalWrite(led, LOW);        // Wyłącz diodę świecącą.
  }
}

W deklaracjach dodajemy adresy diody led i buzzera piszczek, a w definicjach oba porty ustawiamy jako wyjściowe. Cała obsługa znajdzie się na samym końcu, już po wysłaniu znaku do monitora. Najpierw włączamy diodę świecącą, a następnie piszczek. Po upływie dosłownie jednej tylko milisekundy wyłączamy piszczek. Ale dioda wyłączona po tak krótkim czasie ledwo tylko mrugnie. Dlatego wyłączenie jej opóźniamy o kolejne 20 milisekund i teraz wszystko działa jak powinno.

Po tej dawce wiedzy pecetowa klawiatura nie będzie mieć już przed nami tajemnic i posłuży w projektach, które wymagają wygodnego interfejsu wprowadzania danych.

Powiązane tematy

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2Sprawdź tutaj

Przeczytaj również

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2024 arduino.pl