[168] Klawiatura matrycowa dla zaawansowanych cz. 2

Kontynuując temat klawiatury matrycowej przyjrzyjmy się zmiennym konfigurującym zachowanie się biblioteki keypad.h I tak, jeśli użylibyśmy jakiejś mało precyzyjnej klawiatury, dającej wielokrotne odczyty na skutek niepewnych styków, możemy wydłużyć blokadę na stany przejściowe. Używa się do tego poniższej instrukcji.

keypad.setDebounceTime(50);  // Wydłuż czas stabilizacji klawiatury z 10 do 50 ms

Domyślnie blokuje pracę klawiszy na 10 ms, można ją wydłużyć dla zadowalającej wartości. Kolejne ustawienie określa jak długo klawisz ma być trzymany, by biblioteka zarejestrowała go jako przytrzymany właśnie, nadając mu status HOLD

keypad.setHoldTime(500);     // Skróć czas przejścia klawisza w tryb HOLD z 1000 do 500 ms

To przydaje się na przykład do stworzenia alternatywnej odpowiedzi klawiatury przytrzymanej dłużej. Domyślną wartością jest tysiąc milisekund. No to teraz przetestujmy sobie owe statusy, bo jest ich więcej. Biblioteka generuje cztery: klawisz wciśnięto, czyli PRESSED, klawisz wciśnięto i przytrzymano dłużej – przez czas, o którym właśnie pisałem, czyli HOLD, klawisz właśnie puszczono – RELEASED i niczego nie wciśnięto – IDLE. Poniższy program zobrazuje nam to najlepiej.

#include <Wire.h>                                                      // Biblioteka obsługująca magistralę I2C
#include <hd44780.h>                                                   // Biblioteka obsługująca wyświetlacze 44780
#include <hd44780ioClass/hd44780_I2Cexp.h>                             // Dodatek obsługujący wyświetlacze podłączone do ekspandera I2C
hd44780_I2Cexp lcd(0x20, I2Cexp_MCP23008, 7, 6, 5, 4, 3, 2, 1, HIGH);  // Konfiguracja połączeń wyświetlacza LCD

#include <Keypad.h>                         // Biblioteka obsługująca klawiaturę.
const byte rowsKeypad = 4;                  // Liczba wierszy matrycy klawiatury.
const byte colsKeypad = 4;                  // Liczba kolumn matrycy klawiatury.
byte rowPins[rowsKeypad] = { 7, 6, 5, 4 };  // Adresy kolejnych wierszy klawiatury.
byte colPins[colsKeypad] = { 3, 2, 1, 0 };  // Adresy kolejnych kolumn klawiatury.
char keys[rowsKeypad][colsKeypad] = {       // Tablica znaków dla kolejnych klawiszy.
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rowsKeypad, colsKeypad);  // Definiuj klawiaturę zgodnie z powyższymi deklaracjami.
char lastKey;                                                                        // Zmienna przechowująca ostatni sczytany znak z klawiatury.

void setup() {
  lcd.begin(16, 2);            // Inicjuj wyświetlacz.
  keypad.setDebounceTime(50);  // Wydłuż czas stabilizacji klawiatury z 10 do 50 ms
  keypad.setHoldTime(1000);    // Ustaw czas przejścia klawisza w tryb HOLD na 1000 ms
}

void loop() {
  char key = keypad.getKey();            // Pozyskaj znak klawiatury.
  if (key != NO_KEY) { lastKey = key; }  // Jeśli coś wciśnięto, zapisz to w zmiennej.
  lcd.clear();                           // Wyczyść wyświetlacz.
  switch (keypad.getState()) {           // Analizuj stan wciśniętego klawisza.
    case PRESSED:                        // Klawisz wciśnięto.
      lcd.print("PRESSED: ");
      lcd.print(lastKey);
      break;
    case HOLD:  // Klawisz przytrzymano.
      lcd.print("HOLD: ");
      lcd.print(lastKey);
      break;
    case RELEASED:  // Klawisz puszczono.
      lcd.print("RELEASED: ");
      lcd.print(lastKey);
      break;
    case IDLE:
      lcd.print("IDLE");  // Niczego nie wciśnięto.
      break;
    default:
      lcd.print("UNKNOWN");  // Brak zwróconej informacji o klawiaturze.
      lcd.print(lastKey);
      break;
  }
  delay(250);
}

Początek nie różni się niczym. Jednak po pozyskaniu kodu klawisza, wstawiamy go do zmiennej lastKey. Czyścimy wyświetlacz i teraz czytamy ów status z tej zmiennej systemowej biblioteki. Zaraz po wciśnięciu pojawi nam się napis PRESSED (klawisz został wciśnięty). Jeśli będziemy ten klawisz trzymać dłużej, status zmieni się na HOLD (klawisz jest już wciśnięty od co najmniej sekundy). Po puszczeniu klawisza na chwilę pojawi się RELEASED (właśnie puszczono klawisz), a po chwili – IDLE (klawisze są zwolnione), do czasu kolejnego wciśnięcia dowolnego klawisza. Gdyby coś poszło nie tak, ujrzymy jeszcze jeden komunikat UNKNOWN, pojawiający się na skutek braku statusu. Teraz możemy się pobawić, wciskając, trzymając i puszczając przyciski. Każde ze zdarzeń daje status, co można wykorzystać we własnych pomysłach na realizację klawiatury. W jakich na przykład? Proszę bardzo, oto kolejna propozycja. Napiszemy program, który będzie podwajał liczbę znaków osiąganych z klawiaturki i to bez jakiegokolwiek shiftu czy temu podobnego klawisza.

#include <Wire.h>                                                      // Biblioteka obsługująca magistralę I2C
#include <hd44780.h>                                                   // Biblioteka obsługująca wyświetlacze 44780
#include <hd44780ioClass/hd44780_I2Cexp.h>                             // Dodatek obsługujący wyświetlacze podłączone do ekspandera I2C
hd44780_I2Cexp lcd(0x20, I2Cexp_MCP23008, 7, 6, 5, 4, 3, 2, 1, HIGH);  // Konfiguracja połączeń wyświetlacza LCD

#include <Keypad.h>                         // Biblioteka obsługująca klawiaturę.
char lastKey;                               // Zmienna przechowująca ostatni sczytany znak z klawiatury.
const byte rowsKeypad = 4;                  // Liczba wierszy matrycy klawiatury.
const byte colsKeypad = 4;                  // Liczba kolumn matrycy klawiatury.
byte rowPins[rowsKeypad] = { 7, 6, 5, 4 };  // Adresy kolejnych wierszy klawiatury.
byte colPins[colsKeypad] = { 3, 2, 1, 0 };  // Adresy kolejnych kolumn klawiatury.

const char keys[rowsKeypad][colsKeypad] = {  // Tablica znaków dla kolejnych klawiszy.
  { '1', '2', '3', 'a' },
  { '4', '5', '6', 'b' },
  { '7', '8', '9', 'c' },
  { '*', '0', '#', 'd' }
};
const char keysHold[rowsKeypad][colsKeypad] = {  // Tablica znaków dla dla kolejnych klawiszy trzymanych dłużej.
  { '!', '@', '#', 'A' },
  { '$', '%', '^', 'B' },
  { '&', '*', '(', 'C' },
  { '+', ')', '-', 'D' }
};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rowsKeypad, colsKeypad);  // Definiuj klawiaturę zgodnie z powyższymi deklaracjami.
bool statusHold = false;                                                             // Zmienna statusu trzymania klawisza.
bool lockRelease = false;                                                            // Zmienna zabezpieczająca przed autorepetycją.
byte activeRow = 0;                                                                  // Współrzędne wciśniętego klawisza.
byte activeCol = 0;

void setup() {
  lcd.begin(16, 2);  // Inicjuj wyświetlacz.
}
void loop() {
  char key = keypad.getKey();          // Pozyskaj znak klawiatury.
  KeyState state = keypad.getState();  // Pozyskaj status klawiatury.

  if (key != NO_KEY) {    // Jeśli coś wciśnięto...
    lastKey = key;        // Zapisz to w zmiennej.
    statusHold = false;   // Zeruj stan trzymania klawisza.
    lockRelease = false;  // Odblokuj możliwość wypisania znaku dla nowego wciśnięcia.

    for (byte r = 0; r < rowsKeypad; r++) {  // Pozyskaj współrzędne wciśniętego klawisza.
      for (byte c = 0; c < colsKeypad; c++) {
        if (keys[r][c] == key) {
          activeRow = r;
          activeCol = c;
        }
      }
    }
  }
  if (state == HOLD) {  // Jeśli klawisz otrzymał status HOLD...
    statusHold = true;  // Ustaw status trzymania klawisza.
  }
  if (state == RELEASED && lastKey != NO_KEY && !lockRelease) {  // Jeśli puszczono klawisz i wcześniej zapisano jakiś znak...
    char activeKey;                                              // Utwórz zmienną kodu klawisza.
    if (statusHold) {                                            // Jeśli ten klawisz był trzymany dłużej...
      activeKey = keysHold[activeRow][activeCol];                // Wybierz kod znaku z alternatywnej tablicy.
    } else {                                                     // W przeciwnym razie...
      activeKey = keys[activeRow][activeCol];                    // Wybierz kod znaku z głównej tablicy.
    }
    lcd.print(activeKey);  // Wyślij kod znaku na LCD.
    lastKey = NO_KEY;      // Zeruj zmienną kodu znaku.
    statusHold = false;    // Zeruj zmienną trzymania klawisza.
    lockRelease = true;    // Zablokuj kolejne powtórzenia znaku aż do nowego kliknięcia.
  }
  if (state == IDLE) {    // Jeśli żaden klawisz nie jest wciśnięty...
    lockRelease = false;  // Zeruj blokadę autorepetycji.
  }
}

Podstawowe znaki będą wychodzić zwyczajnie – po przyciskaniu klawiszy, alternatywne natomiast pojawią się po chwilowym ich przytrzymaniu. Ponieważ jest to wersja szkoleniowa, uprościłem nieco program i znaki będą się pojawiać dopiero po puszczeniu przycisków – z tym sobie poradzimy później. Niestety biblioteka nie oferuje prostej funkcji podmiany tablicy i będziemy musieli to czynić nieco naokoło. Zadeklarujemy dwie tablice: keys oraz keysHold, w drugiej będą znaki alternatywne. W przypadku liter – małe i duże, dla cyfr – znaki, które znajdziemy na klawiaturze peceta. Można oczywiście wpisać tam cokolwiek. Będą nam też potrzebne różne zmienne pomocnicze, zaraz się wyjaśni która do czego.

Na początku pozyskujemy znak klawiatury key i status klawiatury state. Jeśli coś w ogóle wciśnięto, zapamiętamy to w lastKey i wyzerujemy zmienną trzymania przycisku statusHold, równocześnie odblokowując możliwość wysłania znaku później lockRelease. Następnie musimy przepytać całą klawiaturę, by zidentyfikować który to klawisz wciśnięto.

Kolejno analizujemy czy klawisz jest już trzymany wymagany czas, by mógł zostać określony jako dociśnięty. Dopiero teraz przeanalizujemy czy klawisz został puszczony, czy nie był nieokreślony i czy zezwolono na dalsze procedury, czyli zabezpieczono się przed autopowielaniem. Po spełnieniu tych wszystkich warunków powołujemy do życia zmienną kodu klawisza activeKey, którą potem wyślemy na ekran.

I teraz w zależności od tego czy klawisz był trzymany długo czy krótko, pobierzemy jego kod z tablicy – alternatywnej albo głównej. Kod wyjdzie na ekran, wyzerujemy stosowne zmienne i zablokujemy autorepetycję, bo bez tego kody wylatywałyby bez końca, aż klawisz zostałby zwolniony. Tę możemy odblokować dopiero po rzeczywistym puszczeniu klawisza. Program, czy raczej moduł, działa zgodnie z ideą. W tym wypadku jednak dobrze byłoby mieć jakieś powiadomienie o działaniu klawiaturki.

#include <Wire.h>                                                      // Biblioteka obsługująca magistralę I2C
#include <hd44780.h>                                                   // Biblioteka obsługująca wyświetlacze 44780
#include <hd44780ioClass/hd44780_I2Cexp.h>                             // Dodatek obsługujący wyświetlacze podłączone do ekspandera I2C
hd44780_I2Cexp lcd(0x20, I2Cexp_MCP23008, 7, 6, 5, 4, 3, 2, 1, HIGH);  // Konfiguracja połączeń wyświetlacza LCD

#include <Keypad.h>                         // Biblioteka obsługująca klawiaturę.
char lastKey;                               // Zmienna przechowująca ostatni sczytany znak z klawiatury.
const byte rowsKeypad = 4;                  // Liczba wierszy matrycy klawiatury.
const byte colsKeypad = 4;                  // Liczba kolumn matrycy klawiatury.
byte rowPins[rowsKeypad] = { 7, 6, 5, 4 };  // Adresy kolejnych wierszy klawiatury.
byte colPins[colsKeypad] = { 3, 2, 1, 0 };  // Adresy kolejnych kolumn klawiatury.
const byte buzzer = 9;                      // Port sterujący buzzerem.

const char keys[rowsKeypad][colsKeypad] = {  // Tablica znaków dla kolejnych klawiszy.
  { '1', '2', '3', 'a' },
  { '4', '5', '6', 'b' },
  { '7', '8', '9', 'c' },
  { '*', '0', '#', 'd' }
};
const char keysHold[rowsKeypad][colsKeypad] = {  // Tablica znaków dla dla kolejnych klawiszy trzymanych dłużej.
  { '!', '@', '#', 'A' },
  { '$', '%', '^', 'B' },
  { '&', '*', '(', 'C' },
  { '+', ')', '-', 'D' }
};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, rowsKeypad, colsKeypad);  // Definiuj klawiaturę zgodnie z powyższymi deklaracjami.
bool statusHold = false;                                                             // Zmienna statusu trzymania klawisza.
bool lockRelease = false;                                                            // Zmienna zabezpieczająca przed autorepetycją.
byte activeRow = 0;                                                                  // Współrzędne wciśniętego klawisza.
byte activeCol = 0;

void setup() {
  lcd.begin(16, 2);         // Inicjuj wyświetlacz.
  pinMode(buzzer, OUTPUT);  // Inicjuj port sterujący buzzerem.
}
void loop() {
  char key = keypad.getKey();          // Pozyskaj znak klawiatury.
  KeyState state = keypad.getState();  // Pozyskaj status klawiatury.

  if (key != NO_KEY) {                       // Jeśli coś wciśnięto...
    lastKey = key;                           // Zapisz to w zmiennej.
    statusHold = false;                      // Zeruj stan trzymania klawisza.
    lockRelease = false;                     // Odblokuj możliwość wypisania znaku dla nowego wciśnięcia.
    for (byte r = 0; r < rowsKeypad; r++) {  // Pozyskaj współrzędne wciśniętego klawisza.
      for (byte c = 0; c < colsKeypad; c++) {
        if (keys[r][c] == key) {
          activeRow = r;
          activeCol = c;
        }
      }
    }
    beep();  // Piknij.
  }
  if (state == HOLD) {  // Jeśli klawisz otrzymał status HOLD...
    statusHold = true;  // Ustaw status trzymania klawisza.
    beep();             // Piknij.
  }
  if (state == RELEASED && lastKey != NO_KEY && !lockRelease) {  // Jeśli puszczono klawisz i wcześniej zapisano jakiś znak...
    char activeKey;                                              // Utwórz zmienną kodu klawisza.
    if (statusHold) {                                            // Jeśli ten klawisz był trzymany dłużej...
      activeKey = keysHold[activeRow][activeCol];                // Wybierz kod znaku z alternatywnej tablicy.
    } else {                                                     // W przeciwnym razie...
      activeKey = keys[activeRow][activeCol];                    // Wybierz kod znaku z głównej tablicy.
    }
    lcd.print(activeKey);  // Wyślij kod znaku na LCD.
    lastKey = NO_KEY;      // Zeruj zmienną kodu znaku.
    statusHold = false;    // Zeruj zmienną trzymania klawisza.
    lockRelease = true;    // Zablokuj kolejne powtórzenia znaku aż do nowego kliknięcia.
  }
  if (state == IDLE) {    // Jeśli żaden klawisz nie jest wciśnięty...
    lockRelease = false;  // Zeruj blokadę autorepetycji.
  }
}
void beep() {
  digitalWrite(buzzer, HIGH);
  delay(1);
  digitalWrite(buzzer, LOW);
}

Wprowadziłem prostą funkcję piknięcia, wykorzystującą osadzony na mojej płytce piszczek. Piknięcia pojawią się po każdym wciśnięciu przycisku – raz i po przekroczeniu czasu trzymania klawisza. W tym wypadku będzie to już pikanie ciągłe. Można to zmienić, wstawiając kolejną pułapkę na jednorazowe piknięcie, ale to już pozostawię czytelnikom. W kolejnym artykule wrócimy do wspomnianego na początku zamka szyfrowego.

Płytka edukacyjna TME-EDU-ARD-2Płytka edukacyjna TME-EDU-ARD-2

Inne artykuły z tej kategorii

Nasi partnerzy

TMETech Master EventTME EducationPoweredby
Copyright © 2026 arduino.pl