Tastendruck während Programmablauf sicher erkennen

Hi,
ich bastel nach langer Zeit mal wieder mit vielen WS2812 LEDs an einem Mega2560. Ziel ist es, einen Tastendruck (zwecks Programmwechsel) sicher zu erkennen, ohne die Ausführungsgeschwindigkeit des parallel laufenden Programms zu beeinträchtigen. Wie macht man das? Interrupt? Wenn ja, wie?
Momentan bremse ich entweder das Programm aus durch ständige Portabfragen oder ich verliere Tastendrücke durch zu seltene Portabfragen...
Danke für Tips, Helmuth.

Dafür gibts den Interrupt. Dein Controller macht, was er tun soll und unterbricht seine Arbeit dann, wenn du eine Taste drückst. Mit Tastern hast du dann noch das Problem, daß sie prellen (können). Du kannst sie entweder elektronisch oder per Software entprellen (such mal nach debounce). Wenn du dann noch deine Zeitsteuerungen über eine State Machine machst (also wie das Grundbeispiel Blink without Delay) bekommst du das, was du willst.

Viele Grüße

Elektrix

Hi:

byte taster_status = 0;
long taster_time = 0;

if(digitalRead(...) && taster_status = 0) {
  taster_status = 1;
  taster_time = millis()+50; //entprellzeit
 
  //taster gedruckt
}

if(taster_time < millis()) {
  taster_status = 0;  
}

Eventuell die Entprellzeit noch weiter nach unten setzen

[edit]
Da war jemand schneller :wink:

Interrupts müssen auch entprellt werden, da sie ansonsten zu oft auslösen. Ein Tastendruck ist aus Mikrocontroller Sicht ein sehr langsamer Vorgang, daher würde ich keinen Interrupt verwenden. Dieser unterbricht auch nur den Programmablauf und läuft nicht parallel ab.
[/edit]

Ich gehe da immer so vor:
Interrupt aktivieren, der auf den Tastendruck reagiert.
Der ruft eine ISR (Interrupt-Service-Routine)auf (sie sollte so kurz wie möglich sein, da nicht jedes Programm jederzeit problemlos gestoppt werden kann, z.B. könnte in der Zeit ja ein anderer Interrupt kommen), die nur ein Bit setzt und den Interrupt auschaltet. Dieses wird im Hauptprogramm dann periodisch abgefragt und wenn es gesetzt ist, wird ausgeführt, was auch immer die Taste auslösen soll.
Am Ende des Handlers wird der Interrupt fürs nächste Mal wieder aktiviert.
die Vorgehensweise stellt sicher dass:
-der erste Tastendruck sicher registriert wird
-der Programmablauf auch an kritischen Stellen unterbrochen werden kann, da die ISR enorm kurz ist
-der Taster sauber entprellt wird.

Wie das geht?
http://arduino.cc/de/Reference/AttachInterrupt

So was hier funzt auch ganz gut und sicher ohne Interrupt in der Loop.

// var's for checking valid keypress
int  minkeypresstime = 0;
byte digitalReadPin  = 0;
long millis_lastkey  = 0;
byte keydown         = 0;
byte pin = 5;  // any pin you like

void setup() 
{
  Serial.begin(9600); 
  // ......
  pinMode(pin, INPUT_PULLUP); // define the pin
  digitalReadPin  = pin;       // the pin to read from  
  keydown = 0;                      // remind the keypress
  minkeypresstime = 100;    // min. time (ms) the must have pressed continously
  // ......
}

// used for checking valid keypress
long millis_act;

void loop() 
{
  // ......
  
  millis_act = millis();
  // check keypress for a secure/defined time
  // set "minkeypresstime" to 0 (or < 5) stop's checking for keypress
  if (minkeypresstime > 5)              // ... should the min time =?!
  { if (!digitalRead(digitalReadPin))   // Key pressed - Pin is going down ! We have pullup on pin !
    { if (keydown == 0)
      { millis_lastkey = millis_act;    // remind time
        keydown = 1;                    // remind the keypress
      }
    }
    else                                // Key not pressed - Pin is up - We have pullup on pin !
    { if (keydown == 1)                 // was it pressed before ?
      { millis_lastkey = millis_act - millis_lastkey;
        if (millis_lastkey >= minkeypresstime) // longer than defined ?
          // ---> here we have the "Keypressed-Event" !
          Serial << "Keypress-duration OK ! (min.. " << minkeypresstime << " - act.: " << millis_lastkey << " ms)" << endl;
          // ---> use "millis_lastkey" for your intended purpose
          // ...... call routine for valid Key-Press here !!
        else         
          Serial << "Keypress to short !  (min.. " << minkeypresstime << " - act.: " << millis_lastkey << " ms)" << endl;
        keydown = 0;
      }      
    }
  }

  // ......
}

Der Taster hängt hier an einem belibiegen Pin, an dem der PullUp eingeschaltet ist.

Nachtrag noch, falls so nicht gleich ersichtlich:
Man kann hier mit einem Wert in minkeypresstime vorgeben, wie lange eine Taste in ms mindestens gedrückt sein muss, damit der Tastendruck gültig ist.
In millis_lastkey steht dann nach gültig ausgewertetem Tastendruck die Zeit in ms, wie lange der Tastendruck gedauert hat.
… und wenn man noch die Anmerkungen und Sendkeys’s (nur zur Veranschaulichung !) rauswirft, hat auch nen recht kurzen, kleinen Code.

Solange Du nicht super knapp mit Speicher bist ist die bevorzugte Lösung die Debounce Library: Arduino Playground - Debounce. Das hält den Code leserlich und Du musst Dir nicht Gedanken machen wie man das programmiert. Die Library blockiert vorteilshafterweise nicht.

hallo, helmuth,

nach meinen erfahrungen mit WS2812 und ir-fernbedienung kann ich Dir nur sagen, daß Du da andern rangehen mußt. interrupts kannst Du vergessen.

aber wie wär's, wenn Du das senden der daten für den strip gleich als debouncezeit nimmst?
also:
einmalige tasterabfrage, wert speichern.
stripdaten senden.
tasterabfrage.
stripdaten senden.
tasterabfrage.
usw.

wenn taster bei zwei abfragen hintereinander (oder mehr, wenn's noch prellt), dann aktion.

gruß stefan

Rabenauge:
Ich gehe da immer so vor:
Interrupt aktivieren, der auf den Tastendruck reagiert.

Hallo,

und was machst Du, wenn Du mehr Taster als interruptfähige Pins hast, z.B. drei am Uno oder acht am Mega?

Gruß,
Ralf

Kurzer Blick ins Datenblatt: bei den Atmegas sind ALLE Pins interruptfähig. Da auf den Arduinos Atmegas verbaut sind ist das gar kein Problem.

Dann musst Du mir das aber mal erklären: Arduino - AttachInterrupt

Bei Deiner Erklärung interessiert mich besonders, wie Du hierzu stehst:

Die meisten Arduino Boards haben zwei externe Interrupts: Nummer 0 (auf digitalem Pin2) und 1 (auf digitalem Pin3). Die nachstehende Tabelle zeigt die verfügbaren Interrupts der jeweiligen Boards.

Gruß,
Ralf

Das ist einfach: Du schaust an der falschen Stelle nach.

Im Datenblatt Kapitel 13 steht:

  1. External Interrupts
    The External Interrupts are triggered by the INT0 and INT1 pins or any of the PCINT23...0 pins.

Und eine Library dafür gibt es auch.

http://playground.arduino.cc/Main/PinChangeInt

Ich glaube, ich weiß die Antwort - warte aber mal gespannt mit. 8)

Arrrgh - da war der Udo 30s schneller.
Aber PinChangeInt wäre auch die Lib meines Verdachts gewesen.

@Udo Klein: Hast Du mal Terminator SCC gesehen? Da sagt die Terminatorin oft: "Danke, dass Sie es mir erklärt haben!" - Dem kann ich mich im Moment nur 100%ig anschließen :wink:

Allerdings ziehe ich meine ursprüngliche Frage nicht zurück, sondern ändere sie in: "Was machst Du, wenn Du mehr Taster als für den Interrupt benutzbare Pins hast?" - Das soll übrigens kein Gemoser sein, sondern ist ernst gemeint, ich habe nämlich ein Keyboard mit 37 Tasten und würde die gerne sicher abfragen.

Gruß,
Ralf

Na wenn Du ein Keyboard hast, dann hat das in der Regel einen eingebauten Controller. Wenn nicht kaufst Du entweder einen oder schlachtest ein altes Keyboard aus welches einen hat.

Ansonsten mußt Du die Matrix selber auslesen. Das geht auch per Interrupt. Wenn Du z.B. eine n*m Matrix mit n aktiven Leitungen und m Leseleitungen hast kannst Du ja alle n Leitungen aktivieren und warten bis eine der m Leitungen einen Interrupt auslöst. Dann aktivierst Du die n Leitungen einzeln und schaust welche der m Leitungen das Signal zeigen.

Ähnlich kann man auch mit Schieberegistern verfahren.

Wenn es unbedingt kein Keyboard Controller sein soll, dann würde ich die Tastatur aber per Timerinterrupt pollen. Grund: erstens will ich mein Hauptprogramm nicht verschmutzen und zweitens will ich in der Regel sowieso eine FIFO Queue für die Tastatur um auch dann auf Tasten zu reagieren wenn der Prozessor gerade was anderes erledigt.

Aber wie gesagt: ich würde einen fertigen Controller für sowas nehmen. Oder gleich einen Raspi und ein USB Keyboard. Man muß nicht alles mit dem Arduino machen. Man kann auch einen Raspi nehmen und den Arduino als Output Treiber. Für den Preis eines Raspi kann man jedenfalls nicht lange programmieren. Außer die eigene Zeit kostet nichts...

@Udo Klein: Nein, leider kein Controller. Es handelt sich nicht um eine alphanumerische Tastatur, sondern um ein Keyboard (Synthesizer). Zum "irgenwo-was-ausbauen" habe ich da leider keine Möglichkeit. Es hat eine integrierte Dioden-Matrix, aber die Leiterbahnen habe ich noch nicht entfieselt, mal sehen, wenn ich mal Zeit und Lust habe XD

Auf jeden Fall, ein Interrupt pro Taste käme mir da gerade Recht, aber das kann ich wohl vergessen.

Gruß,
Ralf

Den Controllern ist es völlig egal ob die Tastatur alphanumerisch ist oder nicht. Und mehr als 10 Tasten wirst Du auch auf einem Musik Keyboard eher selten gleichzeitig drücken.