Skocz do zawartości
jare72

Problem z klawiaturą membranową

Pomocna odpowiedź

Napisano (edytowany)

Hej, poniżej kod z którym mam problem. W monitorze portu szeregowego wpisuje mi cały czas pierwszy przycisk, chociaż nic nie naciskam i nie wiem czemu tak się dzieje. Wiem, że można skorzystać z biblioteki keypad, ale muszę sam ogarnąć program, bo w trakcie monitorowania naciśnięcia przycisku ma wykonywać się multipleksowanie diod więc muszę to ze sobą połączyć, żeby miganie diod nie było zauważalne przez ludzie oko. Ewentualnie jeśli macie jakiś pomysł jak połączyć miganie diodami z monitorowaniem klawiatury w tym samym czasie to chętnie wysłucham.

#include <Wire.h>
#include <Adafruit_MCP23017.h>
Adafruit_MCP23017 ekspander;
const int ROWS[] = {13,12,11,10};
const int COLS[] = {0,1,2,3};
const int NUM_ROWS = sizeof(ROWS)/sizeof(int);
const int NUM_COLS = sizeof(COLS)/sizeof(int);

const char KEYS[NUM_ROWS][NUM_COLS] = {
  {'1','2','3','4'},
  {'5','6','7','8'},
  {'9','A','B','C'},
  {'D','E','F','G'}
};

void setup() {
  Serial.begin(9600);
  ekspander.begin(0x01);
  
  for(int x = 0; x < NUM_ROWS; x++) {
    ekspander.pinMode(ROWS[x], OUTPUT);
    ekspander.digitalWrite(ROWS[x], LOW);
  }

  for(int x = 0; x < NUM_COLS; x++) {
    ekspander.pinMode(COLS[x], INPUT_PULLUP);
  }
}

void loop() {
  char key = readKey();
  if(key) Serial.println(key);
}

char readKey() {
  for(int x = 0; x < NUM_ROWS; x++) {
    ekspander.digitalWrite(ROWS[x], HIGH);
    for(int y = 0; y < NUM_COLS; y++) {
      if(ekspander.digitalRead(COLS[y]) == HIGH) return KEYS[x][y];
    }
    ekspander.digitalWrite(ROWS[x], LOW);
  }
  return 0;
}

 

Edytowano przez Gieneq
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

@jare72 witam na forum! 

Po pierwsze miło mnie zaskoczyłeś, bo kod jest fajnie napisany 😄 Tak przejrzałem i myślę sobie "Pewnie na ifach sprawdzanie" a tu niespodzianka 🙂

Wydaje mi się, że problem leży tu:

if(ekspander.digitalRead(COLS[y]) == HIGH) return KEYS[x][y];

Masz pullup więc sprawdzasz czy stan jest NISKI, bo domyślnie wszystkie mają wysoki. Czyli bierze pierwszy jaki spełnia warunek i nie sprawdza dalszych. Czyli zgaduję że jak wciszniesz 1 to pojawi się w konsoli 2, jako kolejny który spełni warunek i wyrzuci return.

Przy okazji zachęcam do używania bloku kodu w edytorze tekstu 😉 

Edytowano przez Gieneq

Udostępnij ten post


Link to post
Share on other sites

Poprawiłem na sprawdzanie stanu niskiego i nadal wypisuje mi pierwszy przycisk bez naciskania żadnego przycisku, sprawdzałem wszystkie połączenia i jest okej. Testowałem na bibliotece keypad i wszystko działa więc nie wiem w czym może być problem 🤔

Korzystałem wczoraj też z tego programu https://starter-kit.nettigo.pl/2017/03/obsluga-klawiatury-matrycowej-arduino/ i wszystko działało tak jak trzeba, ale jak dzisiaj podłączyłem do zasilania to wypisuje mi te nieszczęsne "1" 😦

Udostępnij ten post


Link to post
Share on other sites

A mógłbyś zrobić taki debug. weź raz wywołaj funkcję, ale dodaj w jej wnętrzu trochę printów:

char readKey() {
  for(int x = 0; x < NUM_ROWS; x++) {
    ekspander.digitalWrite(ROWS[x], HIGH);
    for(int y = 0; y < NUM_COLS; y++) {
      //if(ekspander.digitalRead(COLS[y]) == HIGH) return KEYS[x][y];
      Serial.println(ekspander.digitalRead(COLS[y]));
    }
    ekspander.digitalWrite(ROWS[x], LOW);
  }
  return 0;
}

Tu jest kod dostępny w ramach artykułu, który kiedyś pisałem o tej samej tematyce, nie wiem wygląda podobnie.


// główna pętla w której sprawdzane są kliknięcia
void loop() {
  for(int iOutput = 0; iOutput < MATRIX_SIZE; ++iOutput){
    // aktywuj sprawdzanie wiersza o indeksie iOutput
    digitalWrite(OUTPUTS[iOutput], LOW);
    for(int iInput = 0; iInput < MATRIX_SIZE; ++iInput){
      // oblicz numer indeksu w macierzy wszystkich przycisków
      index = iOutput * MATRIX_SIZE + iInput;
      
      // jeżeli obecny stan jest LOW (przycisk wciśnięty)
      if((digitalRead(INPUTS[iInput]) == LOW)){
        if(states[index] == KEY_RELESED){
          // to wywołaj kliknięcie
          onPress(index);
        }
        // ustaw flagę żeby zadziałał zatrzask
        states[index] = KEY_PRESSED;
      } else if(states[index] == KEY_PRESSED)
        // zresetuj zatrzask w momencie puszczenia przycisku
        states[index] = KEY_RELESED;
    }
    // opuść linię aby przejść do następnej
    digitalWrite(OUTPUTS[iOutput], HIGH);
  }
}

 

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Nie wiem w ogóle jaki jest sens podłączania klawitury matrycowej przez ekspander. Przecież procesor nie będzie robił nic innego jak tylko w kółko młócił I2C.

@jare72Naprawdę nie odróżniasz pinów procesora i sposobów dostępu do nich od wyniesionych gdzieś przez wolną magistralę szeregową linii ekspandera? Dostajesz nowe piny na których możesz powiesić jakieś lampki, przekaźniki lub zwykłe przyciski, ale na litość, nie podłączaj tam klawiatury wymagającej ciągłego przeglądania 😭To nie do tego służy i nawet jak to opanujesz to albo zeżre całą moc procesora albo będzie przyczyną innych kłopotów. Równoczesne multipleksowanie diod czy wyświetlacza i klawiatury podłączonej do lokalnych pinów procesora robisz trywialnie.

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites

@marek1707 Klawiatura (14 pinów) + multipleksowanie diod (10 pinów) + wyświetlacz LCD, a ja posiadam Arduino Uno więc dlatego podłączam do ekspandera. Musiałbym zakupić Arduino Due żeby wystarczyło pinów procesora.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Ile masz tych diod?

Klawiatura matrycowa - 8 pinów

10 diod na charlieplexingu - 4 piny

Razem 12, a masz do dyspozycji 16 (D2..D13, A0..A3) nie licząc Seriala i I2C do wyświetlacza...

 

 

Edytowano przez ethanak
  • Lubię! 1
  • Nie zgadzam się! 1

Udostępnij ten post


Link to post
Share on other sites

W czasach gdy mikrokontrolery były w obudowach DIP40 z czego prawie połowa była zmarnowana na jakieś zasilania, zegary itp potrzeby "organizacyjne" robiłem to tak, że do skanowania zarówno klawiatury jak i pola LED (cokolwiek to znaczy) brałem te same linie. Wtedy do powiedzmy kolumn Twojej klawiatury i jednocześnie do sterowania elektrod wspólnych LED masz 4 linie, 4 kolejne na wejście zwrotne (wiersze) klawiatury i kilka (ile potrzeba) na sterowanie segmentów LED. Jeśli na to ostatnie przeznaczysz także 4 linie to w sumie zużywasz 12 linii (np. jeden cały port na wyjście i pół na wejście) i zadanie wykonane: klawiatura 4x4 i max 16 niezleżnych punktów świecących. A program do tego jest naprawdę prosty. Funkcja odpalana z przerwania od któregoś timera robi to sama a program główny z jednej strony wpisuje coś do pola "ekran" i to się wyświetla a z drugiej czyta zmienną "kod_przycisku" i dostaje znaki jak z klawiatury PC. Posiłkując się dodatkowym scalakiem (ale nie ekspanderem przez I2C tylko zwykłym tanim dekoderem TTL za 1.50zł) można liczbę linii jeszcze ograniczyć nie tracąc nic na idei bezpośredniego dostępu do lokalnych portów. 

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Przepraszam, że się wtrącam - ale moim zdaniem używanie ekspandera to nic złego. Oczywiście ma swoje wady: po pierwsze jest drogie, po drugie powolne. Wydaje mi się jednak, że w przypadku konstrukcji amatorskiej, to nie przekreśla stosowania ekspandera.

Pierwszy argument, czyli cena nie ma znaczenia jeśli budujemy jeden egzemplarz. Nawet jeśli jeden układ kosztuje 1zł, a drugi 5, to i tak koszty przesyłki będą wyższe. A gdyby nadal była różnica, to czas spędzony przy zabawie tymi układami ma i tak większą wartość niż wszystkie elementy razem wzięte (doliczając złocenie wyprowadzeń).

Natomiast jak chodzi o czasy działania, to wszystko zależy jak często potrzebujemy wykonywać skanowanie klawiatury. Tak teoretycznie licząc: zapis do ekspandera to 3-4 bajty (adres urządzenia, numer rejestru, 1-2 danych). Niektóre wymagają mniej, ale liczmy dla 4 bajtów. Dla każdej kolumny musimy wykonać zapis - załóżmy że klawiatura ma 4 kolmny - mnożąc przez 4 mamy 16 bajtów. Po każdym zapisie dajemy odczyt wartości  - i i wychodzi wszystkiego razem 32 bajty, czyli 256bitów. Dojdą jeszcze bity startów, stopów, potwierdzeń - żeby w pamięci łatwiej liczyć, niech będzie 500 bitów.

I2C powinno poradzić sobie co najmniej z 100kHz, więc skanowajmie zajmie jakieś 5ms. To z jednej strony wieki - ale jeśli takie skanowanie wykonamy co 100ms to będziemy mieli działającą klawiaturę oraz zużycie CPU na poziomie 5%.

Tak jak napisałem wcześniej - w pełni się zgodzę, że inne rozwiązania są szybsze, tańsze, lepiej dopasowane do produkcji seryjnej. Ale moim zdaniem w tym konkretnym przypadku można spokojnie dodać ekspander, albo dwa i mieć prosty projekt, który pozwoli jego autorowi uczyć się dalej programowania.

Edit: w pierwszej wersji pomyliłem się o rząd wielkości, wyszło mi 0.5%, poprawiłem na 5%.

Edytowano przez Elvis
  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
10 godzin temu, marek1707 napisał:

Nie wiem w ogóle jaki jest sens podłączania klawitury matrycowej przez ekspander. Przecież procesor nie będzie robił nic innego jak tylko w kółko młócił I2C. (...) To nie do tego służy i nawet jak to opanujesz to albo zeżre całą moc procesora

Można obsłużyć I2C na przerwaniach. W zasadzie arduinowe libs obsługują I2C na przerwaniach. Co prawda wole sam napisać taka obsługę ale w przypadku arduino nie ma "tylnej furtki", nie da się przejąc wektora przerwań (widać autorzy Arduino nie brali dobrych wzorców np z C-64) ale można w prostszy l;ub bardziej skomplikowany sposób wykorzystać przerwania I2C i program nie bedzię "mielił".

Nie zmienia to faktu, ze zgadam się z tym, iz najprościej klawiaturę matrycowa obsłużyć na portach uC i tak powinni robić początkujący.

 

9 godzin temu, jare72 napisał:

ja posiadam Arduino Uno więc dlatego podłączam do ekspandera

Wykorzystaj Mega2560.

 

41 minut temu, marek1707 napisał:

obiłem to tak, że do skanowania zarówno klawiatury jak i pola LED (cokolwiek to znaczy) brałem te same linie.

Tą idę wykorzystuje np TM1638.

Udostępnij ten post


Link to post
Share on other sites
11 minut temu, Elvis napisał:

można spokojnie dodać ekspander, albo dwa i mieć prosty projekt, który pozwoli jego autorowi uczyć się dalej programowania

Moim zdaniem (podkreślona grubaśna czcionka) jest akurat odwrotnie - w tym konkretnym przypadku ekspander jest absolutnie zbędny, a autorowi bardziej wyjdzie na zdrowie nauczenie się podłączania diod i charlieplexingu oraz użycia przerwań zegarowych niż wątpliwe ułatwienie sobie pracy jakimiś dodatkowymi bibliotekami.

No - ale jak zaznaczyłem: to moje zdanie.

Udostępnij ten post


Link to post
Share on other sites
1 minutę temu, ethanak napisał:

a autorowi bardziej wyjdzie na zdrowie nauczenie się podłączania diod i charlieplexingu oraz użycia przerwań zegarowych

A po tym może robić to z wykorzystaniem ekspandera. Przy okazji, obliczenia:

16 minut temu, Elvis napisał:

Tak teoretycznie licząc: zapis do ekspandera to 3-4 bajty (adres urządzenia, numer rejestru, 1-2 danych). Niektóre wymagają mniej, ale liczmy dla 4 bajtów. Dla każdej kolumny musimy wykonać zapis - załóżmy że klawiatura ma 4 kolmny - mnożąc przez 4 mamy 16 bajtów. Po każdym zapisie dajemy odczyt wartości  - i i wychodzi wszystkiego razem 32 bajty, czyli 256bitów. Dojdą jeszcze bity startów, stopów, potwierdzeń - żeby w pamięci łatwiej liczyć, niech będzie 500 bitów.

I2C powinno poradzić sobie co najmniej z 100kHz, więc skanowanie zajmie jakieś 5ms.

są błędne. Samo skanowanie niech zajmuje 5ms ale CPU jest angażowany w tym czasie co 90us (przy 100kHz) na kilka us (5..10) w przerwaniu. Mamy więc obciążenie ok 5..10% w czasie 5ms i 0% w pozostałym okresie. Przyjmując skanowanie 50 razy na sekundę da to obciążenie CPU wyniesie 1 max 2,5%.

Obsługa przerwań I2C jest problemem? Jak już napisałem wcześniej, własna w arduino niemożliwa ale wiedząc, że gdy wywołujemy funkcje wire.start(), wire.write(0 itd tak naprawdę nie sa one wykonywane on-line na I2C tylko zapamiętywane w 32 bajtowym (dla AVR) buforze. Dopiero wire.stop() uruchomi transmisję na przerwaniach. Wystarczy więc wykorzytać ten fakt i w przerwaniach od timera obsługiwać i2C. 

Udostępnij ten post


Link to post
Share on other sites

W projekcie mam klawiaturę 7x7 więc dlatego 14 pinów, a diody w układzie 3 rzędy po 7 diod z kluczami tranzystorowymi więc potrzeba 10 pinów. W programie który podałem, wydzieliłem tylko 4x4.

Udostępnij ten post


Link to post
Share on other sites

To następnym razem uprzejmie podawaj z jakim rzeczywistym problemem mamy do czynienia. Czym jeszcze różni się ten twój "projekt" od tego, o czym do tej pory rozmawialiśmy?
 

 

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...