Sinnvolle "Mehrfach-Tastenabfrage"

Hallo,

ich hatte vor kurzem (bei einer Anzahl von fünf Tastern mit jeweils einer ihnen zugeordneten LED) Probleme folgende Funktionalitäten zu implementieren:

  1. Beim Druck auf eine der Tasten soll die entspr. LED dieser Taste leuchten.
  2. Wird die erste Taste gedrückt gehalten und zusammen mit der fünften Taste zusammen für drei Sekunden gedrückt gehalten, soll "etwas passieren".
  3. Werden alle fünf Tasten für drei Sekunden gedrückt gehalten soll "etwas passieren".

Das Ganze sah am Ende so aus und mein Gefühl sagt mir, dass das eine total unbefriedigende Lösung sein muss:

Wie kann man dass denn sinnvoller/ besser lösen? :astonished:

Gruß Chris

Edit: Die "Einzelabfragen" bzgl. dem erstem Punkt (s.o.) habe ich der Einfachheit halber nicht hier im Code inkludiert, also nicht wundern. :*

if (Knopf_1_State == 1 || Knopf_2_State == 1 || Knopf_3_State == 1 || Knopf_4_State == 1 || Knopf_5_State == 1 )  // Falls eine der Eingangstasten gedrückt gehalten wird..
  {
    tastHaltestatus = 1;
    if (Knopf_1_State == 1 && Knopf_2_State == 0 && Knopf_3_State == 0 && Knopf_4_State == 0 && Knopf_5_State == 1)  // Falls nur diese beiden Tasten (!) gedrückt gehalten werden..
    {
      digitalWrite(LED_1,LOW);
      digitalWrite(LED_5,LOW);
      if (tastZeitpunkt != 0 && millis() - tastZeitpunkt > halteDauer)
      {
        "etwas soll passieren"
      }
    }
    else if (Knopf_1_State == 1 && Knopf_2_State == 1 && Knopf_3_State == 1 && Knopf_4_State == 1 && Knopf_5_State == 1)  // Falls sämtliche Eingangstasten gedrückt gehalten werden..
    {
      digitalWrite(LED_1,LOW);
      digitalWrite(LED_2,LOW);
      digitalWrite(LED_3,LOW);
      digitalWrite(LED_4,LOW);
      digitalWrite(LED_5,LOW);
      if (tastZeitpunkt != 0 && millis() - tastZeitpunkt > halteDauer)
      {
        "etwas soll passieren"
      }
    }
  }

Wäre jetzt auch mein erster Ansatz gewesen, das so anzugehen. Ist nicht die schlimmste Lösung.

Was jedoch vielleicht etwas einfacher wäre, Die Werte für die 5 Taster in einer Zahl darzustellen.

Kleines Beispiel:

T1 gedrückt : 1 T2 nicht gedrückt : 0 T3 gedrückt : 1 T4 gerdrückt : 1 T5 nicht gedrückt : 0

-> 10110

Ah-ok.

Und dann praktisch bei Tastendruck, bzw. beim loslassen einer Taste mit bitWrite(x, n, b) "befüllen", oder?

Gruß Chris

Hi,
du könntest deinen Code so verbessern:

Im ersten Moment sieht der Code länger aus, du kannst aber hoffentlich sehr schnell die Vorteile bei der Kombinationsauswahl feststellen, falls mehrere Kombinationen hinzukommen.

// Um nun Speicher zu Sparen kannst du auch direkt in Knopf State den Zustand einlesen Also z.B: 
//uint8_t Knopf_State = (digitalRead(Knopf_1)<<0 | digitalRead(Knopf_2)<<1 | digitalRead(Knopf_3)<<2 | digitalRead(Knopf_4)<<3 | digitalRead(Knopf_5)<<4);

//hier erstellen wir ein bitmuster, bit pos 0 = knopf 1, bit pos 1 = knopf 2, ... 
uint8_t Knopf_State = (Knopf_1_State<<0 | Knopf_1_State<<1 | Knopf_1_State<<2 | Knopf_1_State<<3 | Knopf_1_State<<4);

// du musst die leds nicht mehr alle einzeln setzen, mit dieser funktion werden die leds bei 1 auf low und bei 0 auf hight gesetzt.
// durch die bit funktion wird zbw knopf 1  wie folgt vergilenne 
// 0000 0001 & 000 0001  liefert nur 1, wenn der taster gedrückt ist. 
// bei taster 5 werdend die bits wie folgt verschoben
// 0001 XXXX >> 5 => 0000 0001 & 0000 0001 => 1 wenn Taster gedrückt
// X steht dabei für den Status der anderen Taster
void set_led_status(uint8_t status)
{
  digitalWrite(LED_1, (((Knopf_State>>0) & 0x01)?LOW:HIGH);
  digitalWrite(LED_2, (((Knopf_State>>1) & 0x01)?LOW:HIGH);
  digitalWrite(LED_3, (((Knopf_State>>2) & 0x01)?LOW:HIGH);
  digitalWrite(LED_4, (((Knopf_State>>3) & 0x01)?LOW:HIGH);
  digitalWrite(LED_5, (((Knopf_State>>4) & 0x01)?LOW:HIGH);
}


if(Knopf_State) { // erfüllt sobald ein Knopf betätigt ist, da der Zahlenwert größer als 0 ist
  tastHaltestatus = 1;
  
  // LED Status setzen
  set_led_status(Knopf_State);
  
  //kombination abfragen, die obersten 3 bit sind 0 da hier keine knöpfe reingeschoben wurden
  //es muss nur noch das bitmuster der Taster abgefragt werden. Die LEDs werden automatisch passen gesetzt
  switch(Knopf_State)
  {
    case B00010001:    // taster 5 und taster 1 gedrückt     
      if (tastZeitpunkt != 0 && millis() - tastZeitpunkt > halteDauer)
      {
        "etwas soll passieren"
      }
    break;
    
    case B00011111:   // alle taster gedrückt      
      if (tastZeitpunkt != 0 && millis() - tastZeitpunkt > halteDauer)
      {
        //"etwas soll passieren"
      }
    break;
    
    default:
      // status nicht definiert
    break;
  }
}

Bezüglich den simpleThreads. Spiel einfach mal ein wenig mit der Lib rum, man versteht diese dann doch recht schnell. Eigentlich ist die Lib keine richtige Lib, da kein Objekte erstellt werden. Aber sie generiert durch Makros mit denen Quellcode und Funktionen erstellt werden mit einfachen Möglichkeiten komplizierte Ablaufe. Schau dir die Beispiel in Ruhe an und versuch die Vorteile zu erkennen. Z.B: Es reicht wenn Taster alle 50ms bis 100 ms Abgefragt werden. Ich kenne niemanden der 20 Tasten in der Sekunde nacheinander betätigen kann ;-). Diese Abfrage könnte man in einen virtuellen Thread legen.

Jomelo:
if(Knopf_State) { // erfüllt sobald ein Knopf betätigt ist, da der Zahlenwert größer als 0 ist

Hallo,

vielen Dank für Deine Antwort. Kannst Du mir diese Zeile evtl. noch etwas genauer erklären?

Dank Dir.

Gruß Chris

Bei if abfragen muss man nicht unbedingt auf true oder false abfragen. Die Abfrage ist auch erfüllt wenn der Inhalt einer Variable oder der Ruckgabewert einer Funktion größer 0 sein also z.B. 1. Sobald der Wert 0 ist ist die Bedingung nicht erfüllt.

Also kann man auch schreiben:

if(digitalRead(PinXXX)) ... 
//äquivalent
if(digitalRead(...) == HIGH)

Nun ist die Bedingung erfüllt sobald der Taster gedrückt ist und nicht erfüllt wenn dieser nicht gedrückt ist.

Negieren lässt sich das ganze mit dem ! Ausrufezeichen. Z.B.

if(!digitalRead(PinXXX)) ....
//äquivalent
if(digitalRead(...) != HIGH)

Da wir nun in der Zeile über der Abfrage die einzelnen Bits, also den Status der Eingänge in der Variable Speichern kann man mit der Abfrage eine oder Verknüpfung erzielen.

if(Knopf_State)

Beispiel: - kein Taster gedrückt. Alle Werte 0 in der Variable -> Bedingung nicht erfüllt. - Taster 5 gedrückt. Bit 4 gesetzt -> Zahlenwert 16 -> Wert größer 0 -> Bedingung erfüllt - alle Taster gedrück . Bit 4 - Bit 0 -> Zahlenwert 31 -> Wert größer 0 -> Bedingung erfüllt

Ansonsten einfach mal anschauen wie die Funktionen bitRead ,BitWrite und BitWriteValue funktionieren, die machen im Hintergrund das gleiche wie die Zusammensetzung von Bits für die Knopf_State Variable.

Am Anfang ist es vielleicht auch sinnvoll die Verknüpfungen auf einem Blatt Papier zu überprüfen. Nach einiger Zeit geht das dann auch schnell im Kopf.

Danke für die super Erklärung.

Kannst Du Dir evtl. den von Dir geposteten Code doch noch einmal genauer anschauen?

Müsste es nicht anstatt…

Jomelo:

uint8_t Knopf_State = (Knopf_1_State<<0 | Knopf_1_State<<1 | Knopf_1_State<<2 | Knopf_1_State<<3 | Knopf_1_State<<4);

…so heissen:

uint8_t Knopf_State = (Knopf_1_State<<0 | Knopf_2_State<<1 | Knopf_3_State<<2 | Knopf_4_State<<3 | Knopf_5_State<<4);

Und hier komme ich mit dem einzelnen “status” nicht klar:

Jomelo:

void set_led_status(uint8_t status)

{
 digitalWrite(LED_1, (((Knopf_State>>0) & 0x01)?LOW:HIGH);
 digitalWrite(LED_2, (((Knopf_State>>1) & 0x01)?LOW:HIGH);
 digitalWrite(LED_3, (((Knopf_State>>2) & 0x01)?LOW:HIGH);
 digitalWrite(LED_4, (((Knopf_State>>3) & 0x01)?LOW:HIGH);
 digitalWrite(LED_5, (((Knopf_State>>4) & 0x01)?LOW:HIGH);
}

Irgendwas scheint da nicht so ganz zu stimmen. :~

Ach ja- wie könnte ich es denn bewerkstelligen, dass die drei vordersten Bits unabhängig von ihrem Status nicht berücksichtigt werden?

Gruß Chris

Zu: Müsste es nicht anstatt.. Ja, Kopie Paste Fehler ;-)

Zu Den Bits die verknüpft werden: Es sollte passen, wo genau verstehst du den Quellcode nicht mehr ? Welche Funktion wird fehlerhaft dargestellt ?

Die obersten 3 Bits kannst du mit einer Maske ausblenden z:B. (B00011111 & Knopf_State) Nun sind die obersten 3 Bits von Knopf State = 0 und alle anderen behalten ihren Inhalt. Abfrage der Switch Case Anweisung wäre dann mit 5 Stellen möglich z.B.: case : B10001; da die oberen 3 Stellen immer 0 sind.

Hey, danke für den Maskierungs-Tipp.. hoffe ich hab das jetzt endlich mal verstanden.

Was ich noch nicht verstehe ist die Bedeutung des alleine stehenden Worts "status", da es nur einmal im kompletten Sketch vorkommt und ich auf Arduino.cc in der Reference dazu auch nichts finden kann.

Gruß Chris

Es sollte eigentlich so aussehen:

void set_led_status(uint8_t status)
{
  digitalWrite(LED_1, (((status>>0) & 0x01)?LOW:HIGH);
  digitalWrite(LED_2, (((status>>1) & 0x01)?LOW:HIGH);
  digitalWrite(LED_3, (((status>>2) & 0x01)?LOW:HIGH);
  digitalWrite(LED_4, (((status>>3) & 0x01)?LOW:HIGH);
  digitalWrite(LED_5, (((status>>4) & 0x01)?LOW:HIGH);
}

Da Status ja beim Funktionsaufruf mit Knopf_State gefüttert wird. Sorry, das ich das nicht sofort gesehen hatte.

Ok- jetzt erschließt es sich mir auch. 8)

Danke und Gruß!

Chris