Keypad Shield - Serieller Monitor gibt Tastenfolge aus

Hallo, ich habe mir nach der Anleitung aus dem Buch "Die Elektronische Welt mit Arduino entdecken" (Oreilly) ein Keypad gebaut, habe keinerlei Kurzschlüsse (mit Multimeter geprüft, und mit Lupe). Nun stecke ich das Shield auf den Arduino und lade den Sketch hoch, kaum geschehen gibt der Serielle Monitor Tastenfolgen aus. Wie kann das sein wenn doch keine Tasten gedrückt werden?

Geilduino: Nun stecke ich das Shield auf den Arduino und lade den Sketch hoch, kaum geschehen gibt der Serielle Monitor Tastenfolgen aus. Wie kann das sein wenn doch keine Tasten gedrückt werden?

  • Fehlerhafte Hardware-Beschaltung und/oder
  • Fehlerhafte Software-Abfrage

Was soll's sonst anderes sein?

Ein häufig gemachter Anfängerfehler beim Anschließen von Buttons sind z.B. fehlende oder fehlerhaft beschaltete PullUp/PullDown-Widerstände, so dass abgefragte Eingänge "floaten" statt definierte Pegel zu haben.

Gute frage: ich lade gleich nach dem Essen mal den Sketch sowie die Libraries hoch die ich geschrieben habe (nach anleitung).

Ähem jo im Buch steht was davon das man die internen Widerstände nutzen kann, aber ich begreif das nicht richtig, könntest Du mir das erklären?
Das ist der Sketch:

#include <MyKeyPad.h>

int rowArray[] = {2,3,4,5};              //Array mit Zeilen Pin-Nummern initialisieren
int colArray[] = {6,7,8};                  //Array mit Spalten Pin-Nummern initialisieren

MyKeyPad myOwnKeyPad(rowArray, colArray);    //Instanziierung eines Objektes

void setup(){
  Serial.begin(9600);                        //Serielle Ausgabe vorbereiten
  myOwnKeyPad.setDebounceTime(150);         //Prellzeit auf 500ms setzen
}

void loop(){
  char myKey = myOwnKeyPad.readKey();       //Abfragen des gedrückten Tasters
  if(myKey != KEY_NOT_PRESSED)              //Abfrage ob ein Taster gedrückt
    Serial.println(myKey);
}

das MyKeyPad.h:

#ifndef MYKEYPAD_H 
#define MYKEYPAD_H

#if ARDUINO < 100 
#include <WProgram.h> 
#else
#include <Arduino.h> 
#endif

#define KEY_NOT_PRESSED '-'// Wird benötigt, wenn keine Taste gedrückt wird
#define KEY_1 '1'
#define KEY_2 '2'
#define KEY_3 '3' 
#define KEY_4 '4' 
#define KEY_5 '5' 
#define KEY_6 '6' 
#define KEY_7 '7' 
#define KEY_8 '8' 
#define KEY_9 '9' 
#define KEY_0 '0' 
#define KEY_STAR '*' 
#define KEY_HASH '#'

class MyKeyPad{ 
  public:
  MyKeyPad(int rowArray[], int colArray[]);       // Parametrisierter 
                                                  // Konstruktor
void setDebounceTime(unsigned int debounceTime);  // Setzen der 
                                                  // Prellzeit
char readKey();                                   // Ermittelt die gedrückte Taste auf dem KeyPad
private:
	unsigned int debounceTime;                       // Private Variable für die Prellzeit
	long lastValue;									// Letzte Zeit der millis-Funktion 
	int row[4];                       // Array für die Zeilen
	int col[3];         				// Array für die Spalten                              
}; 
#endif

das MyKeyPad.cpp

#include "MyKeyPad.h"
// Parametrisierter Konstruktor
MyKeyPad::MyKeyPad(int rowArray[], int colArray[]){
// Kopieren der Pin-Arrays
for(int r = 0; r < 4; r++)
  row[r] = rowArray[r]; 
for(int c = 0; c < 3; c++)
  col[c] = colArray[c];
// Programmieren der digitalen Pins
for(int r = 0; r < 4; r++)
  pinMode(row[r], OUTPUT);
for(int c = 0; c < 3; c++)
  pinMode(col[c], INPUT);
// Initialwert für debounceTime auf 300ms festlegen debounceTime = 300;
}
// Methode zum Setzen der Prellzeit
void MyKeyPad::setDebounceTime(unsigned int time){ 
  debounceTime = time;
}
// Methode zum Ermitteln des gedrückten Tasters auf dem KeyPad
char MyKeyPad::readKey(){
  char key = KEY_NOT_PRESSED;
  for(int r = 0; r < 4; r++){
    digitalWrite(row[r], HIGH);
    for(int c = 0; c < 3; c++){
      if((digitalRead(col[c]) == HIGH)&&(millis() - lastValue) >= debounceTime){
        if((c==2)&&(r==3)) key = KEY_1;
        if((c==1)&&(r==3)) key = KEY_2;
        if((c==0)&&(r==3)) key = KEY_3;
        if((c==2)&&(r==2)) key = KEY_4;
        if((c==1)&&(r==2)) key = KEY_5;
        if((c==0)&&(r==2)) key = KEY_6;
        if((c==2)&&(r==1)) key = KEY_7;
        if((c==1)&&(r==1)) key = KEY_8;
        if((c==0)&&(r==1)) key = KEY_9;
        if((c==2)&&(r==0)) key = KEY_STAR; // *
        if((c==1)&&(r==0)) key = KEY_0;
        if((c==0)&&(r==0)) key = KEY_HASH; // #
        lastValue = millis();
      }
    }
  digitalWrite(row[r], LOW); // Zurücksetzten auf Ursprungspegel
  }
return key; 
}

und so richtig kapiert habe ich auch nicht wie genau das mit den Textdateien geht die das Highlighting bewirken.

Geilduino:
Ähem jo im Buch steht was davon das man die internen Widerstände nutzen kann, aber ich begreif das nicht richtig, könntest Du mir das erklären?

Hast Du dieses Arduino Button-Beispiel selbst mit Breadboard, Widerstand und Button zusammengesteckt und durchgearbeitet: http://arduino.cc/en/tutorial/button

Und dabei verstanden, wozu der Widerstand dient?

Ich hab das Buch nicht und darum kann ich wenig sagen.
Laut Analyse des/r Sketch / Bibiothek mußt Du an den Arduinoeingängen (Spalten-Pins colArray = {6,7,8}:wink: Pulldown-Widerstände hängen.

MyKeyPad.cpp hat aber einen kollosalen Gedankenfehler in char MyKeyPad::readKey() :
Die Zeilen-Pins werden LOW geschalten; die Zeile, die gerade abgefragt werden soll, auf HIGH. Wenn Du dann 2 Tasten auf der selben Spalte gleichzeitig drückst dann hast Du einen Kurzschluß zwischen eine LOW und einem HIGH geschaltenen Zeilen-Ausgang. Richtig ist die Zeilen-Ausgänge, die nicht abgefragt werden statt auf LOW auf Eingang (= hochohmig) zu schalten.

// Methode zum Ermitteln des gedrückten Tasters auf dem KeyPad
char MyKeyPad::readKey(){
  char key = KEY_NOT_PRESSED;
  for(int r = 0; r < 4; r++){
    digitalWrite(row[r], HIGH); 
    for(int c = 0; c < 3; c++){
      if((digitalRead(col[c]) == HIGH)&&(millis() - lastValue) >= debounceTime){
        if((c==2)&&(r==3)) key = KEY_1;
        ...
        lastValue = millis();
      }
    }
  digitalWrite(row[r], LOW); // Zurücksetzten auf Ursprungspegel
  }
return key;

Grüße Uwe

jurs:

Geilduino:
Ähem jo im Buch steht was davon das man die internen Widerstände nutzen kann, aber ich begreif das nicht richtig, könntest Du mir das erklären?

Hast Du dieses Arduino Button-Beispiel selbst mit Breadboard, Widerstand und Button zusammengesteckt und durchgearbeitet: http://arduino.cc/en/tutorial/button

Und dabei verstanden, wozu der Widerstand dient?

Ja sicher zu allererst.Der Pulldown Widerstand zieht 5V auf Masse.
Mein Problem liegt meistens an der Software da ich noch nicht ganz 100%ig raus habe wie ich die integrierten Pulldownwiderstände des Atmega 328 nutzen kann, bzw wie ich das mit rein programmiere.

@Uwefed hast du das jetzt schon so geändert das es funktioniert?
Ich frage nur weil ich bis grade noch gearbeitet habe und nicht immer hier reinschauen kann.

Lg Gd

Hm, hab das jetzt mal so geändert (hoffe das das so richtig ist, denn der SM gibt die tasten zwar aus nur eben sehr verzögert, lediglich das selbst tastendrücken vom keypad ist nun weg)// Methode zum Ermitteln des gedrückten Tasters auf dem KeyPad
char MyKeyPad::readKey(){
char key = KEY_NOT_PRESSED;
for(int r = 0; r < 4; r++){
digitalWrite(row[r], INPUT);
for(int c = 0; c < 3; c++){
if((digitalRead(col

) == INPUT)&&(millis() - lastValue) >= debounceTime){
        if((c==2)&&(r==3)) key = KEY_1;
        if((c==1)&&(r==3)) key = KEY_2;
        if((c==0)&&(r==3)) key = KEY_3;
        if((c==2)&&(r==2)) key = KEY_4;
        if((c==1)&&(r==2)) key = KEY_5;
        if((c==0)&&(r==2)) key = KEY_6;
        if((c==2)&&(r==1)) key = KEY_7;
        if((c==1)&&(r==1)) key = KEY_8;
        if((c==0)&&(r==1)) key = KEY_9;
        if((c==2)&&(r==0)) key = KEY_STAR; // *
        if((c==1)&&(r==0)) key = KEY_0;
        if((c==0)&&(r==0)) key = KEY_HASH; // #
        lastValue = millis();
      }
    }
  digitalWrite(row[r], HIGH); // Zurücksetzten auf Ursprungspegel

Geilduino: Mein Problem liegt meistens an der Software da ich noch nicht ganz 100%ig raus habe wie ich die integrierten Pulldownwiderstände des Atmega 328 nutzen kann, bzw wie ich das mit rein programmiere.

@Uwefed hast du das jetzt schon so geändert das es funktioniert? Ich frage nur weil ich bis grade noch gearbeitet habe und nicht immer hier reinschauen kann.

Lg Gd

Der ATmega hat keine internen Pulldown Widerstände sondern nur Pullup. Die schaltet man ein indem man bei einem Eingang digitalWrite(pin,HIGH); schreibt und wieder aus indem man digitalWrite(pin,LOW); schreibt.

Grüße Uwe

Die Teorie der Tastaturabfrage einer Matrix ist folgende:

Dei Tasten schalten eine Zeile mit einer Spalte zusammen.
Man hat die Tasten in einer Matrix von Zeilen und Spalten zusammengeschaltet.
Man definiert zB die Spalten als Eingänge. Die Spalten bzw Arduino-Eingänge brauchen einen Pullup Widerstand damit sie einen defienierten Pegel haben. Man kann die internen Pullup aktivieren oder externe Pullupwiderstände verwenden.

Die Zeilen sind auch als Eingänge definiert, brauchen aber keinen Pullupwiderstand.

Jetzt wird eine Zeile (ein Arduino Pin) als Ausgang definiert und auf LOW geschaltet. Eine gedrückte Taste bringt jetzt die entsprechende Spalte auf LOW. Man liest alle Spalten ein. Ein LOW-Signal auf eine Spalte läßt auf eine gedrückte Taste schließen . Es ist die Taste di zwischen aktiver Reihe und entsprechender Spalte liegt. Nachdem alle Spalten eingelesen sind, wird der Arduino-Pin der aktiven Reihe wieder als Eingang definiert und alles mit der nächsten Reihe wiederholt bis alle Reihen kontrolliert sind.

Eine solche Matrix entziffert sicher nur 1 gedrückte Taste. Bei 2 gedrückten Tasten funktioniert das nur in bestimmten Fällen aber nicht allgemein.

Abhilfe sind Dioden in Reihe zum Taster. Damit kann man gleichzeitige Mehrfachdrücke unterscheiden.

Die genannte Pegel sind so gewählt daß die internen Pullupwiderstände verwendet werden können. Mit externen Pulldown-Widerständen kann auch ein HIGH Pegel an den Reihen verwendet werden.

Aus Beispiel umgeschrieben:

Alle Pins müssen als Eingänge definiert werden MyKeyPad.cpp:

#include "MyKeyPad.h"
  ...
// Programmieren der digitalen Pins
for(int r = 0; r < 4; r++)
  pinMode(row[r], INPUT);
for(int c = 0; c < 3; c++)
  pinMode(col[c], INPUT);

char MyKeyPad::readKey(){
  char key = KEY_NOT_PRESSED;
  for(int r = 0; r < 4; r++){
    pinMode(row[r], OUTPUT);
    digitalWrite (row[r], HIGH);  // eine Zeile aktivieren
    for(int c = 0; c < 3; c++){
      if((digitalRead(col[c]) == LOW)&&((millis() - lastValue) >= debounceTime)){
        if((c==2)&&(r==3)) key = KEY_1;
        if((c==1)&&(r==3)) key = KEY_2;
        if((c==0)&&(r==3)) key = KEY_3;
        if((c==2)&&(r==2)) key = KEY_4;
        if((c==1)&&(r==2)) key = KEY_5;
        if((c==0)&&(r==2)) key = KEY_6;
        if((c==2)&&(r==1)) key = KEY_7;
        if((c==1)&&(r==1)) key = KEY_8;
        if((c==0)&&(r==1)) key = KEY_9;
        if((c==2)&&(r==0)) key = KEY_STAR; // *
        if((c==1)&&(r==0)) key = KEY_0;
        if((c==0)&&(r==0)) key = KEY_HASH; // #
        lastValue = millis();
      }
    }
   pinMode(row[r], INPUT); // Zurücksetzten auf Ursprungspegel

Grüße Uwe

Uwefed meinst du es würde reichen wenn ich die Dioden mit in die Schaltung baue? oder sollte ich doch lieber mit widerständen Arbeiten? denn scheinbar ist es egal was ich mache, mit deinem Code in der MyKeyPad.cpp funktionieren bei Draufsicht nur die mittleren Tasten vom Tastenfeld (also nur die Zahlen 2,5,8,0) wobei dort das gleiche Problem auftritt - nach der zweiten gedrückten taste verselbstständigt sich das ganze wieder. Wenn ich den Code anpasse egal in welcher form passiert das gleiche.

Ich hab die faxen dicke und will das das teil funktioniert, da ich nicht einsehe ein Tastenfeld zu kaufen wenn man sich eins selbst bauen kann.

Der orginale Code und 3 Widerstände von 10kOhm an den Pins 6,7,8 gegen Masse müßten funktionieren. Schalte zum Schutz 4 Widerstände von 1kOhm zwischen den Pins 2,3,4,5 und der Tastatur.

Grüße Uwe

Hey,
Ich habe das Buch auch und glaube dein Fehler liegt hier:

digitalWrite(row[r], HIGH); // Zurücksetzten auf Ursprungspegel

So wie ich das verstehe arbeitest du hier mit Pull Up Widerständen, fragst aber nach High Zustand ab,
das bedeutet das alle Tasten als HIGH angezeigt werden. Man müsste entweder nach Low Zustand der Taster abfragen oder mit Pull Down Beschaltung arbeiten.
Ist aber nur eine Vermutung, bin kein Profi.

Gruß,
Tobi

Hallo Tobias. Im Code den Geilduino Zitiert hat, werden die internen Pullupwiderstände nicht aktiviert.

Hab einen Auszug (legale? Vorschau) des Buches im Internet gefunden und das Keypad-Projekt teilweise gelesen (fehlten einige Seiten). Auf Seite 452 schreibt Erik, daß die Eingänge ohne definierten Pegel sind und der Sketch / Schaltung trotzem meist funktioniert, falls nicht viele elektromagnetischen oder elektrostatischen Störungen vorhanden sind). Als Übung schlägt er vor, Pullupwiderstände zu aktivieren und den Code umzuschreiben.

Ansonsten habe ich einen Lösungsweg ja bereits aufgezeigt und erklärt.

Den unten aufgezeiten Gedankenfehler kann ich leider bestätigen.

Auf http://erik-bartmann.de/programmierung/downloads2.html gibt es Korrekturen, Bauteilliste und die Sketchs zum runterladen.

Grüße Uwe