Forum: Mikrocontroller und Digitale Elektronik Tasten per Software entprellen


von Olli Z. (z80freak)


Lesenswert?

Zu dem Thema hab ich so viel im Netz gefunden, das ich fast nicht mehr 
weiss was ich nun einsetzen soll...

Klar ist wohl, das man nicht mit delay() arbeiten sollte um die 
Prellzeit abzuwarten, denn die Main-Loop sollte in kurzer Zeit 
durchlaufen werden können. Zudem sollte diese Durchlaufzeit möglichst 
konstant sein.
Um EMV und sonstige Geister fern zu halten arbeiten die allermeisten mit 
dem internen Pullup und ziehen den Taster gegen GND.

Dennoch sind viele Lösungen unglaublich kompliziert. Ich hätte mir das 
viel einfacher vorgestellt:

Sobald man einen Tastendruck erkennt (Pin auf LOW) gilt diese als 
gedrückt. Danach setzt man das lesen weiterer Tastendrücke für 50-100ms 
aus sodass praktische jede Prellzeit vorüber ist.

Wechselt der Zustand anschließend wieder auf HIGH gilt die Taste als 
losgelassen. Auch hier setzt man weitere Lesezugriffe für eine gewisse 
Zeit aus.

Die Wartezeit könnte man mittels LogicAnalyzer, Oszi oder evtl. auch 
einer einfachen Testsoftware auf dem Arduino bei Bedarf für den 
jeweiligen Taster ermitteln (kann ja auch ein Tilt-Sensor oder sowas 
sein). Ich habe mal 50 ms angenommen, aber auch 100 ms wären noch ok. So 
schnell drückt kein Mensch, sodass man sicher keinen Tastendruck 
"verliert".

Test-Aufbau: Taster am D2 vom Arduino (Atmega328) und gegen GND.

Mein erster Code um einfach nur den entprellten Zustand an eine LED 
weiterzugeben:
1
#define BUTTON_BOUNCE_TIME 50
2
#define BUTTON_PIN 2
3
#define LED_PIN 4
4
5
int button_pushed = 0;
6
long ignore_until = 0;
7
8
void setup()
9
{
10
  pinMode(BUTTON_PIN, INPUT_PULLUP);
11
  pinMode(LED_PIN, OUTPUT);
12
  digitalWrite(LED_PIN, LOW);
13
}
14
15
void loop()
16
{
17
  if (millis() > ignore_until) {
18
    int button_read = digitalRead(BUTTON_PIN);
19
    if (button_read == LOW) { /* button was first pushed, or still hold down */
20
      button_pushed = 1;
21
    }
22
    else if (button_pushed == 1 && button_read == HIGH) { /* button was released */
23
      button_pushed = 0;
24
    }
25
    ignore_until = millis() + BUTTON_BOUNCE_TIME;
26
  }
27
28
  /* do some other funny stuff...
29
   * delay() is allowed, but beware not to wait too long or you will miss button events
30
   */
31
  if (button_pushed) {
32
    digitalWrite(LED_PIN, HIGH);
33
  } else {
34
    digitalWrite(LED_PIN, LOW);
35
  }
36
}

Und hier der Code für einen einfachen Tipp-Schalter (Toggle):
1
#define BUTTON_BOUNCE_TIME 50
2
#define BUTTON_PIN 2
3
#define LED_PIN 4
4
5
int button_toggle = 0;
6
int button_pushed = 0;
7
long ignore_until = 0;
8
int led_toggle = 0;
9
10
void setup()
11
{
12
  pinMode(BUTTON_PIN, INPUT_PULLUP);
13
  pinMode(LED_PIN, OUTPUT);
14
  digitalWrite(LED_PIN, LOW);
15
}
16
17
void loop()
18
{
19
  if (millis() > ignore_until) {
20
    int button_read = digitalRead(BUTTON_PIN);
21
    if (button_pushed == 0 && button_read == LOW) { /* button was first pushed, or still hold down */
22
      button_pushed = 1;
23
      button_toggle = !button_toggle;
24
    }
25
    else if (button_pushed == 1 && button_read == HIGH) { /* button was released */
26
      button_pushed = 0;
27
    }
28
    ignore_until = millis() + BUTTON_BOUNCE_TIME;
29
  }
30
31
  /* do some other funny stuff...
32
   * delay() is allowed, but beware not to wait too long or you will miss button events
33
   */
34
  if (button_pushed) {
35
    if (button_toggle != led_toggle) {
36
      led_toggle = button_toggle;
37
      digitalWrite(LED_PIN, led_toggle);
38
    }
39
  }
40
}

So, und nun könnt ihr meinen Code in der Luft zereissen und mir klar 
machen das es sooo einfach nun wirklich nicht geht! Bin schon sehr 
gespannt... ;-)

von Peter D. (peda)


Lesenswert?

Olli Z. schrieb:
> Um EMV und sonstige Geister fern zu halten arbeiten die allermeisten mit
> dem internen Pullup und ziehen den Taster gegen GND.

Nö.
Der Pullup ist halt schon da. Warum also extra ein weiteres Bauteil 
einlöten.

von Joachim B. (jar)


Lesenswert?

Peter D. schrieb:
> Nö.
> Der Pullup ist halt schon da. Warum also extra ein weiteres Bauteil
> einlöten.

ich ziehe externe pullup aber immer vor,
1. sind sie immer vorhanden und können nicht je nach Modell abgeschaltet 
werden und
2. kann ich den Strom bestimmen, bzw. wie fest die Ports angebunden sind 
um sich nicht von jedem Leitungshuster in die Irre zu führen lassen.

1mA hat sich bei mir als sicher und gut herausgestellt, 3,3k bei 3,3V 
und 4,7k bei um 5V

LG jar

PS bei einem Portextender I2C mit PCF/PCH 8574 und deine bulletproof 
Entprellroutine habe ich auf externe R verzichtet, aber die Taster und 
der I2C sind sehr kurz angebunden.

: Bearbeitet durch User
von MaWin (Gast)


Lesenswert?

Olli Z. schrieb:
> Dennoch sind viele Lösungen unglaublich kompliziert.

Und was bitte ist dein code ? Noch komplizierter ?

Der setup-Code ist halt wie er ist.
Der funny stuff auch.
Die millis-Funktion ist in Ordnung, eine Art delay ohne zu warten.

Bleibt
1
    int button_read = digitalRead(BUTTON_PIN);
2
    if (button_pushed == 0 && button_read == LOW) { /* button was first pushed, or still hold down */
3
      button_pushed = 1;
4
      button_toggle = !button_toggle;
5
    }
6
    else if (button_pushed == 1 && button_read == HIGH) { /* button was released */
7
      button_pushed = 0;
8
    }
der auch anders geht
1
    button_pushed = button_read;
2
    button_read = digitalRead(BUTTON_PIN);
3
    button_pushed = button_read & ~button_pushed;
und man sollte aus dem button_read und button_pushed bloss ein char 
machen, man nutzt eh nur 1 bit. Der Code kann allerdings auch 8 Taster 
gemeinsam entprellen.

> Sobald man einen Tastendruck erkennt (Pin auf LOW) gilt diese als
> gedrückt. Danach setzt man das lesen weiterer Tastendrücke für 50-100ms
> aus sodass praktische jede Prellzeit vorüber ist.

Richtig, allerdings mit 10ms.

http://www.dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29.1

von msx (Gast)


Lesenswert?

Joachim B. schrieb:
> 1mA hat sich bei mir als sicher und gut herausgestellt, 3,3k bei 3,3V
> und 4,7k bei um 5V

Das sind absolute Mindestwerte. Besser ist es, 10 - 50 mA zu nehmen, 
damit die Kontakte immer gut freigebrannt sind und der Staub verdampfen 
kann!

von Rudolph R. (rudolph)


Lesenswert?

Olli Z. schrieb:
> Sobald man einen Tastendruck erkennt (Pin auf LOW) gilt diese als
> gedrückt. Danach setzt man das lesen weiterer Tastendrücke für 50-100ms
> aus sodass praktische jede Prellzeit vorüber ist.

Interessante Idee, aber so richtig entprellen ist das erstmal nicht.

Ich lese einen Pin zyklisch ein mit meinem Scheduler, also etwa einmal 
pro ms.
Oder auch jedesmal in der loop() beim Arduino.

Jedesmal wenn der Zustand gedrückt entspricht lasse ich einen Zähler 
hoch zählen, ist der Taster nicht betätigt, wird der Zähler runter 
gezählt.

Erreicht der Zähler einen Schwellwert gilt der Taster als betätigt.
Je nach gewünschter Funktion wird das ganze dann mit einer 
Zusatz-Variable verriegelt damit keine weitere Betätigung erkannt wird, 
oder auch nicht.

Das kann man dann auch so gestalten, dass anschliessend der Taster für 
eine gewisse Zeit losgelassen werden muss, bevor eine neue Auslösung 
möglich wird.

Solange das beim Betätigen flattert passiert also nichts, der Zähler 
wird rauf und runter gezählt.
Wird der Schwellwert erreicht war für die eingestellte Zeitdauer der 
Taster betätigt, die bis dahin verstrichene Zeit vielleicht durch das 
Prellen ein wenig länger.

Kein Delay.

: Bearbeitet durch User
von Olli Z. (z80freak)


Lesenswert?

MaWin schrieb:
> button_pushed = button_read;
>     button_read = digitalRead(BUTTON_PIN);
>     button_pushed = button_read & ~button_pushed;

Nach lesen der Lektüre im Link (die ich uneingeschränkt empfehlen kann) 
bin ich wieder etwas schlauer. Leider hab ich Bitwise-Operatoren nicht 
so drauf und daher wollte ich auch meinen Code für Anfänger lesbar 
halten. Das ganze kann man vermutlich á la Perl so schreiben das es 
hocheffizient ist, aber keine Sau mehr kapiert wie das arbeitet ;-)

Das mit dem 'char' anstelle 'int' verstehe ich, aber auch hier ging es 
nicht um Effizienz.

Würdest Du bitte die Funktionsweise der Zeilen mal erklären?

von Stefan F. (Gast)


Lesenswert?

>  Leider hab ich Bitwise-Operatoren nicht so drauf

Das sollte jeder Anfänger aber ganz schnell lernen.

von Olli Z. (z80freak)


Lesenswert?

Ja, wie ich schon in der Einleitung schrieb gibt es hier zahllose und 
immer sehr einfallsreiche Methoden einen Tastendruck ordentlich zu 
erkennen.
Die Zählermethode ist ja eigentlich ein Schmitt-Trigger mit Hysterese 
per Software.
Meine Methode entspricht eher einem RS Flipflop.

Dabei ist das Betätigungsmuster doch immer gleich. Sobald der Taster 
unten ist soll etwas passieren. Solange man gedrückt hält soll es 
entweder so sein als hätte man die Taste wiederholt schnell gedrückt, 
oder es soll nach einer gewissen Haltezeit etwas (anderes) passieren. 
Der Moment des loslassens wird meist nicht ausgewertet.
Dann gäbe es noch den Doppelklick, also zwei Tastendrücke innerhalb 
einer gewissen Zeit, zu nennen. Alles weitere sind Kombinationen und 
Sequenzen mehrerer Tasten.

von Dude (Gast)


Lesenswert?

Mit nem timerinterrupt die Taster Pollen....das reicht völlig aus.

von Olli Z. (z80freak)


Lesenswert?

Dude schrieb:
> Mit nem timerinterrupt die Taster Pollen....das reicht völlig aus.

Gibt doch bitte mal ein Beispiel.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Olli Z. schrieb:
> Gibt doch bitte mal ein Beispiel.

Schon ewig in diesem Forum:
https://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_.28nach_Peter_Dannegger.29

PeDas Routine arbeitet nach dem Prinzip 'Ich schau zweimal und dann ist 
es wahr' :-)

: Bearbeitet durch User
von Karl M. (Gast)


Lesenswert?

Hallo,

Matthias mein mit Ich schau zweimal und dann ist es wahr', dass 4 malig 
das selbe Ergebnis vorliegen muss, als dann wird es als ein Ereignis 
erkannt.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Karl M. schrieb:
> Matthias mein mit Ich schau zweimal und dann ist es wahr', dass 4 malig
> das selbe Ergebnis vorliegen muss, als dann wird es als ein Ereignis
> erkannt.

Nö - nicht 4 mal, sondern eben 2 mal. Das meinte ich schon so. 
Copy&Paste das ASM in Studio 4 und lass den Simulator laufen, wenn du es 
nicht glaubst.

von Paul B. (paul_baumann)


Lesenswert?

Matthias S. schrieb:
> Nö - nicht 4 mal, sondern eben 2 mal.

Nicht "nö"!


> Dude schrieb:
>> Mit nem timerinterrupt die Taster Pollen....das reicht völlig aus.
Olli Z. schrieb:
> Gibt doch bitte mal ein Beispiel.

Damit fordert er den "Dude" auf, ein Beispiel zu geben -nicht 
irgendwelche anderen Leute, zum 200x die Routine von Peter Danegger zu 
zitieren.

SCNR
Paul

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.